From 93416042df8fa1b1ff431c60e4445c815f1c7f7a Mon Sep 17 00:00:00 2001 From: ShellMonster Date: Wed, 22 Apr 2026 11:49:48 +0800 Subject: [PATCH 1/2] feat: improve release workflow with test gate, changelog script and smart summary - Extract inline changelog shell into scripts/generate-changelog.sh - Add test job (go vet, go build, npm ci, npm run build) before release - Filter noise commits (chore/ci/release/bump/test/ignore) from notes - Add Full Changelog diff link at release notes bottom - Add path-based smart summary (detects changed modules automatically) --- .github/workflows/release.yml | 94 +++++++++------------- scripts/generate-changelog.sh | 142 ++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 56 deletions(-) create mode 100755 scripts/generate-changelog.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bda039b..2991478 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,44 @@ permissions: contents: write jobs: + test: + name: 发布前检查 + runs-on: ubuntu-latest + steps: + - name: 拉取源代码 + uses: actions/checkout@v4 + + - name: 安装 Go 环境 + uses: actions/setup-go@v5 + with: + go-version: '1.21' + cache-dependency-path: './backend/go.sum' + + - name: 后端代码检查 + run: go vet ./... + working-directory: ./backend + + - name: 后端编译检查 + run: go build ./... + working-directory: ./backend + + - name: 安装 Node.js 环境 + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + cache-dependency-path: './desktop/package-lock.json' + + - name: 安装前端依赖 + run: npm ci + working-directory: ./desktop + + - name: 前端构建检查 + run: npm run build + working-directory: ./desktop + build-and-release: + needs: test name: 构建并发布 - ${{ matrix.name }} strategy: fail-fast: false @@ -88,59 +125,7 @@ jobs: - name: 生成 Release Notes id: release_notes shell: bash - run: | - # 获取上一个 tag,如果不存在则获取第一个 commit - PREV_TAG=$(git describe --tags --abbrev=0 ${{ github.ref_name }}^ 2>/dev/null || git rev-list --max-parents=0 HEAD) - echo "Previous tag: $PREV_TAG" - - # 提取 commit 记录(不包括 merge commit) - # --no-merges: 只显示实际代码变更的 commit,过滤掉 PR merge 产生的合并提交 - LOGS=$(git log $PREV_TAG..${{ github.ref_name }} --pretty=format:"%s" --no-merges) - - # 初始化分类内容 - FEATS="" - FIXES="" - DOCS="" - OTHERS="" - - # 按前缀归类 - while IFS= read -r line; do - [ -z "$line" ] && continue - - # 清理可能导致 Markdown 渲染问题的特殊字符(如反斜杠、反引号等) - # 这里简单处理,保证基础展示正常 - clean_line=$(echo "$line" | sed 's/\\/\\\\/g; s/`/\\`/g') - - if [[ $line =~ ^feat ]]; then - FEATS="$FEATS\n- ${clean_line#feat: }" - elif [[ $line =~ ^fix ]]; then - FIXES="$FIXES\n- ${clean_line#fix: }" - elif [[ $line =~ ^docs ]]; then - DOCS="$DOCS\n- ${clean_line#docs: }" - elif [[ $line =~ ^refactor ]] || [[ $line =~ ^perf ]]; then - FIXES="$FIXES\n- ${clean_line}" - elif [[ $line =~ ^bump ]]; then - OTHERS="$OTHERS\n- ${clean_line}" - else - OTHERS="$OTHERS\n- ${clean_line}" - fi - done <<< "$LOGS" - - # 组合最终正文 - BODY="" - [ -n "$FEATS" ] && BODY="$BODY\n### 🚀 新功能 (Features)$FEATS\n" - [ -n "$FIXES" ] && BODY="$BODY\n### 🔧 修复与优化 (Bug Fixes & Refactor)$FIXES\n" - [ -n "$DOCS" ] && BODY="$BODY\n### 📝 文档更新 (Documentation)$DOCS\n" - [ -n "$OTHERS" ] && BODY="$BODY\n### 📦 其他改动 (Others)$OTHERS\n" - - [ -z "$BODY" ] && BODY="本次发布包含内部优化与稳定性提升。" - - # 处理换行符并存入环境变量(使用多行模式) - { - echo "body<> "$GITHUB_OUTPUT" + run: bash scripts/generate-changelog.sh "${{ github.ref_name }}" "https://github.com/${{ github.repository }}" >> "$GITHUB_OUTPUT" - name: 准备 Go 边车程序 shell: bash @@ -218,7 +203,6 @@ jobs: releaseBody: | ## 🎨 大香蕉 AI - 创作从未如此简单 - ### 🚀 本次更新 ${{ steps.release_notes.outputs.body }} ### 📦 包含组件 @@ -245,7 +229,6 @@ jobs: releaseBody: | ## 🎨 大香蕉 AI - 创作从未如此简单 - ### 🚀 本次更新 ${{ steps.release_notes.outputs.body }} ### 📦 包含组件 @@ -341,7 +324,6 @@ jobs: releaseBody: | ## 🎨 大香蕉 AI - 创作从未如此简单 - ### 🚀 本次更新 ${{ steps.release_notes.outputs.body }} ### 📦 包含组件 diff --git a/scripts/generate-changelog.sh b/scripts/generate-changelog.sh new file mode 100755 index 0000000..02f21ee --- /dev/null +++ b/scripts/generate-changelog.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# 用法: ./scripts/generate-changelog.sh [repo_url] +# 输出 GITHUB_OUTPUT 多行格式: body</dev/null || git rev-list --max-parents=0 HEAD) +echo "Previous tag: $PREV_TAG" >&2 + +CHANGED_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || true) + +SUMMARY="" +touches() { + echo "$CHANGED_FILES" | grep -qE "$1" 2>/dev/null +} + +if touches "backend/internal/provider/"; then + SUMMARY="${SUMMARY} +- AI 提供商接口适配与模型支持更新" +fi +if touches "backend/internal/worker/"; then + SUMMARY="${SUMMARY} +- 任务并发处理引擎优化" +fi +if touches "backend/internal/templates/"; then + SUMMARY="${SUMMARY} +- 模板市场资源更新" +fi +if touches "backend/internal/promptopt/"; then + SUMMARY="${SUMMARY} +- 提示词智能优化引擎改进" +fi +if touches "backend/internal/(storage|config|folder)/"; then + SUMMARY="${SUMMARY} +- 本地存储与配置管理优化" +fi +if touches "backend/internal/api/"; then + SUMMARY="${SUMMARY} +- 后端 API 接口调整" +fi +if touches "desktop/src/components/"; then + SUMMARY="${SUMMARY} +- 前端界面交互与组件体验升级" +fi +if touches "desktop/src/store/"; then + SUMMARY="${SUMMARY} +- 前端状态管理逻辑更新" +fi +if touches "desktop/src/services/"; then + SUMMARY="${SUMMARY} +- 前端 API 服务层调整" +fi +if touches "desktop/src-tauri/"; then + SUMMARY="${SUMMARY} +- 桌面端 Tauri 容器与权限配置更新" +fi +if touches "desktop/src/i18n/"; then + SUMMARY="${SUMMARY} +- 多语言国际化翻译更新" +fi +if touches "docker|Dockerfile|docker-compose|\.env\.example"; then + SUMMARY="${SUMMARY} +- Docker 部署方案优化" +fi +if touches "\.github/"; then + SUMMARY="${SUMMARY} +- CI/CD 构建与发布流程改进" +fi + +NOISE_REGEX='^(chore|ci|release|bump|test|ignore)(\(|:|!|:)' +LOGS=$(git log "$PREV_TAG".."$CURRENT_TAG" --pretty=format:"%s" --no-merges 2>/dev/null || true) + +FEATS="" +FIXES="" +DOCS="" +OTHERS="" + +while IFS= read -r line; do + [ -z "$line" ] && continue + [[ $line =~ $NOISE_REGEX ]] && continue + + clean_line=$(echo "$line" | sed 's/\\/\\\\/g; s/`/\\`/g') + + if [[ $line =~ ^feat ]]; then + FEATS="$FEATS +- ${clean_line#feat: }" + elif [[ $line =~ ^fix ]]; then + FIXES="$FIXES +- ${clean_line#fix: }" + elif [[ $line =~ ^docs ]]; then + DOCS="$DOCS +- ${clean_line#docs: }" + elif [[ $line =~ ^refactor ]] || [[ $line =~ ^perf ]]; then + FIXES="$FIXES +- ${clean_line}" + else + OTHERS="$OTHERS +- ${clean_line}" + fi +done <<< "$LOGS" + +BODY="" +if [ -n "$SUMMARY" ]; then + BODY="${BODY} +### 📋 更新概要 + +本次发布主要涉及以下模块:${SUMMARY} +" +fi + +[ -n "$FEATS" ] && BODY="${BODY} + +### 🚀 新功能 (Features)${FEATS} +" +[ -n "$FIXES" ] && BODY="${BODY} + +### 🔧 修复与优化 (Bug Fixes & Refactor)${FIXES} +" +[ -n "$DOCS" ] && BODY="${BODY} + +### 📝 文档更新 (Documentation)${DOCS} +" +[ -n "$OTHERS" ] && BODY="${BODY} + +### 📦 其他改动 (Others)${OTHERS} +" + +[ -z "$BODY" ] && BODY="本次发布包含内部优化与稳定性提升。" + +if [ -n "$REPO_URL" ] && [ "$PREV_TAG" != "$CURRENT_TAG" ]; then + BODY="${BODY} + +**Full Changelog**: ${REPO_URL}/compare/${PREV_TAG}...${CURRENT_TAG}" +fi + +{ + echo "body< Date: Wed, 22 Apr 2026 13:47:08 +0800 Subject: [PATCH 2/2] fix: address code review feedback on changelog script - Replace echo with printf for robustness with special characters - Fix scoped conventional commit prefix stripping (feat(scope):) - Use POSIX [[:space:]] instead of \s for BSD sed compatibility - Replace echo -e with printf for safe output --- scripts/generate-changelog.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/generate-changelog.sh b/scripts/generate-changelog.sh index 02f21ee..aa92e5d 100755 --- a/scripts/generate-changelog.sh +++ b/scripts/generate-changelog.sh @@ -12,8 +12,9 @@ echo "Previous tag: $PREV_TAG" >&2 CHANGED_FILES=$(git diff --name-only "$PREV_TAG".."$CURRENT_TAG" 2>/dev/null || true) SUMMARY="" +# 检测变更文件是否匹配指定路径模式 touches() { - echo "$CHANGED_FILES" | grep -qE "$1" 2>/dev/null + printf "%s\n" "$CHANGED_FILES" | grep -qE "$1" } if touches "backend/internal/provider/"; then @@ -81,17 +82,20 @@ while IFS= read -r line; do [ -z "$line" ] && continue [[ $line =~ $NOISE_REGEX ]] && continue - clean_line=$(echo "$line" | sed 's/\\/\\\\/g; s/`/\\`/g') + clean_line=$(printf "%s" "$line" | sed 's/`/\\`/g') + + # 剥离 conventional commit 前缀: feat: / feat(scope): / feat(scope)!: / fix!: 等 + stripped_line=$(printf "%s" "$clean_line" | sed -E 's/^[a-z]+(\([^)]*\))?!?:[[:space:]]*//') if [[ $line =~ ^feat ]]; then FEATS="$FEATS -- ${clean_line#feat: }" +- ${stripped_line}" elif [[ $line =~ ^fix ]]; then FIXES="$FIXES -- ${clean_line#fix: }" +- ${stripped_line}" elif [[ $line =~ ^docs ]]; then DOCS="$DOCS -- ${clean_line#docs: }" +- ${stripped_line}" elif [[ $line =~ ^refactor ]] || [[ $line =~ ^perf ]]; then FIXES="$FIXES - ${clean_line}" @@ -136,7 +140,7 @@ if [ -n "$REPO_URL" ] && [ "$PREV_TAG" != "$CURRENT_TAG" ]; then fi { - echo "body<