docs: add git workflow design spec and implementation plan

This commit is contained in:
pnoker
2026-06-26 14:32:02 +08:00
parent e89f4cc1d2
commit 17eceb7ad4
2 changed files with 657 additions and 0 deletions
@@ -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 Flowmain 生产+部署+tag、develop 集成、feature 单线、release 归档),tag 改 semverCI 去重,补齐 PR/Issue 模板与分支保护。
**Architecture:** 每个仓库从 develop 切 `feature/git-workflow` 分支,在该分支上改 workflow/tag 脚本/模板/CONTRIBUTINGPR 回 develop;两仓库 develop 就绪后各自 promote 到 main,最后做不可逆的 GitHub 设置(默认分支 release→main、分支保护、release 归档)。
**Tech Stack:** GitHub Actions workflowYAML)、Bashtag.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→developCI 全量)→main(发版);release 归档只读。
- **tag**semver `vYYYY.M.P`,只在 main 打;develop 不打正式 tag。
- **不改业务代码**:只动 workflow / 脚本 / 模板 / 文档 / GitHub 设置。
- **commit**Conventional Commitsscope 用 `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-dc3feature/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/<scope>` 分支,PR 回 `develop`CI 全量门禁)。
- `main`:生产主干。`develop` 验证通过后以 PR promote 到 `main`;每次合并到 `main` 即为一次发版。
- `hotfix/<scope>`:从 `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: 合并 PRmerge 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-webfeature/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 3config.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-webstatus 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 的基线确认点已标注。
@@ -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 个 workflowci/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/<scope>``feat/<scope>`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 <tag>` + `gh release create <tag> --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→developCI 全量)→ promote develop→main(发版)。
- tag/发版:`make tag [patch|minor|major]`semver `vYYYY.M.P`,只在 main。
- commit 规范:沿用 Conventional Commits(已有,保留引用)。
- 外部贡献者指引:从 develop 切 featurePR 到 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 去 releasedocs.yml release→maindocker-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 去 releasedocker-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)。