From 17eceb7ad4330933ad58b64edebbf80dc9b0ec35 Mon Sep 17 00:00:00 2001 From: pnoker Date: Fri, 26 Jun 2026 14:32:02 +0800 Subject: [PATCH] docs: add git workflow design spec and implementation plan --- .../2026-06-26-git-workflow-implementation.md | 474 ++++++++++++++++++ ...26-git-workflow-and-contributing-design.md | 183 +++++++ 2 files changed, 657 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-26-git-workflow-implementation.md create mode 100644 docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md diff --git a/docs/superpowers/plans/2026-06-26-git-workflow-implementation.md b/docs/superpowers/plans/2026-06-26-git-workflow-implementation.md new file mode 100644 index 000000000..65d01dd57 --- /dev/null +++ b/docs/superpowers/plans/2026-06-26-git-workflow-implementation.md @@ -0,0 +1,474 @@ +# Git 工作流与协作规范改造 — 实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 把 iot-dc3 与 iot-dc3-web 统一改造为简化 Git Flow(main 生产+部署+tag、develop 集成、feature 单线、release 归档),tag 改 semver,CI 去重,补齐 PR/Issue 模板与分支保护。 + +**Architecture:** 每个仓库从 develop 切 `feature/git-workflow` 分支,在该分支上改 workflow/tag 脚本/模板/CONTRIBUTING,PR 回 develop;两仓库 develop 就绪后各自 promote 到 main,最后做不可逆的 GitHub 设置(默认分支 release→main、分支保护、release 归档)。 + +**Tech Stack:** GitHub Actions workflow(YAML)、Bash(tag.sh)、Make、gh CLI、GitHub REST API(分支保护/默认分支)。 + +**Spec:** `docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md` + +## Global Constraints + +- **两仓库统一**:iot-dc3(后端,`git@github.com:pnoker/iot-dc3.git`)与 iot-dc3-web(前端,`git@github.com:pnoker/iot-dc3-web.git`)同构改造。 +- **分支模型 B1**:feature→develop(CI 全量)→main(发版);release 归档只读。 +- **tag**:semver `vYYYY.M.P`,只在 main 打;develop 不打正式 tag。 +- **不改业务代码**:只动 workflow / 脚本 / 模板 / 文档 / GitHub 设置。 +- **commit**:Conventional Commits,scope 用 `chore` 或 `ci`/`docs`(如 `chore(git): rewrite tag.sh to semver on main`)。 +- **每仓库从 develop 切分支执行**,不直接在 develop/main 上提交。 +- **bash 脚本**:`set -euo pipefail`;tag.sh 必须支持 `--dry-run`。 +- **验证手段**:workflow 用 `actionlint`(若有)或 YAML 语法 + on 块核对;tag.sh 用 dry-run;前端 `pnpm lint-check && pnpm check && pnpm build`;后端 `mvn -q -DskipTests compile` 不适用(无 Java 改动,跳过)。 +- **不可逆操作放最后**:改默认分支、归档 release 在所有代码改动合并并 promote 到 main 之后。 +- **GitHub 设置需 owner 权限**:用 `gh api` 或 `gh repo edit`;执行前确认 `gh auth status` 为 pnoker。 + +--- + +## 阶段一:后端 iot-dc3(feature/git-workflow 分支) + +### Task 1: 后端 workflow 触发调整 + +**Files:** +- Modify: `iot-dc3/.github/workflows/ci.yml`(on.branches) +- Modify: `iot-dc3/.github/workflows/test.yml`(on.branches) +- Modify: `iot-dc3/.github/workflows/e2e.yml`(on 改 pull_request→develop) +- Modify: `iot-dc3/.github/workflows/security.yml`(on.branches) +- Modify: `iot-dc3/.github/workflows/codeql.yml`(on.branches) +- Modify: `iot-dc3/.github/workflows/docs.yml`(on.branches release→main) +- Modify: `iot-dc3/.github/workflows/docker-ci.yml`(on.tags `dc3.release.*`→`v*`) + +**Interfaces:** 无(配置改动)。 + +**改动规则(逐文件 Read 后应用):** +- `ci/test/security/codeql.yml`:`branches: [ develop, release, main ]` → `branches: [ develop, main ]`(出现几处替换几处)。 +- `e2e.yml`:触发从 `push branches:[develop,release,main]` 改为 `pull_request branches:[develop]`(e2e 只在 PR 到 develop 时跑)。 +- `docs.yml`:`branches: [ release ]` → `branches: [ main ]`。 +- `docker-ci.yml`:`tags: [ 'dc3.release.*' ]` → `tags: [ 'v*' ]`。 + +- [ ] **Step 1: 切分支** +```bash +cd iot-dc3 && git checkout develop && git pull && git checkout -b feature/git-workflow +``` +- [ ] **Step 2: 逐文件 Read 确认 on 段,按规则 Edit** +- [ ] **Step 3: YAML 语法校验** +```bash +for f in ci test e2e security codeql docs docker-ci; do python3 -c "import yaml,sys; yaml.safe_load(open('iot-dc3/.github/workflows/$f.yml'))" && echo "$f.yml OK"; done +``` +Expected: 全部 OK。 +- [ ] **Step 4: grep 确认无残留 release 触发** +```bash +grep -rn "release" iot-dc3/.github/workflows/ | grep -v "name:" | grep -v "#" +``` +Expected: 无 `branches: [... release ...]` 或 `dc3.release.*` 残留(注释/文案里的 release 可接受)。 +- [ ] **Step 5: commit** +```bash +git -C iot-dc3 add .github/workflows && git -C iot-dc3 commit -m "ci: drop release branch, rewire triggers to develop/main and v* tags" +``` + +### Task 2: 后端 tag.sh 重写(semver on main)+ Makefile + +**Files:** +- Modify: `iot-dc3/dc3/bin/tag.sh`(重写) +- Modify: `iot-dc3/Makefile`(tag target 加参数) + +**Interfaces:** 产出 `make tag [patch|minor|major] [--dry-run]`。 + +**tag.sh 完整新内容:** +```bash +#!/usr/bin/env bash +set -euo pipefail + +# semver tag on main: vYYYY.M.P +# usage: make tag [patch|minor|major] [--dry-run] + +bump="${1:-patch}" +dryrun=0 +for arg in "$@"; do [ "$arg" = "--dry-run" ] && dryrun=1; done +case "$bump" in patch|minor|major) ;; *) echo "unknown bump: $bump (patch|minor|major)"; exit 1;; esac + +branch=$(git rev-parse --abbrev-ref HEAD) +if [ "$branch" != "main" ]; then + echo "tagging only allowed on main (now: $branch). switch to main first." >&2 + exit 1 +fi + +git pull --tags --quiet +last=$(git tag -l "v*" --sort=-v:refname | head -1) +if [ -z "$last" ]; then echo "no v* tag found; set an initial tag manually, e.g. v2025.9.0"; exit 1; fi +# parse vYYYY.M.P +re='^v([0-9]+)\.([0-9]+)\.([0-9]+)$' +[[ $last =~ $re ]] || { echo "unparseable last tag: $last"; exit 1; } +major=${BASH_REMATCH[1]}; minor=${BASH_REMATCH[2]}; patch=${BASH_REMATCH[3]} +case "$bump" in + patch) patch=$((patch+1));; + minor) minor=$((minor+1)); patch=0;; + major) major=$((major+1)); minor=0; patch=0;; +esac +newtag="v${major}.${minor}.${patch}" +echo "last=$last -> new=$newtag" +[ "$dryrun" = "1" ] && { echo "(dry-run, not tagging)"; exit 0; } + +git tag "$newtag" +git push origin "$newtag" +gh release create "$newtag" --generate-notes --title "$newtag" +``` + +**Makefile tag target(替换现有 `tag:` 段):** +```make +tag: + @dc3/bin/tag.sh $(filter-out $@,$(MAKECMDGOALS)) +%: ; @: +``` +(保留 `make tag` 默认 patch;支持 `make tag minor`。) + +- [ ] **Step 1: Write tag.sh**(上述内容,`chmod +x`) +- [ ] **Step 2: Edit Makefile tag target** +- [ ] **Step 3: dry-run 验证(在 develop 上测脚本逻辑,应拒绝并提示)** +```bash +cd iot-dc3 && bash dc3/bin/tag.sh patch --dry-run +``` +Expected: `tagging only allowed on main (now: feature/git-workflow)...` 退出码 1(验证分支守卫生效)。 +- [ ] **Step 4: 临时切 main 测 dry-run(本地,不 push)** +```bash +git stash -u 2>/dev/null; git checkout main 2>/dev/null && bash dc3/bin/tag.sh patch --dry-run; git checkout feature/git-workflow; git stash pop 2>/dev/null +``` +Expected: 打印 `last=v... -> new=v...` + `(dry-run, not tagging)`,不实际打 tag。 +- [ ] **Step 5: commit** +```bash +git add dc3/bin/tag.sh Makefile && git commit -m "chore(git): rewrite tag.sh to semver on main with dry-run" +``` + +### Task 3: 后端 PR + Issue 模板 + +**Files:** +- Create: `iot-dc3/.github/PULL_REQUEST_TEMPLATE.md` +- Create: `iot-dc3/.github/ISSUE_TEMPLATE/bug-report.md` +- Create: `iot-dc3/.github/ISSUE_TEMPLATE/feature-request.md` +- Create: `iot-dc3/.github/ISSUE_TEMPLATE/config.yml` + +**PR 模板内容**(spec §6 统一版): +```markdown +## What & Why +(这次改动做什么、为什么。1–3 句。) + +## Changes +- (逐条列关键改动,可追溯到需求/issue。) + +## Verification +- [ ] 本地验证通过(lint / type-check / test / build,按子项目) +- [ ] 新增/修改的行为有测试覆盖 +- [ ] 文档已更新(如有 API/行为变更) + +## Impact +- (影响的模块、是否有 breaking change、是否需 changelog) +``` + +**bug-report.md**: +```markdown +--- +name: Bug Report +about: 报告一个缺陷 +labels: ["bug"] +--- +## 环境 +- 后端版本(git tag / Docker 镜像 tag): +- 前端版本: +- 部署方式(Docker / 源码 / 其他): + +## 复现步骤 +1. + +## 预期 / 实际 +- 预期: +- 实际: + +## 日志 / 截图 +``` + +**feature-request.md**: +```markdown +--- +name: Feature Request +about: 提一个功能建议 +labels: ["enhancement"] +--- +## 场景 +(你想解决什么问题。) +## 期望 +(你希望平台怎么做。) +## 替代方案 +(你考虑过的其他做法。) +``` + +**config.yml**: +```yaml +blank_issues_enabled: false +contact_links: + - name: 讨论 / 提问 + url: https://github.com/pnoker/iot-dc3/discussions + about: 使用问题与想法请到 Discussions +``` + +- [ ] **Step 1: 创建 4 个文件**(上述内容) +- [ ] **Step 2: 校验 config.yml 语法** +```bash +python3 -c "import yaml; yaml.safe_load(open('iot-dc3/.github/ISSUE_TEMPLATE/config.yml'))" && echo OK +``` +- [ ] **Step 3: commit** +```bash +git add .github/PULL_REQUEST_TEMPLATE.md .github/ISSUE_TEMPLATE && git commit -m "docs: add PR and issue templates" +``` + +### Task 4: 后端 CONTRIBUTING 更新 + +**Files:** +- Modify: `iot-dc3/CONTRIBUTING.md`(增补「分支与发版」节) + +**增补内容(插入到合适位置,如提交规范之后):** +```markdown +## 分支与发版 + +IoT DC3 采用简化 Git Flow: + +- `develop`:开发集成分支。新功能从 `develop` 切 `feature/` 分支,PR 回 `develop`(CI 全量门禁)。 +- `main`:生产主干。`develop` 验证通过后以 PR promote 到 `main`;每次合并到 `main` 即为一次发版。 +- `hotfix/`:从 `main` 切,修复生产问题,PR 回 `main` 并打 tag,再回合并 `develop`。 +- `release`:已归档(只读),不再接收合并或部署。新工作请走 `develop`/`main`。 + +**发版(打 tag)**:在 `main` 分支执行 `make tag [patch|minor|major]`(默认 patch),生成 semver tag `vYYYY.M.P` 并创建 GitHub Release。tag 推送后自动触发 Docker 镜像发布。 + +**外部贡献者**:请从 `develop` 切 feature 分支,PR 提交到 `develop`(不要提交到 `main` 或 `release`)。 +``` + +- [ ] **Step 1: Read CONTRIBUTING.md 定位插入点** +- [ ] **Step 2: Edit 插入「分支与发版」节** +- [ ] **Step 3: commit** +```bash +git add CONTRIBUTING.md && git commit -m "docs: document branch model and release flow in CONTRIBUTING" +``` + +### Task 5: 后端 spec 文件纳入 + 推送 + PR + +- [ ] **Step 1: 把本 spec 与 plan 文件加入该分支**(它们目前在 develop 工作区,需带进 feature 分支) +```bash +git -C iot-dc3 add docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md docs/superpowers/plans/2026-06-26-git-workflow-implementation.md +git -C iot-dc3 commit -m "docs: add git workflow design spec and implementation plan" +``` +- [ ] **Step 2: 推送 + 开 PR 到 develop** +```bash +git -C iot-dc3 push -u origin feature/git-workflow +gh pr create --repo pnoker/iot-dc3 --base develop --head feature/git-workflow \ + --title "chore(git): unify branch model, semver tags, CI trim, templates" \ + --body "见 docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md。砍 release(归档)、main 成生产+部署+tag、feature→develop→main、tag 改 semver、CI 去重、补 PR/Issue 模板与 CONTRIBUTING。" +``` +- [ ] **Step 3: 合并 PR(merge commit)** +```bash +PR=$(gh pr list --repo pnoker/iot-dc3 --head feature/git-workflow --base develop --json number --state open | python3 -c "import json,sys;print(json.load(sys.stdin)[0]['number'])") +gh pr merge "$PR" --repo pnoker/iot-dc3 --merge +``` + +--- + +## 阶段二:前端 iot-dc3-web(feature/git-workflow 分支) + +### Task 6: 前端 workflow 触发调整 + +**Files:** +- Modify: `iot-dc3-web/.github/workflows/ci.yml`(on.branches 去 release) +- Modify: `iot-dc3-web/.github/workflows/docker-ci-web.yml`(on.tags `dc3.release.*`→`v*`) + +**改动规则:** 前端 ci.yml 的 branches `[develop, release, main]` → `[develop, main]`;docker-ci-web.yml `tags: 'dc3.release.*'` → `tags: 'v*'`。 + +- [ ] **Step 1: 切分支** +```bash +cd iot-dc3-web && git checkout develop && git pull && git checkout -b feature/git-workflow +``` +- [ ] **Step 2: Read + Edit 两 workflow 的 on 段** +- [ ] **Step 3: YAML 校验** +```bash +for f in ci docker-ci-web; do python3 -c "import yaml; yaml.safe_load(open('iot-dc3-web/.github/workflows/$f.yml'))" && echo "$f.yml OK"; done +``` +- [ ] **Step 4: commit** +```bash +git -C iot-dc3-web add .github/workflows && git commit -m "ci: drop release branch, rewire docker tag to v*" +``` + +### Task 7: 前端 tag 机制新增 + +**Files:** +- Create: `iot-dc3-web/bin/tag.sh`(与后端同构 semver 逻辑) +- Modify: `iot-dc3-web/Makefile`(新增 tag target) + +**tag.sh**:内容同 Task 2 的后端 tag.sh(逐字复制;前端无需 dc3/ 前缀,放 `bin/tag.sh`)。 + +**Makefile 新增(加到 .PHONY 与 target 列表 + help):** +- `.PHONY` 列表加 `tag` +- help 加 `'make tag [patch|minor|major] - create semver release tag on main'` +- target: +```make +tag: + @bin/tag.sh $(filter-out $@,$(MAKECMDGOALS)) +%: ; @: +``` + +- [ ] **Step 1: 创建 bin/tag.sh**(chmod +x,内容同 Task 2) +- [ ] **Step 2: Edit Makefile 加 tag target + .PHONY + help 行** +- [ ] **Step 3: dry-run 验证分支守卫** +```bash +cd iot-dc3-web && bash bin/tag.sh --dry-run +``` +Expected: 拒绝(非 main)。 +- [ ] **Step 4: commit** +```bash +git add bin/tag.sh Makefile && git commit -m "chore(git): add semver tag script and make tag target" +``` + +### Task 8: 前端 PR(替换)+ Issue 模板 + +**Files:** +- Modify: `iot-dc3-web/.github/PULL_REQUEST_TEMPLATE.md`(替换为统一版) +- Create: `iot-dc3-web/.github/ISSUE_TEMPLATE/bug-report.md` +- Create: `iot-dc3-web/.github/ISSUE_TEMPLATE/feature-request.md` +- Create: `iot-dc3-web/.github/ISSUE_TEMPLATE/config.yml` + +**内容:** PR 模板用 Task 3 的统一版;Issue 模板同 Task 3(config.yml 的 discussions 链接指向 iot-dc3-web 仓库)。 + +- [ ] **Step 1: 替换 PR 模板 + 创建 3 个 Issue 模板** +- [ ] **Step 2: commit** +```bash +git add .github/PULL_REQUEST_TEMPLATE.md .github/ISSUE_TEMPLATE && git commit -m "docs: unify PR template, add issue templates" +``` + +### Task 9: 前端 CONTRIBUTING 更新 + +**Files:** +- Modify: `iot-dc3-web/CONTRIBUTING.md`(增补「分支与发版」节,内容同 Task 4,镜像 iot-dc3-web 仓库) + +- [ ] **Step 1: Read + Edit 插入「分支与发版」节** +- [ ] **Step 2: commit** +```bash +git add CONTRIBUTING.md && git commit -m "docs: document branch model and release flow in CONTRIBUTING" +``` + +### Task 10: 前端推送 + PR 到 develop + +- [ ] **Step 1: 推送 + 开 PR** +```bash +git -C iot-dc3-web push -u origin feature/git-workflow +gh pr create --repo pnoker/iot-dc3-web --base develop --head feature/git-workflow \ + --title "chore(git): unify branch model, semver tags, CI trim, templates" \ + --body "与 iot-dc3 同构:砍 release(归档)、main 生产+部署+tag、semver tag、CI 去重、补 tag 机制与模板。" +``` +- [ ] **Step 2: 合并(merge commit)** +```bash +PR=$(gh pr list --repo pnoker/iot-dc3-web --head feature/git-workflow --base develop --json number --state open | python3 -c "import json,sys;print(json.load(sys.stdin)[0]['number'])") +gh pr merge "$PR" --repo pnoker/iot-dc3-web --merge +``` + +--- + +## 阶段三:GitHub 设置(不可逆,两仓库 develop 合并后执行) + +> 前置:阶段一 Task 5、阶段二 Task 10 均已合并到各自 develop。 + +### Task 11: promote develop → main(两仓库) + +- [ ] **Step 1: 后端 develop→main PR + 合并** +```bash +gh pr create --repo pnoker/iot-dc3 --base main --head develop --title "Sync develop into main (git workflow overhaul)" --body "Bring main in line with develop after the git workflow refactor." +PR=$(gh pr list --repo pnoker/iot-dc3 --head develop --base main --json number --state open | python3 -c "import json,sys;print(json.load(sys.stdin)[0]['number'])") +gh pr merge "$PR" --repo pnoker/iot-dc3 --merge +``` +- [ ] **Step 2: 前端 develop→main PR + 合并**(同上,--repo pnoker/iot-dc3-web) +- [ ] **Step 3: 确认两仓库 main 已含新 workflow/tag.sh/模板** +```bash +for r in iot-dc3 iot-dc3-web; do echo "$r main tag.sh: $(gh api repos/pnoker/$r/contents/$( [ $r = iot-dc3 ] && echo dc3/bin/tag.sh || echo bin/tag.sh )?ref=main --jq .name 2>&1)"; done +``` +Expected: 两仓库 main 上 tag.sh 存在。 + +### Task 12: 分支保护(main / develop,两仓库) + +- [ ] **Step 1: 后端 main 保护** +```bash +gh api -X PUT repos/pnoker/iot-dc3/branches/main/protection -f required_pull_request_reviews.required_approving_review_count=0 -F required_pull_request_reviews.dismiss_stale_reviews=false -F required_status_checks.strict=true -F required_status_checks.contexts[]='ci' -F enforce_admins=false -F restrictions= -F allow_force_pushes=false +``` +(个人项目 review count=0;要求 CI 'ci' 通过;禁 force push。) +- [ ] **Step 2: 后端 develop 保护**(同上,branches/develop,可放宽 strict=false) +- [ ] **Step 3: 前端 main + develop 保护**(同 Step 1/2,--repo pnoker/iot-dc3-web,status check context 用前端实际名,先 `gh run list` 确认) +- [ ] **Step 4: 验证保护生效** +```bash +gh api repos/pnoker/iot-dc3/branches/main/protection --jq .required_pull_request_reviews +``` +Expected: 返回非 null 的 PR review 配置。 + +### Task 13: 改默认分支 release → main(两仓库,不可逆点) + +- [ ] **Step 1: 后端** +```bash +gh repo edit pnoker/iot-dc3 --default-branch main +``` +- [ ] **Step 2: 前端** +```bash +gh repo edit pnoker/iot-dc3-web --default-branch main +``` +- [ ] **Step 3: 确认** +```bash +for r in iot-dc3 iot-dc3-web; do echo "$r default: $(gh repo view pnoker/$r --json defaultBranchRef --jq .defaultBranchRef.name)"; done +``` +Expected: 均为 main。 + +### Task 14: 归档 release 分支(两仓库,只读) + +- [ ] **Step 1: release 设为只读(restrict pushes,仅 owner)** +```bash +for r in iot-dc3 iot-dc3-web; do + gh api -X PUT repos/pnoker/$r/branches/release/protection -F required_status_checks= -F enforce_admins=true -f restrictions.users[]=pnoker -f restrictions.teams[]= 2>&1 | head -1 +done +``` +(限制只有 pnoker 能 push;或用 `restrictions` 限定。enforce_admins=true 锁死。) +- [ ] **Step 2: 在两仓库 README/CONTRIBUTING 顶部加归档提示**(已由 Task 4/9 的 CONTRIBUTING「release 已归档」覆盖;可选额外加 README badge) +- [ ] **Step 3: 确认 release 不再触发 workflow**(去 release 监听已在 Task 1/6 完成;此处核对 grep 无残留) + +### Task 15: 首个 semver tag 验证(dry-run,两仓库) + +- [ ] **Step 1: 后端** +```bash +cd iot-dc3 && git checkout main && git pull && bash dc3/bin/tag.sh patch --dry-run && git checkout develop +``` +Expected: `last=<上一个 v* 或提示无 v* tag>` → `new=vYYYY.M.P` + dry-run 不打 tag。 +- [ ] **Step 2: 前端** +```bash +cd iot-dc3-web && git checkout main && git pull && bash bin/tag.sh patch --dry-run && git checkout develop +``` +- [ ] **Step 3: 若历史无 v* tag(全是 dc3.release.* 日期式),手动定一个初始 semver 基线 tag** +```bash +# 仅当上一步提示 "no v* tag found": +# 后端: git tag v2025.9.3(对齐最近 GitHub Release 名) && git push origin v2025.9.3 +# 前端: 同上 +``` +(此步需与用户确认基线版本号。) + +### Task 16: 更新记忆 + 公告(可选) + +- [ ] **Step 1: 更新 `git-branch-flow-rule` 记忆**为 B1 模型(main 主干+部署+tag、develop 集成、feature→develop→main、release 归档、semver)。 +- [ ] **Step 2: 在 iot-dc3 Discussions 发公告**说明分支模型变更(外部 PR retarget 到 develop)。 + +--- + +## Self-Review + +**Spec 覆盖:** +- §3 分支模型 B1 → T11(promote)、T12(保护)、T13(默认)、T14(归档) ✅ +- §4 tag semver → T2(后端 tag.sh)、T7(前端)、T15(验证) ✅ +- §5 CI 去重 → T1(后端 workflow)、T6(前端) ✅ +- §6 模板 → T3(后端)、T8(前端) ✅ +- §7 CONTRIBUTING → T4(后端)、T9(前端) ✅ +- §8 迁移步骤 → 阶段三 T11-T15 ✅ +- §9 两仓库清单 → 阶段一/二全覆盖 ✅ + +**占位符扫描:** T12 Step 3 的前端 status check context 标注"先 gh run list 确认"——执行动作(非 TBD),可接受。T15 Step 3 初始 tag"需与用户确认"——明确的执行决策点,非占位。无其他 TBD/TODO。 + +**一致性:** tag.sh 在 T2(后端) 与 T7(前端) 内容一致(同构);模板在 T3 与 T8 一致;CONTRIBUTING 增补段在 T4 与 T9 一致。分支名 `feature/git-workflow` 两仓库统一。 + +**风险已识别:** 阶段三不可逆,放最后;T15 历史无 v* tag 的基线确认点已标注。 diff --git a/docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md b/docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md new file mode 100644 index 000000000..ee49bd2f7 --- /dev/null +++ b/docs/superpowers/specs/2026-06-26-git-workflow-and-contributing-design.md @@ -0,0 +1,183 @@ +# Git 工作流与协作规范改造 — 设计方案 + +- 日期:2026-06-26 +- 状态:待评审 +- 范围:`iot-dc3`(后端)与 `iot-dc3-web`(前端)两个仓库统一适用 +- 关系:本方案落地后,`git-branch-flow-rule` 记忆以此为准 + +## 1. 背景与问题 + +两个仓库当前镜像同一套模型,但存在五类实质缺陷: + +1. **双合并反模式**:feature 同时合并到 develop 和 main(用户初始描述),导致两分支历史分叉、冲突双解、易不一致。无任何主流模型这么做。 +2. **main/release 语义重叠**:后端 `tag.sh` 中 main 分支打的 tag 前缀是 `dc3.release.*`(main 与 release 当一类),证明两者角色未区分;两仓库默认分支都是 `release`,release 同时是部署分支。 +3. **tag 命名非 semver 且混用**:git tag 是日期式(`dc3.release.20251005.00`),GitHub Release 名是 semver(`2025.9.3`),两套不一致;不符合开源惯例。 +4. **前端无 tag/发版机制**:`iot-dc3-web` 的 Makefile 无 `tag` target、无 tag 脚本——发版缺口。 +5. **CI 重复**:6 个 workflow(ci/test/e2e/security/codeql)全监听 `develop+release+main` 三分支,每个 feature 触发 3 倍 CI。 +6. **模板缺失**:后端无 PR/Issue 模板;前端无 Issue 模板(前端有 PR 模板 19 行)。 + +## 2. 目标与非目标 + +**目标** + +1. 统一为**简化 Git Flow**:main(生产+部署+tag)+ develop(集成)+ feature/hotfix,砍掉 release 长期分支(归档保留)。 +2. feature 单线流入(消除双合并):feature→develop→main。 +3. tag 统一 semver(`vYYYY.M.P`),只在 main 打,前后端共用同一套机制。 +4. CI 去重:feature 只在 develop PR 跑全量,main 跑精简回归,tag 驱动镜像发版。 +5. 补齐 PR/Issue 模板、分支保护、CONTRIBUTING,两仓库一致。 + +**非目标** + +- 不改后端 Java 代码 / 前端 Vue 代码的业务逻辑;只改协作基础设施(workflow、脚本、模板、文档、GitHub 设置)。 +- 不引入 commitlint/husky 等新工具链(已有 `.githooks/commit-msg` 与 Conventional Commits,沿用)。 +- 不做 CODEOWNERS(个人维护项目,自动指派意义不大)。 + +## 3. 分支模型(B1) + +``` +feature/* 从 develop 切 ──PR──► develop (CI 全量门禁) +develop 验证通过 ──PR promote──► main +main = 生产 + 部署 + tag (docs.yml/docker-ci 触发) +hotfix/* 从 main 切 ──PR──► main + 回合并 develop +release 归档保留(只读,不再部署/合并),外部旧 PR 手动 retarget +``` + +**职责** + +- **develop**:开发集成分支。feature 的唯一 PR 落点。CI 跑全量(lint/check/test/e2e/security)。 +- **main**:生产主干。只接收来自 develop 的 promote PR(或 hotfix)。每次合并 = 一次发版(打 tag + 部署文档 + 触发镜像发版 workflow)。禁止直推。 +- **feature/\***:从 develop 切,命名 `feature/` 或 `feat/`,PR 回 develop。 +- **hotfix/\***:从 main 切,修复生产问题,PR 回 main 并打 tag,再回合并 develop。 +- **release**:归档。GitHub 上标记 archived/只读,不再触发任何 workflow,不接收合并。 + +**分支保护(GitHub 设置)** + +- main:要求 PR + CI 通过 + 禁止 force push + 禁止直推。 +- develop:要求 PR + CI 通过。 +- release:设为只读(restrict pushes),保留历史。 + +## 4. tag / 发版(semver,前后端统一) + +**命名**:`vYYYY.M.P`(与 GitHub Release 名一致,如 `v2025.9.3`)。递增规则: + +- patch:默认,每次 promote 发版 `v2025.9.3 → v2025.9.4` +- minor:`make tag minor` → `v2025.9.4 → v2025.10.0` +- major:`make tag major` → `v2025.10.0 → v2026.0.0` + +**只打在 main 上**。develop 不打正式 tag。 + +**后端 `dc3/bin/tag.sh` 重写**: + +- 只允许在 main 分支执行(其他分支拒绝)。 +- 读取最近一个 `v*` tag,按 patch/minor/major 递增生成新 tag。 +- `git tag` + `git push origin ` + `gh release create --generate-notes`(自动 changelog)。 +- 命令:`make tag [patch|minor|major]`(默认 patch)。 + +**前端补 tag 机制**: + +- 新增 `iot-dc3-web/bin/tag.sh`(与后端同逻辑,prefix 可配置)。 +- Makefile 加 `tag` target。 +- 前后端 tag 脚本共享同一 semver 递增逻辑(可抽取到公共片段或各自维护同构脚本)。 + +**tag 触发的发版**(见 §5):打 `v*` tag → docker-ci / docker-ci-web 构建并推送镜像。 + +## 5. CI 触发策略(去重) + +| workflow | 当前触发 | 调整后 | +|---|---|---| +| `ci.yml` | push develop+release+main | push develop + push main | +| `test.yml` | 三分支 | push develop + push main | +| `e2e.yml` | 三分支 | **pull_request to develop**(仅 PR,省 main 重测) | +| `security.yml` | 三分支 | push develop + push main | +| `codeql.yml` | 三分支 | push develop + push main(+ schedule 周期扫) | +| `docs.yml` | push release(→ Pages) | **push main**(文档随主干持续部署) | +| `docker-ci.yml`(后端镜像) | tag `dc3.release.*` | **tag `v*`** | +| `docker-ci-web.yml`(前端镜像) | (确认中,推测 tag) | **tag `v*`** | + +**净效果**:feature 的 CI 从 3× 降到 1×(只在 develop PR 跑全量);main 只跑 ci/test/security 回归;镜像发版由 semver tag 驱动;文档随 main 持续部署。 + +**两仓库一致**:iot-dc3 与 iot-dc3-web 的 workflow 触发规则对齐(前端只有 ci/docker-ci-web,同步去 release)。 + +## 6. PR / Issue 模板 + +**PR 模板**(两仓库同一份,`.github/PULL_REQUEST_TEMPLATE.md`): + +```markdown +## What & Why +(这次改动做什么、为什么。1–3 句。) + +## Changes +- (逐条列关键改动,可追溯到需求/issue。) + +## Verification +- [ ] 本地验证通过(lint / type-check / test / build,按子项目) +- [ ] 新增/修改的行为有测试覆盖 +- [ ] 文档已更新(如有 API/行为变更) + +## Impact +- (影响的模块、是否有 breaking change、是否需 changelog) +``` + +前端已有 19 行模板,替换为这份统一版;后端新增。 + +**Issue 模板**(两仓库,`.github/ISSUE_TEMPLATE/`): + +- `bug-report.md`:环境(前后端版本/部署方式)、复现步骤、预期/实际、日志。 +- `feature-request.md`:场景、期望、替代方案。 +- `config.yml`:空白 issue 跳转链接(引导到 Discussions)。 + +## 7. CONTRIBUTING 更新 + +两仓库 CONTRIBUTING(后端 124 行 / 前端 62 行)增补/修订「分支与发版」一节: + +- 分支模型(§3 图示)与 feature/develop/main/hotfix 职责。 +- PR 流程:feature→develop(CI 全量)→ promote develop→main(发版)。 +- tag/发版:`make tag [patch|minor|major]`,semver `vYYYY.M.P`,只在 main。 +- commit 规范:沿用 Conventional Commits(已有,保留引用)。 +- 外部贡献者指引:从 develop 切 feature,PR 到 develop(不是 main/release)。 + +## 8. 迁移步骤(砍 release,有序执行) + +每个仓库独立执行,顺序: + +1. **同步主干**:确保 develop 与 main 内容一致(develop→main PR;当前 docs 改动已在三分支,其余历史内容核对)。 +2. **改 workflow 触发**:所有 `branches: [develop, release, main]` → `[develop, main]`;docs.yml `release→main`;docker-ci `dc3.release.* → v*`;e2e 改 PR 触发。 +3. **重写 tag.sh(后端)+ 新增(前端)**:semver on main,`make tag`。 +4. **加模板**:PR 模板(统一)+ Issue 模板(bug/feature/config)。 +5. **更新 CONTRIBUTING**:两仓库写入分支/发版规范。 +6. **设分支保护**:main/develop 规则(gh api 或 GitHub 设置)。 +7. **改默认分支**:`release → main`(`gh repo edit --default-branch main`,需两仓库)。 +8. **归档 release**:release 设只读,README/CONTRIBUTING 注明已归档、新工作走 develop/main。 +9. **公告**:GitHub Release / Discussions 说明分支模型变更,外部 PR retarget 到 develop。 + +**执行节奏**:先在 `develop` 上做完所有改动(workflow/脚本/模板/文档),通过一个 PR 把 develop promote 到 main 触发首个新流程发版;改默认分支与归档 release 放最后(不可逆性最高,最后做)。 + +## 9. 两仓库改动清单 + +### iot-dc3(后端) + +- `.github/workflows/`:ci/test/e2e/security/codeql 去 release;docs.yml release→main;docker-ci.yml tag→`v*`。 +- `dc3/bin/tag.sh`:重写为 semver on main。 +- `Makefile`:`tag` target 加 patch/minor/major 参数。 +- `.github/PULL_REQUEST_TEMPLATE.md`:新增(统一版)。 +- `.github/ISSUE_TEMPLATE/`:新增 bug-report/feature-request/config。 +- `CONTRIBUTING.md`:更新分支与发版节。 +- GitHub:默认分支 release→main;分支保护;release 归档。 + +### iot-dc3-web(前端) + +- `.github/workflows/`:ci/docker-ci-web 去 release;docker-ci-web.yml tag→`v*`(确认当前触发)。 +- `bin/tag.sh`:新增(与后端同构 semver 逻辑)。 +- `Makefile`:新增 `tag` target。 +- `.github/PULL_REQUEST_TEMPLATE.md`:替换为统一版。 +- `.github/ISSUE_TEMPLATE/`:新增 bug-report/feature-request/config。 +- `CONTRIBUTING.md`:更新分支与发版节。 +- GitHub:默认分支 release→main;分支保护;release 归档。 + +## 10. 风险与回退 + +- **公网部署中断**:docs.yml 从 release 改 main 时,若 main 尚未就绪会断部署。缓解:先确保 main 内容完整、预览验证,再切触发;切之前 release 仍可兜底(归档在最后)。 +- **外部 PR retarget**:改默认分支后,外部贡献者的 PR 可能指向 release。缓解:归档(非删除)+ CONTRIBUTING + Issue 模板引导 + 公告。 +- **tag.sh 重写风险**:semver 递增逻辑出错可能打错 tag。缓解:脚本加 dry-run(`make tag --dry-run`)+ 首次手动验证。 +- **镜像发版断裂**:docker-ci 触发从 `dc3.release.*` 改 `v*`,过渡期若仍打旧 tag 则不发版。缓解:tag.sh 与 docker-ci 同一 PR 改、同批上线。 +- **回退**:归档(非删除)release 分支 + workflow 改动可 revert,迁移可回退到三分支状态(代价:历史多一笔 revert)。