feat: Tag管理功能 + 打包脚本优化 + CI/CD发布流水线
- 新增标签管理: 列表/创建/删除/推送标签 (后端API + 前端UI) - 前端新增标签Tab页,支持创建、删除、推送操作 - config.go 改为从可执行文件同目录加载 config.yaml - 各平台构建脚本自动复制 config.yaml 到输出目录 - Windows 打包改为 ZIP 格式 (不依赖 NSIS) - 新增 GitHub Actions Release 工作流 (三平台自动构建+发布)
This commit is contained in:
@@ -701,3 +701,97 @@ func (s *AppService) SwitchBranch(path, branch string) error {
|
||||
_, err := s.gitClient.Checkout(path, strings.TrimSpace(branch))
|
||||
return err
|
||||
}
|
||||
|
||||
// TagInfo 标签信息
|
||||
type TagInfo struct {
|
||||
Name string `json:"name"`
|
||||
Hash string `json:"hash"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// GetTags 获取所有标签
|
||||
func (s *AppService) GetTags(path string) ([]TagInfo, error) {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("项目路径不存在: %s", path)
|
||||
}
|
||||
out, err := s.gitClient.TagList(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取标签列表失败: %w", err)
|
||||
}
|
||||
var tags []TagInfo
|
||||
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(line, "\t", 5)
|
||||
if len(parts) < 3 {
|
||||
continue
|
||||
}
|
||||
var ts int64
|
||||
fmt.Sscanf(parts[2], "%d", &ts)
|
||||
hash := parts[1]
|
||||
// 注释标签的实际提交哈希在 *objectname
|
||||
if len(parts) > 3 && parts[3] != "" {
|
||||
hash = parts[3]
|
||||
}
|
||||
message := ""
|
||||
if len(parts) > 4 {
|
||||
message = parts[4]
|
||||
}
|
||||
tags = append(tags, TagInfo{
|
||||
Name: parts[0],
|
||||
Hash: hash,
|
||||
Timestamp: ts,
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// CreateTag 创建标签
|
||||
func (s *AppService) CreateTag(path, name, message string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return fmt.Errorf("项目路径不存在: %s", path)
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return fmt.Errorf("标签名不能为空")
|
||||
}
|
||||
if strings.TrimSpace(message) == "" {
|
||||
message = name
|
||||
}
|
||||
_, err := s.gitClient.CreateTag(path, name, message)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteTag 删除标签(本地+远程)
|
||||
func (s *AppService) DeleteTag(path, name string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return fmt.Errorf("项目路径不存在: %s", path)
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return fmt.Errorf("标签名不能为空")
|
||||
}
|
||||
// 删除本地标签
|
||||
if _, err := s.gitClient.DeleteTag(path, name); err != nil {
|
||||
return fmt.Errorf("删除本地标签失败: %w", err)
|
||||
}
|
||||
// 尝试删除远程标签(忽略错误,可能未推送过)
|
||||
s.gitClient.DeleteRemoteTag(path, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// PushTag 推送标签到远程
|
||||
func (s *AppService) PushTag(path, name string) error {
|
||||
if _, err := os.Stat(path); os.IsNotExist(err) {
|
||||
return fmt.Errorf("项目路径不存在: %s", path)
|
||||
}
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "" {
|
||||
return fmt.Errorf("标签名不能为空")
|
||||
}
|
||||
_, err := s.gitClient.PushTag(path, name)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -129,6 +129,32 @@ func (g *GitClient) RevertCommit(path, hash string) (string, error) {
|
||||
return g.Run(path, "revert", "--no-edit", hash)
|
||||
}
|
||||
|
||||
// TagList 获取所有标签(按版本号降序,带创建时间和提交哈希)
|
||||
func (g *GitClient) TagList(path string) (string, error) {
|
||||
return g.Run(path, "tag", "-l", "--sort=-version:refname",
|
||||
"--format=%(refname:short)\t%(objectname:short)\t%(creatordate:unix)\t%(*objectname:short)\t%(contents:subject)")
|
||||
}
|
||||
|
||||
// CreateTag 创建注释标签
|
||||
func (g *GitClient) CreateTag(path, name, message string) (string, error) {
|
||||
return g.Run(path, "tag", "-a", name, "-m", message)
|
||||
}
|
||||
|
||||
// DeleteTag 删除本地标签
|
||||
func (g *GitClient) DeleteTag(path, name string) (string, error) {
|
||||
return g.Run(path, "tag", "-d", name)
|
||||
}
|
||||
|
||||
// PushTag 推送标签到远程
|
||||
func (g *GitClient) PushTag(path, name string) (string, error) {
|
||||
return g.Run(path, "push", "origin", name)
|
||||
}
|
||||
|
||||
// DeleteRemoteTag 删除远程标签
|
||||
func (g *GitClient) DeleteRemoteTag(path, name string) (string, error) {
|
||||
return g.Run(path, "push", "origin", "--delete", name)
|
||||
}
|
||||
|
||||
// BranchList 获取所有本地分支
|
||||
func (g *GitClient) BranchList(path string) (string, error) {
|
||||
return g.Run(path, "branch", "--format=%(refname:short)\t%(HEAD)")
|
||||
|
||||
Reference in New Issue
Block a user