Bladeren bron

添加工具和生成器以支持构建和发布流程

新增了多个工具包和生成器函数,用于执行系统命令、清理字符串、处理文件系统操作、获取Git信息以及生成版本和随机数据。这些工具和生成器将帮助自动化项目的构建和发布过程。同时调整了一些现有文件中的代码位置和逻辑,以更好地配合新功能。
SongZihuan 5 dagen geleden
bovenliggende
commit
16cf3ea20b

+ 4 - 1
.gitignore

@@ -25,4 +25,7 @@ build_date.txt
 commit_data.txt
 tag_data.txt
 tag_commit_data.txt
-random_data.txt
+
+random_data.txt
+
+release_info.md

+ 3 - 3
resource.go

@@ -24,6 +24,9 @@ var Report string
 //go:embed NAME
 var Name string
 
+//go:embed ENV_PREFIX
+var EnvPrefix string
+
 //go:embed build_date.txt
 var buildDateTxt string
 
@@ -41,9 +44,6 @@ var GitTagCommitHash string
 //go:embed random_data.txt
 var randomData string
 
-//go:embed ENV_PREFIX
-var EnvPrefix string
-
 func init() {
 	initCleanFile() // 最先执行
 

+ 1 - 1
src/utils/filesystemutils/dir.go

@@ -21,5 +21,5 @@ func IsExistsAndDir(path string) (exists, isDir bool) {
 		return false, false
 	}
 
-	return s.IsDir(), true
+	return true, s.IsDir()
 }

+ 2 - 2
src/utils/filesystemutils/file.go

@@ -15,11 +15,11 @@ func IsFile(path string) bool {
 	return !s.IsDir()
 }
 
-func IsExistsAndFile(path string) (exists, isDir bool) {
+func IsExistsAndFile(path string) (exists, isFile bool) {
 	s, err := os.Stat(path)
 	if err != nil {
 		return false, false
 	}
 
-	return !s.IsDir(), true
+	return true, !s.IsDir()
 }

+ 82 - 0
tool/generate/basefile/basefile.go

@@ -0,0 +1,82 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package basefile
+
+import "github.com/SongZihuan/BackendServerTemplate/tool/utils/fileutils"
+
+const (
+	FileVersion   = "./VERSION"
+	FileLicense   = "./LICENSE"
+	FileReport    = "./REPORT"
+	FileName      = "./NAME"
+	FileEnvPrefix = "./ENV_PREFIX"
+
+	FileBuildDateTxt  = "./build_date.txt"
+	FileCommitDateTxt = "./commit_data.txt"
+	FileTagDataTxt    = "./tag_data.txt"
+	FileTagCommitData = "./tag_commit_data.txt"
+	FileRandomData    = "./random_data.txt"
+
+	FileReleaseInfoMD = "./release_info.md"
+)
+
+func CreateBaseFile() (err error) {
+	err = fileutils.Touch(FileVersion)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileLicense)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileReport)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileName)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileEnvPrefix)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileBuildDateTxt)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileCommitDateTxt)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileTagDataTxt)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileTagCommitData)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileRandomData)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Touch(FileReleaseInfoMD)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 18 - 0
tool/generate/builddate/build_date_data.go

@@ -0,0 +1,18 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package builddate
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/basefile"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/fileutils"
+	"time"
+)
+
+var buildTime = time.Now()
+
+func WriteBuildDateData() error {
+	return fileutils.Write(basefile.FileBuildDateTxt, fmt.Sprint(buildTime.Unix()))
+}

+ 66 - 0
tool/generate/changelog/changelog_data.go

