Some checks failed
Deploy BillAI / Deploy to Production (push) Has been cancelled
- Update changelog.go to use binary directory as base path - Uses os.Executable() instead of relative path '..' - Automatically adapts to local and Docker environments - Modify server Dockerfile build context - Change context from ./server to project root (.) - Update COPY commands for new context - Add CHANGELOG.md to container image - Update AGENTS.md guidelines - Explicitly specify relative path format for file references This ensures the changelog API works correctly in both local development and Docker deployment environments.
129 lines
3.1 KiB
Go
129 lines
3.1 KiB
Go
package service
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// ChangelogEntry 变更日志条目
|
|
type ChangelogEntry struct {
|
|
Version string `json:"version"`
|
|
Date string `json:"date"`
|
|
Changes map[string][]string `json:"changes"`
|
|
}
|
|
|
|
// ParseChangelog 解析 CHANGELOG.md 文件
|
|
func ParseChangelog() ([]ChangelogEntry, error) {
|
|
// 获取项目根目录
|
|
rootDir := os.Getenv("PROJECT_ROOT")
|
|
if rootDir == "" {
|
|
// 使用二进制文件所在目录作为基准
|
|
execPath, err := os.Executable()
|
|
if err == nil {
|
|
rootDir = filepath.Dir(execPath)
|
|
}
|
|
}
|
|
|
|
changelogPath := filepath.Join(rootDir, "CHANGELOG.md")
|
|
|
|
file, err := os.Open(changelogPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("打开 CHANGELOG.md 失败: %w", err)
|
|
}
|
|
defer file.Close()
|
|
|
|
var entries []ChangelogEntry
|
|
var currentEntry *ChangelogEntry
|
|
var currentCategory string
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
|
|
// 匹配版本号行 ## [1.4.0] - 2026-03-23
|
|
if strings.HasPrefix(line, "## [") && strings.Contains(line, "]") {
|
|
// 保存前一个 entry
|
|
if currentEntry != nil {
|
|
entries = append(entries, *currentEntry)
|
|
}
|
|
|
|
// 解析版本号和日期
|
|
version, date := parseVersionLine(line)
|
|
if version != "" && version != "Unreleased" {
|
|
currentEntry = &ChangelogEntry{
|
|
Version: version,
|
|
Date: date,
|
|
Changes: make(map[string][]string),
|
|
}
|
|
currentCategory = ""
|
|
} else {
|
|
currentEntry = nil
|
|
}
|
|
continue
|
|
}
|
|
|
|
// 跳过 Unreleased 和其他非版本行
|
|
if currentEntry == nil {
|
|
continue
|
|
}
|
|
|
|
// 匹配分类行 ### 新增、### 优化等
|
|
if strings.HasPrefix(line, "### ") {
|
|
currentCategory = strings.TrimPrefix(line, "### ")
|
|
if currentEntry.Changes[currentCategory] == nil {
|
|
currentEntry.Changes[currentCategory] = []string{}
|
|
}
|
|
continue
|
|
}
|
|
|
|
// 匹配项目行 - 项目描述
|
|
if strings.HasPrefix(line, "- ") && currentCategory != "" {
|
|
item := strings.TrimPrefix(line, "- ")
|
|
// 移除加粗标记和链接等
|
|
item = cleanItem(item)
|
|
currentEntry.Changes[currentCategory] = append(currentEntry.Changes[currentCategory], item)
|
|
}
|
|
}
|
|
|
|
// 保存最后一个 entry
|
|
if currentEntry != nil {
|
|
entries = append(entries, *currentEntry)
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
return nil, fmt.Errorf("扫描文件失败: %w", err)
|
|
}
|
|
|
|
return entries, nil
|
|
}
|
|
|
|
// parseVersionLine 解析版本行 ## [1.4.0] - 2026-03-23
|
|
func parseVersionLine(line string) (version, date string) {
|
|
// 提取版本号
|
|
startIdx := strings.Index(line, "[")
|
|
endIdx := strings.Index(line, "]")
|
|
if startIdx >= 0 && endIdx > startIdx {
|
|
version = line[startIdx+1 : endIdx]
|
|
}
|
|
|
|
// 提取日期
|
|
dateStartIdx := strings.LastIndex(line, "- ") + 2
|
|
if dateStartIdx > 1 {
|
|
date = strings.TrimSpace(line[dateStartIdx:])
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// cleanItem 清理项目描述(移除加粗标记等)
|
|
func cleanItem(item string) string {
|
|
// 移除加粗标记 **text**
|
|
item = strings.ReplaceAll(item, "**", "")
|
|
// 移除代码标记 `text`
|
|
item = strings.ReplaceAll(item, "`", "")
|
|
return strings.TrimSpace(item)
|
|
}
|