From b194238385a3e64f121069480010d21910391f21 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 26 Apr 2026 09:42:18 +0800 Subject: [PATCH 1/5] =?UTF-8?q?build(release):=20=E8=BF=81=E7=A7=BB?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E5=8C=96=E7=89=88=E6=9C=AC=E6=89=93=E6=A0=87?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 semantic-release 配置并固定 release rules 与 v 前缀 tag 格式 - 重构 auto-tag workflow 为 main 上的真实打标与 workflow_dispatch dry-run 双入口 - 保留现有 publish workflow 并补充 ai-plan 跟踪与验证记录 --- .github/workflows/auto-tag.yml | 100 +++++++++++------- .releaserc.json | 72 +++++++++++++ ai-plan/public/README.md | 7 ++ .../semantic-release-versioning-tracking.md | 67 ++++++++++++ .../semantic-release-versioning-trace.md | 39 +++++++ 5 files changed, 247 insertions(+), 38 deletions(-) create mode 100644 .releaserc.json create mode 100644 ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md create mode 100644 ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 9588c304..f005608e 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -1,4 +1,4 @@ -name: Auto Increment Version and Tag +name: Semantic Release Version and Tag on: workflow_run: @@ -9,28 +9,17 @@ on: - main workflow_dispatch: concurrency: - group: auto-tag-main + group: semantic-release-main cancel-in-progress: false jobs: - auto-tag: + dry-run: if: > - ( - github.event_name == 'workflow_run' && - github.event.workflow_run.conclusion == 'success' && - contains(github.event.workflow_run.head_commit.message, '[release ci]') - ) - || - ( - github.event_name == 'workflow_dispatch' && - github.ref == 'refs/heads/main' - ) - + github.event_name == 'workflow_dispatch' && + github.ref == 'refs/heads/main' runs-on: ubuntu-latest permissions: - contents: write - outputs: - tagged: ${{ steps.create_tag.outcome == 'success' }} + contents: read steps: - name: Checkout code uses: actions/checkout@v6 @@ -38,29 +27,64 @@ jobs: fetch-depth: 0 persist-credentials: false - - name: Get next version - id: version - run: | - LATEST_TAG=$(git tag --list "v*" --sort=-v:refname | head -n 1) - LATEST_TAG=${LATEST_TAG:-v0.0.0} - VERSION=${LATEST_TAG#v} - IFS=. read MAJOR MINOR PATCH <<< "$VERSION" - PATCH=$((PATCH+1)) - echo "new_tag=v$MAJOR.$MINOR.$PATCH" >> $GITHUB_OUTPUT - - - name: Create tag + # 手动触发仅用于验证 semantic-release 的版本推导结果,不会真正推送 tag。 + - name: Semantic release dry-run + id: semantic_release + uses: cycjimmy/semantic-release-action@v6 + with: + dry_run: true + ci: false env: - PAT: ${{ secrets.PAT_TOKEN }} - TAG: ${{ steps.version.outputs.new_tag }} + GITHUB_TOKEN: ${{ github.token }} + + - name: Show dry-run result run: | - set -e - git config user.name "GitHub Action" - git config user.email "action@github.com" + echo "published=${{ steps.semantic_release.outputs.new_release_published }}" + echo "last_tag=${{ steps.semantic_release.outputs.last_release_git_tag }}" + echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" + echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" - if git show-ref --tags --verify --quiet "refs/tags/$TAG"; then - echo "Tag $TAG already exists, skipping" - exit 0 + release-tag: + if: > + github.event_name == 'workflow_run' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_branch == 'main' && + contains(github.event.workflow_run.head_commit.message, '[release ci]') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ github.event.workflow_run.head_branch }} + + - name: Validate PAT token + run: | + if [ -z "${PAT_TOKEN}" ]; then + echo "::error::PAT_TOKEN is required because a tag created with GITHUB_TOKEN will not trigger publish.yml." + exit 1 fi + env: + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - git tag -a "$TAG" -m "Auto tag $TAG" - git push "https://x-access-token:${PAT}@github.com/${{ github.repository }}.git" "$TAG" + # 真实 workflow_run 负责按 Conventional Commits 计算版本并推送 tag。 + - name: Semantic release + id: semantic_release + uses: cycjimmy/semantic-release-action@v6 + env: + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + + - name: Show release result + env: + PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} + LAST_TAG: ${{ steps.semantic_release.outputs.last_release_git_tag }} + NEXT_VERSION: ${{ steps.semantic_release.outputs.new_release_version }} + NEXT_TAG: ${{ steps.semantic_release.outputs.new_release_git_tag }} + run: | + echo "published=${PUBLISHED}" + echo "last_tag=${LAST_TAG}" + echo "next_version=${NEXT_VERSION}" + echo "next_tag=${NEXT_TAG}" diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 00000000..cf7e1a2c --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,72 @@ +{ + "branches": [ + "main" + ], + "tagFormat": "v${version}", + "plugins": [ + [ + "@semantic-release/commit-analyzer", + { + "releaseRules": [ + { + "type": "feat", + "release": "minor" + }, + { + "type": "fix", + "release": "patch" + }, + { + "type": "perf", + "release": "patch" + }, + { + "type": "refactor", + "release": "patch" + }, + { + "type": "docs", + "release": false + }, + { + "type": "test", + "release": false + }, + { + "type": "chore", + "release": false + }, + { + "type": "build", + "release": false + }, + { + "type": "ci", + "release": false + }, + { + "type": "style", + "release": false + } + ], + "parserOpts": { + "noteKeywords": [ + "BREAKING CHANGE", + "BREAKING CHANGES" + ] + } + } + ], + [ + "@semantic-release/release-notes-generator", + { + "parserOpts": { + "noteKeywords": [ + "BREAKING CHANGE", + "BREAKING CHANGES" + ] + } + } + ] + ] +} diff --git a/ai-plan/public/README.md b/ai-plan/public/README.md index f5efae2b..9ead4d21 100644 --- a/ai-plan/public/README.md +++ b/ai-plan/public/README.md @@ -43,6 +43,10 @@ help the current worktree land on the right recovery documents without scanning - Purpose: continue the data repository persistence hardening plus the settings / serialization follow-up backlog. - Tracking: `ai-plan/public/data-repository-persistence/todos/data-repository-persistence-tracking.md` - Trace: `ai-plan/public/data-repository-persistence/traces/data-repository-persistence-trace.md` +- `semantic-release-versioning` + - Purpose: migrate release version calculation from fixed patch bumps to semantic-release while keeping the existing tag-driven NuGet publish flow. + - Tracking: `ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md` + - Trace: `ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md` ## Worktree To Active Topic Map @@ -63,6 +67,9 @@ help the current worktree land on the right recovery documents without scanning - Branch: `feat/data-repository-persistence` - Worktree hint: `GFramework-data-repository-persistence` - Priority 1: `data-repository-persistence` +- Branch: `feat/semantic-release-versioning` + - Worktree hint: `GFramework` + - Priority 1: `semantic-release-versioning` - Branch: `docs/sdk-update-documentation` - Worktree hint: `GFramework-update-documentation` - Priority 1: `documentation-full-coverage-governance` 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 new file mode 100644 index 00000000..51f52ee3 --- /dev/null +++ b/ai-plan/public/semantic-release-versioning/todos/semantic-release-versioning-tracking.md @@ -0,0 +1,67 @@ +# Semantic Release 版本迁移跟踪 + +## 目标 + +将版本管理从固定 `patch + 1` 的自动打 tag 迁移到 `semantic-release`,同时保留现有 `.github/workflows/publish.yml` +的 tag 触发打包、NuGet 发布、GitHub Packages 发布和 GitHub Release 流程。 + +- 用 `cycjimmy/semantic-release-action` 替换 `auto-tag.yml` 的版本判断和打 tag 逻辑 +- 保留 `publish.yml` 的现有发布实现,不重写 NuGet 流程 +- 避免 `semantic-release` 与 `publish.yml` 重复创建 GitHub Release +- 将版本规则固定为 `feat -> minor`、`fix/perf/refactor -> patch`、`BREAKING CHANGE` 或 `! -> major` +- 为手动 `workflow_dispatch` 保留 dry-run 验证入口,先验证最近提交会算出什么版本 + +## 当前恢复点 + +- 恢复点编号:`SEMREL-RP-001` +- 当前阶段:`Phase 1` +- 当前焦点: + - 增加 `.releaserc.json`,仅启用版本分析与 release notes 生成,不启用 GitHub Release 发布插件 + - 将 `auto-tag.yml` 改成 `workflow_run` 真正打 tag、`workflow_dispatch` 只做 dry-run 的双入口 + - 明确 `PAT_TOKEN` 与 `GITHUB_TOKEN` 的职责边界,确保 tag 继续触发 `publish.yml` + +### 已知风险 + +- `GITHUB_TOKEN` 推送 tag 不会再触发另一个 workflow,真实发布仍需要 `PAT_TOKEN` +- `semantic-release` 的版本判断完全依赖 Conventional Commits;不规范提交会直接影响版本计算 +- 当前仓库本地 `dotnet clean/build` 仍受 WSL fallback NuGet 路径影响,验证时需要继续采用已知可用的直接构建命令 + +## 已完成 + +- 已确认当前版本入口为 `.github/workflows/auto-tag.yml`,现状始终执行 `PATCH + 1` +- 已确认当前 `.github/workflows/publish.yml` 由 tag 触发,并负责 `.nupkg` 打包、发布和 GitHub Release +- 已确认最新 tag 为 `v0.0.222` +- 已确认 `v0.0.222..HEAD` 之间存在 `feat(...)` 提交,按目标规则首次 dry-run 预期版本应为 `v0.1.0` +- 已新增 `.releaserc.json`,仅保留 `@semantic-release/commit-analyzer` 与 + `@semantic-release/release-notes-generator`,避免 `semantic-release` 直接创建 GitHub Release +- 已将 `.github/workflows/auto-tag.yml` 重写为: + - `workflow_run` 在 `main` 上、CI 成功且提交消息包含 `[release ci]` 时执行真实打 tag + - `workflow_dispatch` 只执行 dry-run,输出 `last_tag`、`next_version` 与 `next_tag` +- 已明确真实打 tag 仍使用 `PAT_TOKEN`,因为 `GITHUB_TOKEN` 推送的 tag 不会继续触发 `publish.yml` + +## 验证 + +- `git describe --tags --abbrev=0` + - 结果:通过 + - 备注:当前最新 tag 为 `v0.0.222` +- `git log --pretty=format:%h%x09%s v0.0.222..HEAD` + - 结果:通过 + - 备注:最近版本窗口内存在多条 `feat(...)`,后续 dry-run 预期应提升 `minor` +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` + - 结果:通过 + - 备注:`GFramework.Core.Abstractions` 与 `GFramework.Cqrs.Abstractions` Release 构建通过,`0 warning / 0 error` +- `npx --yes semantic-release --dry-run --no-ci` + - 结果:受阻 + - 备注:当前工作树的本地 tag 历史在 `git fetch --tags` 阶段出现 `would clobber existing tag` 冲突,不能直接作为 dry-run 环境 +- `git clone --branch main --single-branch git@github.com:GeWuYou/GFramework.git /tmp/gframework-semrel-dryrun` + - 结果:通过 + - 备注:已建立干净临时克隆用于 dry-run 验证 +- `npx --yes semantic-release --dry-run --no-ci`(在 `/tmp/gframework-semrel-dryrun`) + - 结果:通过 + - 备注:dry-run 成功识别 `v0.0.222` 为最新 release,并分析 `269` 个提交;按当前规则会提升到下一次 `minor` 发布,预期 tag 为 `v0.1.0` + +## 下一步 + +1. 复核 `workflow_dispatch` dry-run 输出格式是否还需要额外收窄或增加说明 +2. 评估是否要把 `workflow_run` 的 `[release ci]` 门闸改成更显式的 PR label 或 manual approval +3. 若本轮验证通过,按仓库要求创建提交并等待你审阅发版流程细节 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 new file mode 100644 index 00000000..518be71b --- /dev/null +++ b/ai-plan/public/semantic-release-versioning/traces/semantic-release-versioning-trace.md @@ -0,0 +1,39 @@ +# Semantic Release 版本迁移追踪 + +## 2026-04-26 + +### 阶段:方案落地准备(SEMREL-RP-001) + +- 读取当前 `auto-tag.yml` 与 `publish.yml`,确认最小侵入改法应只替换版本判断与打 tag,保留 tag 触发发布链 +- 核对最近 tag 与提交历史: + - 最新 tag 为 `v0.0.222` + - `v0.0.222..HEAD` 含多条 `feat(...)`,按目标规则首次 dry-run 预期结果为 `v0.1.0` +- 补建本主题的 active tracking / trace 入口,并在 `ai-plan/public/README.md` 中为 + `feat/semantic-release-versioning` 建立 worktree 映射 + +### 阶段:配置落地与验证(SEMREL-RP-001) + +- 新增 `.releaserc.json`,显式固定: + - `feat -> minor` + - `fix/perf/refactor -> patch` + - `docs/test/chore/build/ci/style -> no release` + - `BREAKING CHANGE` / `BREAKING CHANGES` 作为 major 信号 +- 重写 `auto-tag.yml`: + - 保留 `workflow_run` 监听 `CI - Build & Test` + - `workflow_dispatch` 变为 dry-run 入口 + - 真实打 tag 改由 `semantic-release-action` 处理,并要求 `PAT_TOKEN` +- 完成最小构建验证: + - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` + - 结果:通过,`0 warning / 0 error` +- 直接在当前工作树执行 `semantic-release --dry-run` 时命中本地 tag 抓取冲突: + - `git fetch --tags ... would clobber existing tag` + - 结论:当前工作树不适合作为 dry-run 验证环境 +- 改用干净临时克隆 `/tmp/gframework-semrel-dryrun` 再跑 dry-run: + - 成功识别 `v0.0.222` 为最新 release + - 成功分析 `269` 个提交 + - 按当前规则得出下一次应为 `minor` 发布,预期版本窗口从 `0.0.222` 提升到 `0.1.0` + +### 下一步 + +1. 复核变更 diff 并创建提交 +2. 向用户说明新的发版链路与可优化点 From d7ddff9f53eec9ea7eac641131628b54cb2f35a3 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 26 Apr 2026 09:49:14 +0800 Subject: [PATCH 2/5] =?UTF-8?q?build(release):=20=E6=94=B6=E7=B4=A7?= =?UTF-8?q?=E6=89=8B=E5=8A=A8=E5=8F=91=E7=89=88=E5=85=A5=E5=8F=A3=E4=B8=8E?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E8=AF=AD=E4=B9=89=E7=BA=A6=E6=9D=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 semantic-release workflow 为 preview 与 release 两种手动触发模式 - 更新 AGENTS 中的 Conventional Commit 规则并禁止 feat(docs) 这类文档提交类型 - 补充 semantic-release 迁移跟踪与二次验证记录 --- .github/workflows/auto-tag.yml | 103 +++++++++--------- AGENTS.md | 13 +++ .../semantic-release-versioning-tracking.md | 18 ++- .../semantic-release-versioning-trace.md | 15 ++- 4 files changed, 87 insertions(+), 62 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index f005608e..172203cb 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -1,56 +1,26 @@ name: Semantic Release Version and Tag on: - workflow_run: - workflows: ["CI - Build & Test"] - types: - - completed - branches: - - main workflow_dispatch: + inputs: + release_mode: + description: Choose preview to inspect the next version, or release to create and push the next tag. + required: true + default: preview + type: choice + options: + - preview + - release + concurrency: group: semantic-release-main cancel-in-progress: false jobs: - dry-run: + semantic-release: if: > - github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main' runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - fetch-depth: 0 - persist-credentials: false - - # 手动触发仅用于验证 semantic-release 的版本推导结果,不会真正推送 tag。 - - name: Semantic release dry-run - id: semantic_release - uses: cycjimmy/semantic-release-action@v6 - with: - dry_run: true - ci: false - env: - GITHUB_TOKEN: ${{ github.token }} - - - name: Show dry-run result - run: | - echo "published=${{ steps.semantic_release.outputs.new_release_published }}" - echo "last_tag=${{ steps.semantic_release.outputs.last_release_git_tag }}" - echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" - echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" - - release-tag: - if: > - github.event_name == 'workflow_run' && - github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.head_branch == 'main' && - contains(github.event.workflow_run.head_commit.message, '[release ci]') - runs-on: ubuntu-latest permissions: contents: write steps: @@ -59,9 +29,16 @@ jobs: with: fetch-depth: 0 persist-credentials: false - ref: ${{ github.event.workflow_run.head_branch }} + + - name: Validate release branch + run: | + if [ "${GITHUB_REF}" != "refs/heads/main" ]; then + echo "::error::Semantic Release Version and Tag only supports workflow_dispatch on main." + exit 1 + fi - name: Validate PAT token + if: inputs.release_mode == 'release' run: | if [ -z "${PAT_TOKEN}" ]; then echo "::error::PAT_TOKEN is required because a tag created with GITHUB_TOKEN will not trigger publish.yml." @@ -70,21 +47,41 @@ jobs: env: PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - # 真实 workflow_run 负责按 Conventional Commits 计算版本并推送 tag。 + # preview 模式只输出版本推导结果,不会真正推送 tag。 + - name: Semantic release preview + if: inputs.release_mode == 'preview' + id: semantic_release + uses: cycjimmy/semantic-release-action@v6 + with: + dry_run: true + ci: false + env: + GITHUB_TOKEN: ${{ github.token }} + + # release 模式由维护者手动触发,直接创建并推送下一个 tag。 - name: Semantic release + if: inputs.release_mode == 'release' id: semantic_release uses: cycjimmy/semantic-release-action@v6 env: GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} - - name: Show release result - env: - PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} - LAST_TAG: ${{ steps.semantic_release.outputs.last_release_git_tag }} - NEXT_VERSION: ${{ steps.semantic_release.outputs.new_release_version }} - NEXT_TAG: ${{ steps.semantic_release.outputs.new_release_git_tag }} + - name: Show result run: | - echo "published=${PUBLISHED}" - echo "last_tag=${LAST_TAG}" - echo "next_version=${NEXT_VERSION}" - echo "next_tag=${NEXT_TAG}" + echo "mode=${{ inputs.release_mode }}" + echo "published=${{ steps.semantic_release.outputs.new_release_published }}" + echo "last_tag=${{ steps.semantic_release.outputs.last_release_git_tag }}" + echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" + echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" + + - name: Write summary + run: | + { + echo "## Semantic Release" + echo + echo "- Mode: \`${{ inputs.release_mode }}\`" + echo "- Published: \`${{ steps.semantic_release.outputs.new_release_published }}\`" + echo "- Last tag: \`${{ steps.semantic_release.outputs.last_release_git_tag }}\`" + echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`" + echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" + } >> "${GITHUB_STEP_SUMMARY}" diff --git a/AGENTS.md b/AGENTS.md index 87bd8d49..1383f5f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,6 +55,19 @@ All AI agents and contributors must follow these rules when writing, reviewing, - The commit `body` MUST use unordered list items, and each item MUST start with a verb such as `新增`、`修复`、`优化`、 `更新`、`补充`、`重构`. - Each commit body bullet MUST describe one independent change point; avoid repeated or redundant descriptions. +- Commit `type` MUST reflect release semantics instead of author intent: + - Use `feat` only for user-facing or consumer-facing capability additions that should raise the next released version's + `minor` segment. + - Use `fix` for behavior corrections, `perf` for observable performance improvements, and `refactor` only for + non-feature code restructuring. + - Use `docs`、`test`、`chore`、`build`、`ci`、`style` for their literal categories; do not encode these changes as + `feat` just because they feel important. +- Documentation-only changes MUST NOT use `feat`, including new guides, refreshed examples, navigation updates, and + adoption notes for existing capabilities. If a commit changes both product behavior and related docs, either split the + commit or use `feat` only when the code/package behavior is the primary released change. +- Contributors MUST avoid ambiguous scopes such as `feat(docs)` for documentation work. If the change only affects docs, + prefer `docs()`; if it adds a real capability in a docs-related toolchain, use the scope of that + actual subsystem instead of `docs`. - Keep technical terms in English when they are established project terms, such as `API`、`Model`、`System`. - When composing a multi-line commit body from shell commands, contributors MUST NOT rely on Bash `$"..."` quoting for newline escapes, because it passes literal `\n` sequences to Git. Use multiple `-m` flags or ANSI-C `$'...'` 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 51f52ee3..41dc324a 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 @@ -17,7 +17,7 @@ - 当前阶段:`Phase 1` - 当前焦点: - 增加 `.releaserc.json`,仅启用版本分析与 release notes 生成,不启用 GitHub Release 发布插件 - - 将 `auto-tag.yml` 改成 `workflow_run` 真正打 tag、`workflow_dispatch` 只做 dry-run 的双入口 + - 将 `auto-tag.yml` 改成纯 `workflow_dispatch` 双模式入口,由维护者手动选择 `preview` 或 `release` - 明确 `PAT_TOKEN` 与 `GITHUB_TOKEN` 的职责边界,确保 tag 继续触发 `publish.yml` ### 已知风险 @@ -35,9 +35,12 @@ - 已新增 `.releaserc.json`,仅保留 `@semantic-release/commit-analyzer` 与 `@semantic-release/release-notes-generator`,避免 `semantic-release` 直接创建 GitHub Release - 已将 `.github/workflows/auto-tag.yml` 重写为: - - `workflow_run` 在 `main` 上、CI 成功且提交消息包含 `[release ci]` 时执行真实打 tag - - `workflow_dispatch` 只执行 dry-run,输出 `last_tag`、`next_version` 与 `next_tag` + - `workflow_dispatch` 由维护者手动选择 `preview` 或 `release` + - `preview` 只执行 dry-run,输出 `last_tag`、`next_version` 与 `next_tag` + - `release` 由维护者手动触发真实打 tag,并把结果写入 job summary - 已明确真实打 tag 仍使用 `PAT_TOKEN`,因为 `GITHUB_TOKEN` 推送的 tag 不会继续触发 `publish.yml` +- 已更新 `AGENTS.md` 的 Conventional Commit 规则,显式禁止把纯文档变更写成 `feat(...)` 或 `feat(docs)` +- 已移除基于 `workflow_run` 和 `[release ci]` 的自动发版门闸,后续版本预览与真实发版都由维护者手动触发 ## 验证 @@ -59,9 +62,12 @@ - `npx --yes semantic-release --dry-run --no-ci`(在 `/tmp/gframework-semrel-dryrun`) - 结果:通过 - 备注:dry-run 成功识别 `v0.0.222` 为最新 release,并分析 `269` 个提交;按当前规则会提升到下一次 `minor` 发布,预期 tag 为 `v0.1.0` +- `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=`(手动发版入口调整后复验) + - 结果:通过 + - 备注:`0 warning / 0 error` ## 下一步 -1. 复核 `workflow_dispatch` dry-run 输出格式是否还需要额外收窄或增加说明 -2. 评估是否要把 `workflow_run` 的 `[release ci]` 门闸改成更显式的 PR label 或 manual approval -3. 若本轮验证通过,按仓库要求创建提交并等待你审阅发版流程细节 +1. 复核 `workflow_dispatch` 的 `preview` / `release` 两种模式命名是否还要进一步收紧 +2. 评估是否要在 release 模式中补充额外输入,例如预期 tag 确认或二次确认文本 +3. 若本轮验证通过,按仓库要求创建补充提交并等待你审阅手动发版流程细节 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 518be71b..b1a63edc 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 @@ -19,9 +19,18 @@ - `docs/test/chore/build/ci/style -> no release` - `BREAKING CHANGE` / `BREAKING CHANGES` 作为 major 信号 - 重写 `auto-tag.yml`: - - 保留 `workflow_run` 监听 `CI - Build & Test` - - `workflow_dispatch` 变为 dry-run 入口 - - 真实打 tag 改由 `semantic-release-action` 处理,并要求 `PAT_TOKEN` + - 改为纯 `workflow_dispatch` 手动入口 + - 增加 `release_mode=preview|release` 输入 + - `preview` 只跑 dry-run 并输出 `last_tag`、`next_version`、`next_tag` + - `release` 由维护者手动触发真实打 tag,并要求 `PAT_TOKEN` +- 收紧 `AGENTS.md` 的提交信息约束,明确提交类型必须反映 release semantics,纯文档变更禁止写成 `feat(...)` +- 按用户反馈继续调整发版入口: + - 删除基于 `workflow_run` 的自动发版路径 + - 统一改为 `workflow_dispatch` 手动触发 + - 通过 `release_mode=preview|release` 区分“查看下个版本”和“真实打 tag” +- 复验最小构建命令: + - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` + - 结果:通过,`0 warning / 0 error` - 完成最小构建验证: - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` - 结果:通过,`0 warning / 0 error` From 16e04cc541d8c0cd5a5442d97a9528229434a13e Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 26 Apr 2026 10:10:53 +0800 Subject: [PATCH 3/5] =?UTF-8?q?build(release):=20=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E4=B8=BA=E5=90=8C=E6=AC=A1=E8=BF=90=E8=A1=8C=E9=A2=84=E8=A7=88?= =?UTF-8?q?=E5=90=8E=E5=AE=A1=E6=89=B9=E5=8F=91=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 semantic-release workflow 为 preview 预览后经 environment 审批继续 release - 绑定同一 SHA 的 preview 与 release 结果并在 summary 中展示版本信息 - 更新 semantic-release 迁移跟踪文档记录新的审批式发版链路 --- .github/workflows/auto-tag.yml | 110 ++++++++++++------ .../semantic-release-versioning-tracking.md | 14 ++- .../semantic-release-versioning-trace.md | 4 +- 3 files changed, 85 insertions(+), 43 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 172203cb..9f56dbd2 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -2,33 +2,30 @@ name: Semantic Release Version and Tag on: workflow_dispatch: - inputs: - release_mode: - description: Choose preview to inspect the next version, or release to create and push the next tag. - required: true - default: preview - type: choice - options: - - preview - - release concurrency: group: semantic-release-main cancel-in-progress: false jobs: - semantic-release: + preview: if: > github.ref == 'refs/heads/main' runs-on: ubuntu-latest permissions: - contents: write + contents: read + outputs: + published: ${{ steps.semantic_release.outputs.new_release_published }} + last_tag: ${{ steps.semantic_release.outputs.last_release_git_tag }} + next_version: ${{ steps.semantic_release.outputs.new_release_version }} + next_tag: ${{ steps.semantic_release.outputs.new_release_git_tag }} steps: - name: Checkout code uses: actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false + ref: ${{ github.sha }} - name: Validate release branch run: | @@ -37,19 +34,8 @@ jobs: exit 1 fi - - name: Validate PAT token - if: inputs.release_mode == 'release' - run: | - if [ -z "${PAT_TOKEN}" ]; then - echo "::error::PAT_TOKEN is required because a tag created with GITHUB_TOKEN will not trigger publish.yml." - exit 1 - fi - env: - PAT_TOKEN: ${{ secrets.PAT_TOKEN }} - - # preview 模式只输出版本推导结果,不会真正推送 tag。 + # preview 始终先运行,用于给当前 SHA 生成待发布版本预览。 - name: Semantic release preview - if: inputs.release_mode == 'preview' id: semantic_release uses: cycjimmy/semantic-release-action@v6 with: @@ -58,28 +44,80 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} - # release 模式由维护者手动触发,直接创建并推送下一个 tag。 - - name: Semantic release - if: inputs.release_mode == 'release' - id: semantic_release - uses: cycjimmy/semantic-release-action@v6 - env: - GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} - - - name: Show result + - name: Show preview result run: | - echo "mode=${{ inputs.release_mode }}" echo "published=${{ steps.semantic_release.outputs.new_release_published }}" echo "last_tag=${{ steps.semantic_release.outputs.last_release_git_tag }}" echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" - - name: Write summary + - name: Write preview summary run: | { - echo "## Semantic Release" + echo "## Semantic Release Preview" echo - echo "- Mode: \`${{ inputs.release_mode }}\`" + echo "- Commit: \`${{ github.sha }}\`" + echo "- Release needed: \`${{ steps.semantic_release.outputs.new_release_published }}\`" + echo "- Last tag: \`${{ steps.semantic_release.outputs.last_release_git_tag }}\`" + echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`" + echo "- Next tag: \`${{ steps.semantic_release.outputs.new_release_git_tag }}\`" + echo + echo "If the version looks correct, approve the \`release-approval\` environment to continue." + } >> "${GITHUB_STEP_SUMMARY}" + + release: + if: > + github.ref == 'refs/heads/main' && + needs.preview.outputs.published == 'true' + needs: + - preview + runs-on: ubuntu-latest + permissions: + contents: write + environment: + name: release-approval + steps: + - name: Checkout code + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ github.sha }} + + - name: Validate PAT token + run: | + if [ -z "${PAT_TOKEN}" ]; then + echo "::error::PAT_TOKEN is required because a tag created with GITHUB_TOKEN will not trigger publish.yml." + exit 1 + fi + env: + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + - name: Semantic release + id: semantic_release + uses: cycjimmy/semantic-release-action@v6 + env: + GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + + - name: Show release result + run: | + echo "published=${{ steps.semantic_release.outputs.new_release_published }}" + echo "preview_last_tag=${{ needs.preview.outputs.last_tag }}" + echo "preview_next_version=${{ needs.preview.outputs.next_version }}" + echo "preview_next_tag=${{ needs.preview.outputs.next_tag }}" + echo "last_tag=${{ steps.semantic_release.outputs.last_release_git_tag }}" + echo "next_version=${{ steps.semantic_release.outputs.new_release_version }}" + echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" + + - name: Write release summary + run: | + { + echo "## Semantic Release Publish" + echo + echo "- Commit: \`${{ github.sha }}\`" + echo "- Preview last tag: \`${{ needs.preview.outputs.last_tag }}\`" + echo "- Preview next version: \`${{ needs.preview.outputs.next_version }}\`" + echo "- Preview next tag: \`${{ needs.preview.outputs.next_tag }}\`" echo "- Published: \`${{ steps.semantic_release.outputs.new_release_published }}\`" echo "- Last tag: \`${{ steps.semantic_release.outputs.last_release_git_tag }}\`" echo "- Next version: \`${{ steps.semantic_release.outputs.new_release_version }}\`" 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 41dc324a..9841fab7 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 @@ -17,7 +17,7 @@ - 当前阶段:`Phase 1` - 当前焦点: - 增加 `.releaserc.json`,仅启用版本分析与 release notes 生成,不启用 GitHub Release 发布插件 - - 将 `auto-tag.yml` 改成纯 `workflow_dispatch` 双模式入口,由维护者手动选择 `preview` 或 `release` + - 将 `auto-tag.yml` 改成同一次 `workflow_dispatch` 里先 `preview`,再等待 environment 审批后继续 `release` - 明确 `PAT_TOKEN` 与 `GITHUB_TOKEN` 的职责边界,确保 tag 继续触发 `publish.yml` ### 已知风险 @@ -35,12 +35,14 @@ - 已新增 `.releaserc.json`,仅保留 `@semantic-release/commit-analyzer` 与 `@semantic-release/release-notes-generator`,避免 `semantic-release` 直接创建 GitHub Release - 已将 `.github/workflows/auto-tag.yml` 重写为: - - `workflow_dispatch` 由维护者手动选择 `preview` 或 `release` + - `workflow_dispatch` 启动后总是先跑 `preview` - `preview` 只执行 dry-run,输出 `last_tag`、`next_version` 与 `next_tag` - - `release` 由维护者手动触发真实打 tag,并把结果写入 job summary + - `release` job 依赖 `preview` 输出,并通过 `release-approval` environment 暂停等待人工确认 + - 人工批准后,`release` 在同一 SHA 上执行真实打 tag,并把 preview / release 结果都写入 job summary - 已明确真实打 tag 仍使用 `PAT_TOKEN`,因为 `GITHUB_TOKEN` 推送的 tag 不会继续触发 `publish.yml` - 已更新 `AGENTS.md` 的 Conventional Commit 规则,显式禁止把纯文档变更写成 `feat(...)` 或 `feat(docs)` - 已移除基于 `workflow_run` 和 `[release ci]` 的自动发版门闸,后续版本预览与真实发版都由维护者手动触发 +- 已将 release 流程从“两次独立 workflow_dispatch”收敛为“同一次 run 里 preview + 审批 + release”的链路 ## 验证 @@ -68,6 +70,6 @@ ## 下一步 -1. 复核 `workflow_dispatch` 的 `preview` / `release` 两种模式命名是否还要进一步收紧 -2. 评估是否要在 release 模式中补充额外输入,例如预期 tag 确认或二次确认文本 -3. 若本轮验证通过,按仓库要求创建补充提交并等待你审阅手动发版流程细节 +1. 在仓库 Settings -> Environments 中为 `release-approval` 配置 required reviewers,确保 workflow 会在 preview 后真正暂停 +2. 复核 Actions summary 呈现方式是否还需要更醒目的版本展示 +3. 若本轮验证通过,按仓库要求创建补充提交并等待你审阅同次 run 的手动发版流程细节 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 b1a63edc..63a3851b 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 @@ -27,7 +27,9 @@ - 按用户反馈继续调整发版入口: - 删除基于 `workflow_run` 的自动发版路径 - 统一改为 `workflow_dispatch` 手动触发 - - 通过 `release_mode=preview|release` 区分“查看下个版本”和“真实打 tag” + - 先在同一次 run 中执行 `preview` + - 再通过 `release-approval` environment 做人工确认 + - 审批通过后继续同一 SHA 的真实 `release` - 复验最小构建命令: - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` - 结果:通过,`0 warning / 0 error` From 3ca095e987353aecae7e3df1db1e0e85af307676 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 26 Apr 2026 10:26:09 +0800 Subject: [PATCH 4/5] =?UTF-8?q?chore(pr-review):=20=E6=94=AF=E6=8C=81=20Ge?= =?UTF-8?q?mini=20reviewer=20=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 gemini-code-assist reviewer 到 PR review 抓取脚本的受支持列表并输出汇总信息 - 更新 gframework-pr-review skill 文档,补充 Gemini reviewer 的工作流、输出预期和触发示例 --- .agents/skills/gframework-pr-review/SKILL.md | 9 +++++---- .../scripts/fetch_current_pr_review.py | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.agents/skills/gframework-pr-review/SKILL.md b/.agents/skills/gframework-pr-review/SKILL.md index 473f9aa0..20db5d4b 100644 --- a/.agents/skills/gframework-pr-review/SKILL.md +++ b/.agents/skills/gframework-pr-review/SKILL.md @@ -1,6 +1,6 @@ --- name: gframework-pr-review -description: Repository-specific GitHub PR review workflow for the GFramework repo. Use when Codex needs to inspect the GitHub pull request for the current branch, extract AI review findings from CodeRabbit or greptile-apps, read failed checks, MegaLinter warnings, or failed test signals from the PR page, and then verify which findings should be fixed in the local codebase. Trigger explicitly with $gframework-pr-review or with prompts such as "look at the current PR", "extract CodeRabbit comments", "extract Greptile comments", or "check Failed Tests on the PR". +description: Repository-specific GitHub PR review workflow for the GFramework repo. Use when Codex needs to inspect the GitHub pull request for the current branch, extract AI review findings from CodeRabbit, greptile-apps, or gemini-code-assist, read failed checks, MegaLinter warnings, or failed test signals from the PR page, and then verify which findings should be fixed in the local codebase. Trigger explicitly with $gframework-pr-review or with prompts such as "look at the current PR", "extract CodeRabbit comments", "extract Greptile comments", "extract Gemini comments", or "check Failed Tests on the PR". --- # GFramework PR Review @@ -20,7 +20,7 @@ Shortcut: `$gframework-pr-review` - fetch PR metadata, issue comments, reviews, and review comments through the GitHub API - extract CodeRabbit-specific summary blocks such as `Summary by CodeRabbit` and actionable-comment rollups when present - parse the latest CodeRabbit review body itself, including folded sections such as `🧹 Nitpick comments (N)` and the overall AI-agent prompt - - capture unresolved latest-head review threads for supported AI reviewers, including both `coderabbitai[bot]` and `greptile-apps[bot]` + - capture unresolved latest-head review threads for supported AI reviewers, including `coderabbitai[bot]`, `greptile-apps[bot]`, and `gemini-code-assist[bot]` - surface which supported AI reviewers currently have open latest-commit review threads, even when they do not use CodeRabbit-style issue comments - fetch the latest head commit review threads from the GitHub PR API - prefer unresolved review threads on the latest head commit over older summary-only signals @@ -53,7 +53,7 @@ Shortcut: `$gframework-pr-review` The script should produce: - PR metadata: number, title, state, branch, URL -- Supported AI reviewer summary, including latest reviews and open-thread counts for `coderabbitai[bot]` and `greptile-apps[bot]` +- Supported AI reviewer summary, including latest reviews and open-thread counts for `coderabbitai[bot]`, `greptile-apps[bot]`, and `gemini-code-assist[bot]` - CodeRabbit summary block from issue comments when available - Folded latest-review sections such as `Nitpick comments (N)` when CodeRabbit puts them in the review body instead of issue comments - Parsed latest head-review threads, with unresolved threads clearly separated @@ -72,7 +72,7 @@ The script should produce: - If the current WSL session resolves `git.exe` but cannot execute it cleanly, keep using the explicit Linux worktree binding instead of retrying Windows Git. - Prefer GitHub API results over PR HTML. The PR HTML page is now a fallback/debugging source, not the primary source of truth. - If the summary block and the latest head review threads disagree, trust the latest unresolved head-review threads and treat older summary findings as stale until re-verified locally. -- Do not assume every AI reviewer behaves like CodeRabbit. `greptile-apps[bot]` findings may exist only as latest-head review threads, without CodeRabbit-style issue comments or folded review-body sections. +- Do not assume every AI reviewer behaves like CodeRabbit. `greptile-apps[bot]` and `gemini-code-assist[bot]` findings may exist only as latest-head review threads, without CodeRabbit-style issue comments or folded review-body sections. - Treat GitHub Actions comments with `Success with warnings` as actionable review input when they include concrete linter diagnostics such as `MegaLinter` detailed issues; do not skip them just because the parent check is green. - Do not assume all CodeRabbit findings live in issue comments. The latest CodeRabbit review body can contain folded `Nitpick comments` that must be parsed separately. - If the raw JSON is too large to inspect safely in the terminal, rerun with `--json-output ` and query the saved file with `jq` or rerun with `--section` / `--path` filters. @@ -84,5 +84,6 @@ The script should produce: - `Use $gframework-pr-review on the current branch` - `Check the current PR and extract CodeRabbit suggestions` - `Check the current PR and extract Greptile suggestions` +- `Check the current PR and extract Gemini Code Assist suggestions` - `Look for Failed Tests on the PR page` - `先用 $gframework-pr-review 看当前分支 PR` diff --git a/.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py b/.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py index ee9d870c..773503a7 100644 --- a/.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py +++ b/.agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py @@ -29,6 +29,7 @@ WORK_TREE_ENVIRONMENT_KEY = "GFRAMEWORK_WORK_TREE" USER_AGENT = "codex-gframework-pr-review" CODERABBIT_LOGIN = "coderabbitai[bot]" GREPTILE_LOGIN = "greptile-apps[bot]" +GEMINI_CODE_ASSIST_LOGIN = "gemini-code-assist[bot]" GITHUB_ACTIONS_LOGIN = "github-actions[bot]" REVIEW_COMMENT_ADDRESSED_MARKER = "" VISIBLE_ADDRESSED_IN_COMMIT_PATTERN = re.compile(r"✅\s*Addressed in commit\s+[0-9a-f]{7,40}", re.I) @@ -47,6 +48,12 @@ SUPPORTED_AI_REVIEWERS = ( "display_name": "Greptile", "supports_review_body_parsing": False, }, + { + "slug": "gemini-code-assist", + "login": GEMINI_CODE_ASSIST_LOGIN, + "display_name": "Gemini Code Assist", + "supports_review_body_parsing": False, + }, ) DISPLAY_SECTION_CHOICES = ( "pr", From 9f04c0b5f8456b77ac05423be28bd2e1f852a595 Mon Sep 17 00:00:00 2001 From: gewuyou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 26 Apr 2026 10:36:27 +0800 Subject: [PATCH 5/5] =?UTF-8?q?fix(release):=20=E4=BF=AE=E5=A4=8D=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E5=8C=96=E5=8F=91=E7=89=88=E5=AE=A1=E6=9F=A5=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 semantic-release 配置,切换到 conventionalcommits preset 并显式声明 breaking、revert、patch 语义 - 更新 auto-tag workflow,补充 preview 成功守卫、PAT 存活性校验、snapshot 说明与 release notes summary 输出 - 更新 AGENTS 与 semantic-release topic 的 tracking/trace,记录当前发版规则、验证结果与恢复点 --- .github/workflows/auto-tag.yml | 67 ++++++++++++++--- .releaserc.json | 10 +++ AGENTS.md | 6 +- .../semantic-release-versioning-tracking.md | 46 +++++++++--- .../semantic-release-versioning-trace.md | 72 +++++++++---------- 5 files changed, 141 insertions(+), 60 deletions(-) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 9f56dbd2..d502bd64 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -27,13 +27,6 @@ jobs: persist-credentials: false ref: ${{ github.sha }} - - name: Validate release branch - run: | - if [ "${GITHUB_REF}" != "refs/heads/main" ]; then - echo "::error::Semantic Release Version and Tag only supports workflow_dispatch on main." - exit 1 - fi - # preview 始终先运行,用于给当前 SHA 生成待发布版本预览。 - name: Semantic release preview id: semantic_release @@ -41,6 +34,8 @@ jobs: with: dry_run: true ci: false + extra_plugins: | + conventional-changelog-conventionalcommits@9.1.0 env: GITHUB_TOKEN: ${{ github.token }} @@ -52,6 +47,9 @@ jobs: echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" - name: Write preview summary + env: + RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} + RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} run: | { echo "## Semantic Release Preview" @@ -61,6 +59,15 @@ jobs: echo "- Last tag: \`${{ steps.semantic_release.outputs.last_release_git_tag }}\`" 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 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 + echo + echo "
Preview release notes" + echo + printf '%s\n' "${RELEASE_NOTES}" + echo + echo "
" + fi echo echo "If the version looks correct, approve the \`release-approval\` environment to continue." } >> "${GITHUB_STEP_SUMMARY}" @@ -68,6 +75,7 @@ jobs: release: if: > github.ref == 'refs/heads/main' && + needs.preview.result == 'success' && needs.preview.outputs.published == 'true' needs: - preview @@ -85,17 +93,46 @@ jobs: ref: ${{ github.sha }} - name: Validate PAT token + env: + PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + REPO_API_URL: ${{ github.api_url }}/repos/${{ github.repository }} run: | if [ -z "${PAT_TOKEN}" ]; then echo "::error::PAT_TOKEN is required because a tag created with GITHUB_TOKEN will not trigger publish.yml." exit 1 fi - env: - PAT_TOKEN: ${{ secrets.PAT_TOKEN }} + + response_file="$(mktemp)" + status_code="$( + curl -sS -o "${response_file}" -w "%{http_code}" \ + -H "Authorization: Bearer ${PAT_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "${REPO_API_URL}" + )" + + case "${status_code}" in + 200) + ;; + 401|403) + echo "::error::PAT_TOKEN is invalid or lacks access to ${GITHUB_REPOSITORY} (HTTP ${status_code})." + cat "${response_file}" + exit 1 + ;; + *) + echo "::error::Failed to validate PAT_TOKEN against ${REPO_API_URL} (HTTP ${status_code})." + cat "${response_file}" + exit 1 + ;; + esac - name: Semantic release id: semantic_release uses: cycjimmy/semantic-release-action@v6 + with: + dry_run: false + extra_plugins: | + conventional-changelog-conventionalcommits@9.1.0 env: GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} @@ -110,6 +147,9 @@ jobs: echo "next_tag=${{ steps.semantic_release.outputs.new_release_git_tag }}" - name: Write release summary + env: + RELEASE_PUBLISHED: ${{ steps.semantic_release.outputs.new_release_published }} + RELEASE_NOTES: ${{ steps.semantic_release.outputs.new_release_notes }} run: | { echo "## Semantic Release Publish" @@ -122,4 +162,13 @@ jobs: echo "- Last tag: \`${{ steps.semantic_release.outputs.last_release_git_tag }}\`" 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 + echo + echo "
Published release notes" + echo + printf '%s\n' "${RELEASE_NOTES}" + echo + echo "
" + fi } >> "${GITHUB_STEP_SUMMARY}" diff --git a/.releaserc.json b/.releaserc.json index cf7e1a2c..c5d1ba04 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -7,7 +7,16 @@ [ "@semantic-release/commit-analyzer", { + "preset": "conventionalcommits", "releaseRules": [ + { + "breaking": true, + "release": "major" + }, + { + "revert": true, + "release": "patch" + }, { "type": "feat", "release": "minor" @@ -60,6 +69,7 @@ [ "@semantic-release/release-notes-generator", { + "preset": "conventionalcommits", "parserOpts": { "noteKeywords": [ "BREAKING CHANGE", diff --git a/AGENTS.md b/AGENTS.md index 1383f5f9..06610a00 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,9 +59,11 @@ All AI agents and contributors must follow these rules when writing, reviewing, - Use `feat` only for user-facing or consumer-facing capability additions that should raise the next released version's `minor` segment. - Use `fix` for behavior corrections, `perf` for observable performance improvements, and `refactor` only for - non-feature code restructuring. + non-feature code restructuring; these should raise the next released version's `patch` segment. - Use `docs`、`test`、`chore`、`build`、`ci`、`style` for their literal categories; do not encode these changes as - `feat` just because they feel important. + `feat` just because they feel important. These categories MUST NOT trigger a release. + - Use `BREAKING CHANGE` in the commit footer or `!` after the type / scope header (for example `feat!:` or + `feat(core)!:`) when the change should raise the next released version's `major` segment. - Documentation-only changes MUST NOT use `feat`, including new guides, refreshed examples, navigation updates, and adoption notes for existing capabilities. If a commit changes both product behavior and related docs, either split the commit or use `feat` only when the code/package behavior is the primary released change. 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 9841fab7..ba8ebb56 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 @@ -13,18 +13,20 @@ ## 当前恢复点 -- 恢复点编号:`SEMREL-RP-001` -- 当前阶段:`Phase 1` +- 恢复点编号:`SEMREL-RP-002` +- 当前阶段:`Phase 2` - 当前焦点: - - 增加 `.releaserc.json`,仅启用版本分析与 release notes 生成,不启用 GitHub Release 发布插件 - - 将 `auto-tag.yml` 改成同一次 `workflow_dispatch` 里先 `preview`,再等待 environment 审批后继续 `release` - - 明确 `PAT_TOKEN` 与 `GITHUB_TOKEN` 的职责边界,确保 tag 继续触发 `publish.yml` + - 让 `.releaserc.json` 对 `BREAKING CHANGE` 与 `feat!:` / `feat(scope)!:` 的 `major` 语义与文档保持一致 + - 将 `auto-tag.yml` 的 preview / release 输出与 PR review 建议对齐,避免 release notes 被静默丢弃 + - 提前校验 `PAT_TOKEN` 的真实可用性,并把当前 PR review 修复结果同步回 `AGENTS.md` 与 active trace ### 已知风险 - `GITHUB_TOKEN` 推送 tag 不会再触发另一个 workflow,真实发布仍需要 `PAT_TOKEN` - `semantic-release` 的版本判断完全依赖 Conventional Commits;不规范提交会直接影响版本计算 -- 当前仓库本地 `dotnet clean/build` 仍受 WSL fallback NuGet 路径影响,验证时需要继续采用已知可用的直接构建命令 +- `cycjimmy/semantic-release-action@v6` 需要在 preview / release 两端都安装 `conventional-changelog-conventionalcommits` + 以保证 `conventionalcommits` preset 在 GitHub Actions 中可解析 +- 当前仓库本地 `dotnet clean/build` 会带出既有 analyzer warnings;本轮仅修正发版配置与文档,不额外处理这些历史 warning ## 已完成 @@ -34,15 +36,32 @@ - 已确认 `v0.0.222..HEAD` 之间存在 `feat(...)` 提交,按目标规则首次 dry-run 预期版本应为 `v0.1.0` - 已新增 `.releaserc.json`,仅保留 `@semantic-release/commit-analyzer` 与 `@semantic-release/release-notes-generator`,避免 `semantic-release` 直接创建 GitHub Release +- 已将 `.releaserc.json` 的 `commit-analyzer` / `release-notes-generator` 同步切换到 `conventionalcommits` + preset,并显式声明: + - `breaking -> major` + - `revert -> patch` + - `feat -> minor` + - `fix/perf/refactor -> patch` + - `docs/test/chore/build/ci/style -> no release` - 已将 `.github/workflows/auto-tag.yml` 重写为: - `workflow_dispatch` 启动后总是先跑 `preview` - `preview` 只执行 dry-run,输出 `last_tag`、`next_version` 与 `next_tag` - `release` job 依赖 `preview` 输出,并通过 `release-approval` environment 暂停等待人工确认 - 人工批准后,`release` 在同一 SHA 上执行真实打 tag,并把 preview / release 结果都写入 job summary +- 已按 PR review 修复 `auto-tag.yml`: + - 删除 preview job 中与 job 级 `if` 重复的运行时分支校验 + - 为 release job 增加 `needs.preview.result == 'success'` 守卫 + - 为 preview / release 的 semantic-release action 显式安装 `conventional-changelog-conventionalcommits@9.1.0` + - 在 release 前通过 GitHub API 校验 `PAT_TOKEN` 是否真实可访问当前仓库 + - 在 preview / release summary 中补充 snapshot 语义与生成的 release notes - 已明确真实打 tag 仍使用 `PAT_TOKEN`,因为 `GITHUB_TOKEN` 推送的 tag 不会继续触发 `publish.yml` -- 已更新 `AGENTS.md` 的 Conventional Commit 规则,显式禁止把纯文档变更写成 `feat(...)` 或 `feat(docs)` +- 已更新 `AGENTS.md` 的 Conventional Commit 规则,显式补充: + - `fix/perf/refactor -> patch` + - `docs/test/chore/build/ci/style -> no release` + - `BREAKING CHANGE` 或 `!` header -> major - 已移除基于 `workflow_run` 和 `[release ci]` 的自动发版门闸,后续版本预览与真实发版都由维护者手动触发 - 已将 release 流程从“两次独立 workflow_dispatch”收敛为“同一次 run 里 preview + 审批 + release”的链路 +- 已精简 active trace,移除已废弃的 `release_mode=preview|release` 中间方案,保留当前有效恢复点 ## 验证 @@ -64,12 +83,19 @@ - `npx --yes semantic-release --dry-run --no-ci`(在 `/tmp/gframework-semrel-dryrun`) - 结果:通过 - 备注:dry-run 成功识别 `v0.0.222` 为最新 release,并分析 `269` 个提交;按当前规则会提升到下一次 `minor` 发布,预期 tag 为 `v0.1.0` +- `npx --yes -p semantic-release -p conventional-changelog-conventionalcommits@9.1.0 semantic-release --dry-run --no-ci`(在 `/tmp/gframework-semrel-dryrun`) + - 结果:通过 + - 备注:成功加载 `@semantic-release/commit-analyzer` 与 `@semantic-release/release-notes-generator`,证明 + `conventionalcommits` preset 包可被解析;本次 dry-run 未继续出版本,是因为干净克隆的 `main` 已落后远端 - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=`(手动发版入口调整后复验) - 结果:通过 - 备注:`0 warning / 0 error` +- `dotnet build GFramework.sln -c Release` + - 结果:通过 + - 备注:Release 构建完成,`639 warning / 0 error`;warning 为仓库既有基线,与本轮 workflow / doc 改动无新增关联项 ## 下一步 -1. 在仓库 Settings -> Environments 中为 `release-approval` 配置 required reviewers,确保 workflow 会在 preview 后真正暂停 -2. 复核 Actions summary 呈现方式是否还需要更醒目的版本展示 -3. 若本轮验证通过,按仓库要求创建补充提交并等待你审阅同次 run 的手动发版流程细节 +1. 复核当前 PR review 的 open threads 是否只剩等待 push 的已修复项 +2. 将本轮修复提交到当前分支,等待 GitHub reviewer 重新评估 +3. 若后续需要,再在真实仓库主线最新快照上复验一次 `semantic-release` dry-run 结果展示 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 63a3851b..5aba262a 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 @@ -2,49 +2,43 @@ ## 2026-04-26 -### 阶段:方案落地准备(SEMREL-RP-001) +### 当前恢复点(SEMREL-RP-002) -- 读取当前 `auto-tag.yml` 与 `publish.yml`,确认最小侵入改法应只替换版本判断与打 tag,保留 tag 触发发布链 -- 核对最近 tag 与提交历史: - - 最新 tag 为 `v0.0.222` - - `v0.0.222..HEAD` 含多条 `feat(...)`,按目标规则首次 dry-run 预期结果为 `v0.1.0` -- 补建本主题的 active tracking / trace 入口,并在 `ai-plan/public/README.md` 中为 - `feat/semantic-release-versioning` 建立 worktree 映射 - -### 阶段:配置落地与验证(SEMREL-RP-001) - -- 新增 `.releaserc.json`,显式固定: +- 当前链路: + - `workflow_dispatch` 手动启动 + - `preview` 对 dispatch SHA 执行 dry-run + - `release-approval` environment 审批 + - `release` 在同一次 run、同一 SHA 上执行真实打 tag +- 当前规则: + - `conventionalcommits` preset 负责解析 `feat!:` / `feat(scope)!:` 与 `BREAKING CHANGE` - `feat -> minor` - `fix/perf/refactor -> patch` - `docs/test/chore/build/ci/style -> no release` - - `BREAKING CHANGE` / `BREAKING CHANGES` 作为 major 信号 -- 重写 `auto-tag.yml`: - - 改为纯 `workflow_dispatch` 手动入口 - - 增加 `release_mode=preview|release` 输入 - - `preview` 只跑 dry-run 并输出 `last_tag`、`next_version`、`next_tag` - - `release` 由维护者手动触发真实打 tag,并要求 `PAT_TOKEN` -- 收紧 `AGENTS.md` 的提交信息约束,明确提交类型必须反映 release semantics,纯文档变更禁止写成 `feat(...)` -- 按用户反馈继续调整发版入口: - - 删除基于 `workflow_run` 的自动发版路径 - - 统一改为 `workflow_dispatch` 手动触发 - - 先在同一次 run 中执行 `preview` - - 再通过 `release-approval` environment 做人工确认 - - 审批通过后继续同一 SHA 的真实 `release` -- 复验最小构建命令: - - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` - - 结果:通过,`0 warning / 0 error` -- 完成最小构建验证: - - `dotnet build GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj -c Release -p:RestoreFallbackFolders=` - - 结果:通过,`0 warning / 0 error` -- 直接在当前工作树执行 `semantic-release --dry-run` 时命中本地 tag 抓取冲突: - - `git fetch --tags ... would clobber existing tag` - - 结论:当前工作树不适合作为 dry-run 验证环境 -- 改用干净临时克隆 `/tmp/gframework-semrel-dryrun` 再跑 dry-run: - - 成功识别 `v0.0.222` 为最新 release - - 成功分析 `269` 个提交 - - 按当前规则得出下一次应为 `minor` 发布,预期版本窗口从 `0.0.222` 提升到 `0.1.0` + - `breaking -> major` +- 当前 workflow 加固: + - `release` 额外要求 `needs.preview.result == 'success'` + - `PAT_TOKEN` 在真实 release 前通过 GitHub API 做存活性校验 + - preview / release summary 会展示 snapshot 语义与生成的 release notes + +### 本轮关键决策 + +- 保留 `@semantic-release/release-notes-generator`,但不再让它白跑: + - 继续生成 notes + - 将 notes 写入 GitHub Actions summary +- 不保留已废弃的 `release_mode=preview|release` 中间方案: + - active trace 只保留当前有效链路 + - 历史演进以 tracking 文档的已完成项为准 + +### 验证结论 + +1. `npx --yes -p semantic-release -p conventional-changelog-conventionalcommits@9.1.0 semantic-release --dry-run --no-ci` + - 已确认新 preset 包可加载,`commit-analyzer` 与 `release-notes-generator` 正常初始化 + - 本次 dry-run 未继续出版本,因为干净克隆的 `main` 已落后远端 +2. `dotnet build GFramework.sln -c Release` + - 通过,`639 warning / 0 error` + - warning 为仓库既有基线,本轮未新增关联 warning ### 下一步 -1. 复核变更 diff 并创建提交 -2. 向用户说明新的发版链路与可优化点 +1. 复查当前 PR 的 open review threads 是否只剩等待 push 的已修复项 +2. 创建提交并推送当前分支