@@ -0,0 +1,66 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package changelog
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/cleanstringutils"
+	"os"
+	"strings"
+	"sync"
+)
+
+const FileChangelog = "./CHANGELOG.md"
+
+var lastChangeLog = ""
+var once sync.Once
+
+func GetLastChangLog() string {
+	once.Do(func() {
+		lastChangeLog = getLastChangLog()
+	})
+	return lastChangeLog
+}
+
+func getLastChangLog() string {
+	dat, err := os.ReadFile(FileChangelog)
+	if err != nil {
+		panic(err)
+	}
+
+	logSrc := strings.Split(cleanstringutils.GetString(string(dat)), "\n")
+
+	res := new(strings.Builder)
+	index := 0
+
+	// 定位最新版本
+FindVersionCycle:
+	for {
+		if index >= len(logSrc) {
+			panic("Error CHANGELOG.md: can not find the log")
+		}
+
+		s := logSrc[index]
+		if strings.HasPrefix(s, "## [") && !strings.HasPrefix(s, "## [未") {
+			res.WriteString(s + "\n")
+			break FindVersionCycle
+		}
+	}
+
+GetVersionLogCycle:
+	for {
+		if index >= len(logSrc) {
+			break GetVersionLogCycle
+		}
+
+		s := logSrc[index]
+		if strings.HasPrefix(s, "## [") {
+			break GetVersionLogCycle
+		}
+
+		res.WriteString(s + "\n")
+	}
+
+	return res.String()
+}

+ 136 - 0
tool/generate/git/git_data.go

@@ -0,0 +1,136 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/basefile"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/fileutils"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/gitutils"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/modutils"
+	"strings"
+	"sync"
+)
+
+var once sync.Once
+var lastCommit string = ""
+var lastTag string = ""
+var lastTagCommit string = ""
+var secondToLastTag string = ""
+var secondToLastTagCommit string = ""
+var firstCommit string = ""
+
+func InitGitData() (err error) {
+	once.Do(func() {
+		err = initGitData()
+	})
+	return err
+}
+
+func initGitData() (err error) {
+	if !gitutils.HasGit() {
+		return nil
+	}
+
+	defer func() {
+		if err != nil {
+			lastCommit = ""
+			lastTag = ""
+			lastTagCommit = ""
+			secondToLastTag = ""
+			secondToLastTagCommit = ""
+			firstCommit = ""
+		}
+	}()
+
+	lastCommit, err = gitutils.GetLastCommit()
+	if err != nil {
+		return err
+	}
+
+	firstCommit, err = gitutils.GetFirstCommit()
+	if err != nil {
+		return err
+	}
+
+	tagList, err := gitutils.GetTagListWithFilter(func(s string) bool {
+		return strings.HasPrefix(s, "v")
+	})
+	if err == nil {
+		return err
+	}
+
+	if len(tagList) > 0 {
+		lastTag = tagList[0]
+		lastTagCommit, err = gitutils.GetTagCommit(lastTag)
+		if err != nil {
+			return err
+		}
+	}
+
+	if len(tagList) > 1 {
+		secondToLastTag = tagList[1]
+		secondToLastTagCommit, err = gitutils.GetTagCommit(secondToLastTag)
+		if err != nil {
+			return err
+		}
+	} else {
+		secondToLastTag = ""
+		secondToLastTagCommit = ""
+	}
+
+	return nil
+}
+
+func Version() string {
+	_ = InitGitData()
+	return lastTag
+}
+
+func WriteGitData() (err error) {
+	_ = InitGitData()
+
+	err = fileutils.Write(basefile.FileCommitDateTxt, lastCommit)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Write(basefile.FileTagDataTxt, lastTag)
+	if err != nil {
+		return err
+	}
+
+	err = fileutils.Write(basefile.FileTagCommitData, lastTagCommit)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func GetGitHubCompareMD() string {
+	_ = InitGitData()
+
+	compare, url := GetGitHubCompareURL()
+	if compare == "" || url == "" {
+		return ""
+	}
+
+	return fmt.Sprintf("**Git Changelog:[%s](%s)", compare, url)
+}
+
+func GetGitHubCompareURL() (string, string) {
+	_ = InitGitData()
+
+	if !modutils.IsGitHub || lastTag == "" || firstCommit == lastTagCommit {
+		return "", ""
+	}
+
+	if secondToLastTag != "" {
+		compare := fmt.Sprintf("%s...%s", secondToLastTag, lastTag)
+		return compare, fmt.Sprintf("https://%s/compare/%s", modutils.ModPath, compare)
+	}
+
+	return lastTag, fmt.Sprintf("https://%s/compare/%s...%s", modutils.ModPath, firstCommit, lastTag)
+}

+ 54 - 0
tool/generate/main.go

@@ -0,0 +1,54 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/basefile"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/builddate"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/git"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/random"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/releaseinfo"
+	"os"
+)
+
+func main() {
+	os.Exit(command())
+}
+
+func command() int {
+	var err error
+
+	err = basefile.CreateBaseFile()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	err = git.InitGitData()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	err = builddate.WriteBuildDateData()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	err = git.WriteGitData()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	err = random.WriteRandomData()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	err = releaseinfo.WriteReleaseData()
+	if err != nil {
+		return ReturnError(err)
+	}
+
+	return ReturnSuccess()
+}

