From 3cb0177936034913a005da1447cb5cbd3909f4b8 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Fri, 1 May 2026 20:22:08 +0800 Subject: [PATCH 1/2] feat(ci): add cliff-based release summaries --- .github/cliff.toml | 100 +++++++++++++++++++++++++++++++++ .github/workflows/auto-tag.yml | 51 ++++++++++++----- .github/workflows/publish.yml | 36 ++++++++++-- 3 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 .github/cliff.toml diff --git a/.github/cliff.toml b/.github/cliff.toml new file mode 100644 index 00000000..e2c8fb9f --- /dev/null +++ b/.github/cliff.toml @@ -0,0 +1,100 @@ +[remote.github] +owner = "GeWuYou" +repo = "GFramework" + +[changelog] +header = "" + +body = """ +{%- macro remote_url() -%} +https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% macro has_release_highlight(commit) -%} +{%- set highlighted = false -%} +{%- if commit.remote and commit.remote.pr_labels -%} + {%- for label in commit.remote.pr_labels -%} + {%- if label == "release-highlight" or label == "highlight" -%} + {%- set highlighted = true -%} + {%- endif -%} + {%- endfor -%} +{%- endif -%} +{%- if not highlighted and commit.footers -%} + {%- for footer in commit.footers -%} + {%- if footer.token == "Release-Highlight" and footer.value | trim == "true" -%} + {%- set highlighted = true -%} + {%- endif -%} + {%- endfor -%} +{%- endif -%} +{{ highlighted }} +{%- endmacro %} + +{% macro print_commit(commit) -%} +- {{ commit.message | split(pat="\n") | first | trim | upper_first }}{% if commit.remote and commit.remote.username %} by @{{ commit.remote.username }}{% elif commit.author.name %} by {{ commit.author.name }}{% endif %}{% if commit.remote and commit.remote.pr_number %} in [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}){% endif %} +{%- endmacro %} + +{% if version -%} +## {{ version }} ({{ timestamp | date(format="%Y-%m-%d") }}) +{% else -%} +## 未发布 +{% endif %} + +{% set highlights = commits | filter(attribute="breaking", value=true) %} +{% for commit in commits -%} + {% if self::has_release_highlight(commit=commit) == "true" -%} + {% set_global highlights = highlights | concat(with=commit) -%} + {% endif -%} +{% endfor -%} + +{% if highlights | length > 0 -%} +## 重点条目 +{% for commit in highlights -%} +{{ self::print_commit(commit=commit) }} +{% endfor %} + +{% endif -%} + +## What's Changed +{% for commit in commits -%} +{{ self::print_commit(commit=commit) }} +{% endfor %} + +{% for group, commits in commits | group_by(attribute="group") -%} +### {{ group | striptags | trim }} +{% for commit in commits -%} +{{ self::print_commit(commit=commit) }} +{% endfor %} + +{% endfor -%} + +{% if previous and previous.version and version -%} +Full Changelog: [{{ previous.version }}...{{ version }}]({{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }}) +{% endif -%} +""" + +footer = "" + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +protect_breaking_commits = false +sort_commits = "oldest" + +commit_parsers = [ + { message = ".*\\[skip changelog\\].*", skip = true }, + { body = ".*\\[skip changelog\\].*", skip = true }, + { message = "^feat", group = "✨ 新功能" }, + { message = "^fix", group = "🐛 Bug 修复" }, + { message = "^perf", group = "⚡ 优化" }, + { message = "^refactor", group = "⚡ 优化" }, + { message = "^docs", group = "📝 文档/其他" }, + { message = "^test", group = "📝 文档/其他" }, + { message = "^chore", group = "📝 文档/其他" }, + { message = "^build", group = "📝 文档/其他" }, + { message = "^ci", group = "📝 文档/其他" }, + { message = "^style", group = "📝 文档/其他" } +] + +[git.github] +commits = true diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 4e6b6017..b48915ed 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -56,13 +56,27 @@ jobs: echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" + - name: Generate preview release notes + if: ${{ steps.semantic_release.outputs.new_release_published == 'true' }} + id: cliff_preview + uses: orhun/git-cliff-action@v4 + with: + config: .github/cliff.toml + args: >- + -vv --unreleased --strip header + --tag "${{ steps.semantic_release.outputs.new_release_git_tag }}" + env: + OUTPUT: PREVIEW_RELEASE_NOTES.md + GITHUB_REPO: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + - name: Write preview summary env: RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} - RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} + CLIFF_RELEASE_NOTES: ${{ steps.cliff_preview.outputs.content }} run: | { - echo "## Semantic Release Preview" + echo "## Release Preview" echo echo "- Commit: \`${{ github.sha }}\`" echo "- Release needed: \`${{ steps.semantic_release.outputs.new_release_published }}\`" @@ -71,13 +85,11 @@ jobs: echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" echo "- Preview auth: uses \`PAT_TOKEN\` because semantic-release dry-run still performs a remote push permission probe." echo "- Snapshot semantics: this preview is pinned to dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the run starts are not included." - if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${RELEASE_NOTES}" ]; then + if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${CLIFF_RELEASE_NOTES}" ]; then echo - echo "
Preview release notes" + echo "### 候选发布说明" echo - printf '%s\n' "${RELEASE_NOTES}" - echo - echo "
" + printf '%s\n' "${CLIFF_RELEASE_NOTES}" fi echo echo "If the version looks correct, approve the \`release-approval\` environment to continue." @@ -131,13 +143,26 @@ jobs: echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" + - name: Generate published release notes + if: ${{ steps.semantic_release.outputs.new_release_published == 'true' }} + id: cliff_release + uses: orhun/git-cliff-action@v4 + with: + config: .github/cliff.toml + args: >- + -vv --latest --strip header + env: + OUTPUT: PUBLISHED_RELEASE_NOTES.md + GITHUB_REPO: ${{ github.repository }} + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + - name: Write release summary env: RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} - RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} + CLIFF_RELEASE_NOTES: ${{ steps.cliff_release.outputs.content }} run: | { - echo "## Semantic Release Publish" + echo "## Release Publish" echo echo "- Commit: \`${{ github.sha }}\`" echo "- Preview last tag: \`${{ needs.preview.outputs.last_tag }}\`" @@ -148,12 +173,10 @@ jobs: echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`" echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" echo "- Snapshot semantics: this publish run still uses dispatch SHA \`${{ github.sha }}\`; commits added to \`main\` after the preview started are excluded." - if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${RELEASE_NOTES}" ]; then + if [ "${RELEASE_PUBLISHED}" = "true" ] && [ -n "${CLIFF_RELEASE_NOTES}" ]; then echo - echo "
Published release notes" + echo "### 已发布说明" echo - printf '%s\n' "${RELEASE_NOTES}" - echo - echo "
" + printf '%s\n' "${CLIFF_RELEASE_NOTES}" fi } >> "${GITHUB_STEP_SUMMARY}" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e1287f3e..f9844bea 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -242,25 +242,53 @@ jobs: packages: read steps: + - name: Checkout repository (at tag) + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: true + - name: Download package artifacts uses: actions/download-artifact@v8 with: name: packages path: ./packages + - name: Generate release notes + id: cliff_release + uses: orhun/git-cliff-action@v4 + with: + config: .github/cliff.toml + args: >- + -vv --latest --strip header + env: + OUTPUT: RELEASE_NOTES.md + GITHUB_REPO: ${{ github.repository }} + GITHUB_TOKEN: ${{ github.token }} + # 无论某一侧包源发布是否失败,都继续创建 Release。 # 合规工件由独立 workflow 生成,当前发布流不再假设这些文件在同一次运行中可用。 - name: Create GitHub Release and Upload Assets uses: softprops/action-gh-release@v3 with: - generate_release_notes: true name: "Release ${{ github.ref_name }}" - body: | - Release created by CI for tag ${{ github.ref_name }} - Package version: ${{ needs.build-pack.outputs.package_version }} + body: ${{ steps.cliff_release.outputs.content }} draft: false prerelease: false files: | ./packages/*.nupkg env: GITHUB_TOKEN: ${{ github.token }} + + - name: Write publish summary + env: + CLIFF_RELEASE_NOTES: ${{ steps.cliff_release.outputs.content }} + run: | + { + echo "## GitHub Release" + echo + echo "- Tag: \`${{ github.ref_name }}\`" + echo "- Package version: \`${{ needs.build-pack.outputs.package_version }}\`" + echo + printf '%s\n' "${CLIFF_RELEASE_NOTES}" + } >> "${GITHUB_STEP_SUMMARY}" From a870ea28a8eaaef80951c8493dd79bf38a84ceb7 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Fri, 1 May 2026 20:40:01 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=E5=8F=91?= =?UTF-8?q?=E5=B8=83=E8=AF=B4=E6=98=8E=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 git-cliff 模板重复输出提交的问题 - 更新 GitHub Release 使用 release notes 文件作为正文 - 补充 PR review 修复记录与验证结果 --- .github/cliff.toml | 5 --- .github/workflows/publish.yml | 2 +- .../semantic-release-versioning-tracking.md | 31 ++++++++++++++----- .../semantic-release-versioning-trace.md | 25 +++++++++++++++ 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/.github/cliff.toml b/.github/cliff.toml index e2c8fb9f..b8a6ab1f 100644 --- a/.github/cliff.toml +++ b/.github/cliff.toml @@ -54,11 +54,6 @@ https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} {% endif -%} -## What's Changed -{% for commit in commits -%} -{{ self::print_commit(commit=commit) }} -{% endfor %} - {% for group, commits in commits | group_by(attribute="group") -%} ### {{ group | striptags | trim }} {% for commit in commits -%} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f9844bea..ed787717 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -272,7 +272,7 @@ jobs: uses: softprops/action-gh-release@v3 with: name: "Release ${{ github.ref_name }}" - body: ${{ steps.cliff_release.outputs.content }} + body_path: RELEASE_NOTES.md draft: false prerelease: false files: | diff --git a/ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md b/ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md index 1e49a193..de8980d4 100644 --- a/ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md +++ b/ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md @@ -16,9 +16,9 @@ - 恢复点编号:`SEMREL-RP-004` - 当前阶段:`Phase 2` - 当前焦点: - - 将 preview / release 的 PAT 校验收敛为同一个复用入口,避免后续修复在两段脚本间漂移 - - 在 PAT 校验阶段提前识别“仅有 read、没有 push”的 token,真正覆盖 `git push --dry-run` 的权限前提 - - 将 active tracking 中已稳定的历史完成项归档,恢复默认入口的可读性 + - 收敛 PR #312 最新 AI review 的 release notes 输出问题 + - 确保 `.github/cliff.toml` 不再重复输出同一批 commits + - 确保 `publish.yml` 创建 GitHub Release 时通过文件传递多行 release notes ### 已知风险 @@ -29,6 +29,8 @@ - `cycjimmy/semantic-release-action@v6` 需要在 preview / release 两端都安装 `conventional-changelog-conventionalcommits` 以保证 `conventionalcommits` preset 在 GitHub Actions 中可解析 - 当前仓库本地 `dotnet clean/build` 会带出既有 analyzer warnings;本轮仅修正发版配置与文档,不额外处理这些历史 warning +- `git-cliff-action` 的 `OUTPUT` 文件需要在 `softprops/action-gh-release` 执行时保留在当前工作目录,后续如调整 + working-directory 或 artifact 路径,需要同步复查 `body_path` ## 已完成 @@ -39,17 +41,32 @@ `semantic-release` 的 `git push --dry-run` 阶段才失败 - 已为 PAT 校验的 `mktemp` 文件补充 `trap` 清理,避免异常退出时遗留临时文件路径干扰日志 - 已同步更新 active trace 到 `SEMREL-RP-004`,记录本轮 PR review 收敛结果 +- 已用 `$gframework-pr-review` 抓取 PR #312 最新 review payload,确认未失败测试、未发现 MegaLinter 明细,仍有 + CodeRabbit / Greptile 针对 release notes 的未解决线程 +- 已移除 `.github/cliff.toml` 中 `## What's Changed` 下的未分组 commit 循环,仅保留按 Conventional Commit group + 分类后的输出,避免每个 commit 在生成的 changelog 中出现两次 +- 已将 `.github/workflows/publish.yml` 的 GitHub Release 正文从多行 expression 改为 `body_path: RELEASE_NOTES.md`, + 复用 `git-cliff-action` 写出的 release notes 文件 ## 验证 +- `python3 -c 'import tomllib; tomllib.load(open(".github/cliff.toml", "rb")); print("cliff.toml OK")'` + - 结果:通过 + - 备注:确认 `.github/cliff.toml` 仍为合法 TOML +- `python3 -c 'import yaml; yaml.safe_load(open(".github/workflows/publish.yml", encoding="utf-8")); print("publish.yml OK")'` + - 结果:通过 + - 备注:确认 `.github/workflows/publish.yml` 仍可解析为 YAML +- `yq '.jobs."create-release".steps[] | select(.name == "Create GitHub Release and Upload Assets") | .with' .github/workflows/publish.yml` + - 结果:通过 + - 备注:确认 release step 现在使用 `body_path: RELEASE_NOTES.md` - `dotnet build GFramework.sln -c Release` - 结果:通过 - - 备注:Release 构建通过,`639 warning / 0 error`;warning 为仓库既有基线,preview 鉴权修复后与本轮 PAT 校验收敛后复验结果一致 + - 备注:Release 构建通过,`0 warning / 0 error`;本轮只改动 GitHub Actions / git-cliff 配置 - 更早阶段的 dry-run / tag /抽象项目验证已归档到 `ai-plan/public/semantic-release-versioning/archive/todos/semantic-release-versioning-2026-04-26.md` ## 下一步 -1. 手动重跑 `Semantic Release Version and Tag` 的 preview job,确认 read-only PAT 会在校验步骤提前失败、可写 PAT 不再进入 `git push --dry-run ... 403` -2. 推送本轮修复后重新抓取 PR review,确认 CodeRabbit / Greptile 的 open threads 已转为过时或可关闭 -3. 如 CI 仍报告权限边界问题,再决定是否将 PAT 校验升级为更贴近真实链路的远端 git 探测 +1. 提交并推送本轮 PR review 修复 +2. 重新抓取 PR review,确认 CodeRabbit / Greptile 的 release notes open threads 已转为过时或可关闭 +3. 如 CI 仍报告 release notes 发布问题,再优先复查 `git-cliff-action` 输出文件路径与 `action-gh-release` 输入契约 diff --git a/ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md b/ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md index 2dde02af..9c07273c 100644 --- a/ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md +++ b/ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md @@ -1,5 +1,30 @@ # Semantic Release 版本迁移追踪 +## 2026-05-01 + +### 当前恢复点(SEMREL-RP-004) + +- 通过 `$gframework-pr-review` 抓取当前分支 PR #312: + - CodeRabbit 对 `.github/cliff.toml` 提出 1 个未解决线程,指出 release notes 会重复输出 commit + - Greptile 对 `.github/cliff.toml` 提出同一问题的未解决线程 + - Greptile 对 `.github/workflows/publish.yml` 提出 1 个未解决线程,指出多行 release notes expression 作为 + `body` 传入 GitHub Release action 风险较高 + - CTRF 测试报告显示 `2247 passed / 0 failed` + - 未找到 MegaLinter 明细块 +- 本地复核结论: + - `.github/cliff.toml` 先遍历 `commits` 输出平铺列表,再对同一批 `commits` 按 `group` 输出,重复问题成立 + - `.github/workflows/publish.yml` 已让 `git-cliff-action` 写出 `RELEASE_NOTES.md`,因此 `action-gh-release` 可直接使用 + `body_path` +- 已应用修复: + - 删除 `.github/cliff.toml` 中 `## What's Changed` 下的未分组循环,只保留 grouped 输出 + - 将 `.github/workflows/publish.yml` 的 `body` 改为 `body_path: RELEASE_NOTES.md` +- 已完成语法检查: + - `.github/cliff.toml` 通过 Python `tomllib` 解析 + - `.github/workflows/publish.yml` 通过 PyYAML 解析 + - `yq` 确认 release step 使用 `body_path` +- `dotnet build GFramework.sln -c Release` 通过,`0 warning / 0 error`。 +- 下一步是提交并推送本轮 PR review 修复,然后重新抓取 PR review 确认相关线程状态。 + ## 2026-04-26 ### 当前恢复点(SEMREL-RP-004)