+ 15 - 0
tool/generate/random/random_data.go

@@ -0,0 +1,15 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package random
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/basefile"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/fileutils"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/randomutils"
+)
+
+func WriteRandomData() error {
+	return fileutils.Write(basefile.FileRandomData, randomutils.GenerateRandomString(40))
+}

+ 48 - 0
tool/generate/releaseinfo/release_data.go

@@ -0,0 +1,48 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package releaseinfo
+
+import (
+	_ "embed"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/basefile"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/changelog"
+	"github.com/SongZihuan/BackendServerTemplate/tool/generate/git"
+	"os"
+	"text/template"
+)
+
+//go:embed release_data.md.tmpl
+var releaseTemplateString string
+var releaseTemplate *template.Template = template.New("Release")
+
+type templateData struct {
+	Version       string
+	GithubCompare string
+	ChangeLog     string
+}
+
+func init() {
+	var err error
+	_, err = releaseTemplate.Parse(releaseTemplateString)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func WriteReleaseData() error {
+	file, err := os.OpenFile(basefile.FileReleaseInfoMD, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = file.Close()
+	}()
+
+	return releaseTemplate.Execute(file, templateData{
+		Version:       git.Version(),
+		ChangeLog:     changelog.GetLastChangLog(),
+		GithubCompare: git.GetGitHubCompareMD(),
+	})
+}

+ 6 - 0
tool/generate/releaseinfo/release_data.md.tmpl

@@ -0,0 +1,6 @@
+新版本 {{ .Version }} 成功发布啦!🚀🚀🚀
+快来体验吧!
+
+{{ .GithubCompare }}
+
+{{ .ChangeLog }}

+ 20 - 0
tool/generate/return.go

@@ -0,0 +1,20 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "log"
+
+const exitCodeSuccess = 0
+const exitCodeFailed = 1
+
+func ReturnError(err error) int {
+	log.Printf("Error: %s\n", err.Error())
+	return exitCodeFailed
+}
+
+func ReturnSuccess() int {
+	log.Println("Success!")
+	return exitCodeSuccess
+}

+ 14 - 0
tool/utils/cleanstringutils/moreline.go

@@ -0,0 +1,14 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package cleanstringutils
+
+import "strings"
+
+func GetString(data string) (res string) {
+	res = strings.Replace(data, "\r", "", -1)
+	res = strings.Split(res, "\n")[0]
+	res = strings.TrimSpace(res)
+	return res
+}

+ 15 - 0
tool/utils/cleanstringutils/oneline.go

@@ -0,0 +1,15 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package cleanstringutils
+
+import "strings"
+
+func GetStringOneLine(data string) (res string) {
+	res = strings.Replace(data, "\t", "    ", -1)
+	res = strings.Replace(data, "\r", "", -1)
+	res = strings.Split(res, "\n")[0]
+	res = strings.TrimSpace(res)
+	return res
+}

+ 36 - 0
tool/utils/executils/exec.go

@@ -0,0 +1,36 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package executils
+
+import (
+	"bytes"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/cleanstringutils"
+	"os"
+	"os/exec"
+)
+
+func Run(name string, args ...string) (string, error) {
+	cmd := exec.Command(name, args...)
+
+	var out bytes.Buffer
+	cmd.Stdin = os.Stdin
+	cmd.Stdout = &out
+	cmd.Stderr = os.Stderr
+
+	if err := cmd.Run(); err != nil {
+		return "", err
+	}
+
+	return out.String(), nil
+}
+
+func RunOnline(name string, args ...string) (string, error) {
+	res, err := Run(name, args...)
+	if err != nil {
+		return "", err
+	}
+
+	return cleanstringutils.GetStringOneLine(res), nil
+}

+ 16 - 0
tool/utils/filesystemutils/exists.go

@@ -0,0 +1,16 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package filesystemutils
+
+import "os"
+
+func IsDir(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+		return false
+	}
+
+	return s.IsDir()
+}

+ 19 - 0
tool/utils/fileutils/touch.go

@@ -0,0 +1,19 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package fileutils
+
+import (
+	"os"
+)
+
+func Touch(filePath string) error {
+	// 尝试打开文件
+	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, 0644)
+	if err != nil {
+		return err
+	}
+	_ = file.Close()
+	return nil
+}

+ 20 - 0
tool/utils/fileutils/write.go

@@ -0,0 +1,20 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package fileutils
+
+import "os"
+
+func Write(filePath string, dat string) error {
+	// 尝试打开文件
+	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		_ = file.Close()
+	}()
+	_, err = file.Write([]byte(dat))
+	return err
+}

+ 60 - 0
tool/utils/gitutils/git.go

@@ -0,0 +1,60 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package gitutils
+
+import (
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/cleanstringutils"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/executils"
+	"github.com/SongZihuan/BackendServerTemplate/tool/utils/filesystemutils"
+	"strings"
+	"sync"
+)
+
+var hasGitOnce sync.Once
+var hasGit = false
+
+func HasGit() bool {
+	hasGitOnce.Do(func() {
+		hasGit = filesystemutils.IsDir("./.git")
+	})
+	return hasGit
+}
+
+func GetLastCommit() (string, error) {
+	return executils.RunOnline("git", "rev-parse", "HEAD")
+}
+
+func GetTagListWithFilter(filter func(string) bool) ([]string, error) {
+	ret, err := executils.RunOnline("git", "for-each-ref", "--sort=-creatordate", "--format", "%(refname:short)", "refs/tags/")
+	if err != nil {
+		return nil, err
+	}
+
+	ret = cleanstringutils.GetString(ret)
+	tagListSrc := strings.Split(ret, "\n")
+	tagList := make([]string, 0, len(tagListSrc))
+
+	for _, tag := range tagList {
+		tag = strings.TrimSpace(tag)
+		if tag == "" || (filter != nil && !filter(tag)) {
+			continue
+		}
+		tagList = append(tagList, tag)
+	}
+
+	return tagList, nil
+}
+
+func GetTagList() ([]string, error) {
+	return GetTagListWithFilter(nil)
+}
+
+func GetTagCommit(tag string) (string, error) {
+	return executils.RunOnline("git", "rev-list", "-n", "1", tag)
+}
+
+func GetFirstCommit() (string, error) {
+	return executils.RunOnline("git", "rev-list", "--max-parents=0", "HEAD")
+}

+ 23 - 0
tool/utils/modutils/mod.go

@@ -0,0 +1,23 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package modutils
+
+import (
+	"runtime/debug"
+	"strings"
+)
+
+var ModPath = ""
+var IsGitHub bool = false
+
+func init() {
+	info, ok := debug.ReadBuildInfo()
+	if !ok {
+		panic("read build info failed")
+	}
+
+	ModPath = info.Main.Path
+	IsGitHub = strings.HasPrefix(ModPath, "github.com/")
+}

+ 22 - 0
tool/utils/randomutils/random.go

@@ -0,0 +1,22 @@
+// Copyright 2025 BackendServerTemplate Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package randomutils
+
+import (
+	"math/rand"
+	"time"
+)
+
+// GenerateRandomString generates a random string of the specified length
+// containing lowercase letters ('a'-'z') and digits ('0'-'9').
+func GenerateRandomString(length int) string {
+	const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
+	seededRand := rand.New(rand.NewSource(time.Now().UnixNano()))
+	result := make([]byte, length)
+	for i := range result {
+		result[i] = charset[seededRand.Intn(len(charset))]
+	}
+	return string(result)
+}