@elvis1513/auto-coding-skill 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -23
- package/cli/assets/skill/SKILL.md +60 -17
- package/cli/assets/skill/data/templates/ENGINEERING.md +48 -12
- package/cli/assets/skill/data/templates/bridges/CLAUDE.md +1 -0
- package/cli/assets/skill/data/templates/bridges/CODEX.md +1 -0
- package/cli/assets/skill/data/templates/docs/deployment/deploy-records/_TEMPLATE-DEPLOY-RECORD.md +17 -3
- package/cli/assets/skill/data/templates/docs/deployment/deploy-runbook.md +17 -10
- package/cli/assets/skill/data/templates/docs/interfaces/api.md +5 -5
- package/cli/assets/skill/data/templates/docs/reviews/_TEMPLATE-REVIEW.md +7 -5
- package/cli/assets/skill/data/templates/docs/tasks/summaries/_TEMPLATE-TASK-SUMMARY.md +11 -7
- package/cli/assets/skill/data/templates/docs/tasks/taskbook.md +5 -4
- package/cli/assets/skill/data/templates/docs/testing/regression-matrix.md +9 -3
- package/cli/assets/skill/scripts/ap.py +307 -14
- package/cli/assets/skill/scripts/core.py +1 -1
- package/cli/assets/skill/scripts/http_checks.py +92 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# auto-coding-skill
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Engineering workflow skill for:
|
|
4
4
|
|
|
5
5
|
- Claude Code
|
|
6
6
|
- Codex CLI
|
|
7
7
|
|
|
8
|
-
This
|
|
9
|
-
It
|
|
8
|
+
This branch is specialized for Go backend + frontend monorepo projects that build Docker images locally, validate with project `docker compose`, and rely on Jenkins to auto-build and update target environments after push.
|
|
9
|
+
It supports both Claude and Codex. During development, it prefers already available MCP servers, installed skills, plugins, and app connectors for design, research, documentation, verification, and external system updates.
|
|
10
|
+
It also prefers multi-agent execution whenever the work can be split into parallel subtasks safely.
|
|
10
11
|
|
|
11
12
|
## Install
|
|
12
13
|
|
|
@@ -41,7 +42,7 @@ python3 .claude/skills/auto-coding-skill/scripts/ap.py --repo . install
|
|
|
41
42
|
|
|
42
43
|
- `docs/ENGINEERING.md` frontmatter
|
|
43
44
|
|
|
44
|
-
This frontmatter is the only manual config source (commands + local Docker runtime + docs paths).
|
|
45
|
+
This frontmatter is the only manual config source (commands + local Docker runtime + Jenkins + docs paths).
|
|
45
46
|
|
|
46
47
|
4. Start AI development by constraints:
|
|
47
48
|
|
|
@@ -64,6 +65,19 @@ This frontmatter is the only manual config source (commands + local Docker runti
|
|
|
64
65
|
- Split research, design, implementation, validation, and documentation into parallel subtasks whenever the boundaries are clear.
|
|
65
66
|
- Keep one main agent responsible for integration and final gates.
|
|
66
67
|
|
|
68
|
+
7. Delivery rule during execution:
|
|
69
|
+
|
|
70
|
+
- Local `docker compose` validation must pass before commit.
|
|
71
|
+
- `git push` is expected to trigger Jenkins automatically.
|
|
72
|
+
- Task is not complete until Jenkins succeeds and the target environment health check passes.
|
|
73
|
+
|
|
74
|
+
8. Branch rule during execution:
|
|
75
|
+
|
|
76
|
+
- `dev` is the long-lived integration branch.
|
|
77
|
+
- If there is no parallel work conflict, prefer `dev`-first.
|
|
78
|
+
- If the repo is in detached HEAD, worktree mode, or another task is already mutating `dev`, create a temporary task branch first.
|
|
79
|
+
- Temporary branches should stay task-scoped and rebase back to latest `dev` before final integration.
|
|
80
|
+
|
|
67
81
|
## AGENTS.md Constraint Example
|
|
68
82
|
|
|
69
83
|
```md
|
|
@@ -72,7 +86,7 @@ This frontmatter is the only manual config source (commands + local Docker runti
|
|
|
72
86
|
- Before any code change, read and obey:
|
|
73
87
|
1) docs/ENGINEERING.md
|
|
74
88
|
2) docs/tasks/taskbook.md
|
|
75
|
-
- Execute gates using `python3
|
|
89
|
+
- Execute gates using `python3 docs/tools/autopipeline/ap.py`.
|
|
76
90
|
- If required docs are missing, create/update docs first, then code.
|
|
77
91
|
```
|
|
78
92
|
|
|
@@ -81,16 +95,16 @@ This frontmatter is the only manual config source (commands + local Docker runti
|
|
|
81
95
|
### 1) docs/ENGINEERING.md
|
|
82
96
|
- Purpose: single source of project config + engineering gate rules.
|
|
83
97
|
- How to record:
|
|
84
|
-
- Fill YAML frontmatter once (project/commands/runtime/docs fields).
|
|
85
|
-
- Keep all local runtime info here only (compose file/service/container/image/health).
|
|
98
|
+
- Fill YAML frontmatter once (project/commands/runtime/jenkins/docs fields).
|
|
99
|
+
- Keep all local runtime and Jenkins info here only (compose file/service/container/image/health/job/env).
|
|
86
100
|
- Do not duplicate config in other docs.
|
|
87
101
|
|
|
88
102
|
### 2) docs/deployment/
|
|
89
103
|
- Files:
|
|
90
|
-
- `docs/deployment/deploy-runbook.md`: local
|
|
91
|
-
- `docs/deployment/deploy-records/<TASK_ID>-YYYYMMDD.md`:
|
|
104
|
+
- `docs/deployment/deploy-runbook.md`: local Compose validation + Jenkins deployment procedure.
|
|
105
|
+
- `docs/deployment/deploy-records/<TASK_ID>-YYYYMMDD.md`: local validation + Jenkins deployment evidence.
|
|
92
106
|
- How to record:
|
|
93
|
-
-
|
|
107
|
+
- Record both local Compose validation and Jenkins deployment evidence: compose file, service, container, image tag, Jenkins build, deploy env, health checks.
|
|
94
108
|
|
|
95
109
|
### 3) docs/design/
|
|
96
110
|
- Files:
|
|
@@ -114,7 +128,7 @@ This frontmatter is the only manual config source (commands + local Docker runti
|
|
|
114
128
|
- Files:
|
|
115
129
|
- `docs/reviews/<TASK_ID>-<timestamp>.md` (from review template).
|
|
116
130
|
- Purpose:
|
|
117
|
-
- Gate review evidence: static checks,
|
|
131
|
+
- Gate review evidence: static checks, Go + frontend quality, local Compose validation, Jenkins readiness, risks.
|
|
118
132
|
- How to record:
|
|
119
133
|
- Record commands used (lint/typecheck from docs/ENGINEERING.md frontmatter) and conclusion (Pass/Blocked).
|
|
120
134
|
|
|
@@ -130,26 +144,58 @@ This frontmatter is the only manual config source (commands + local Docker runti
|
|
|
130
144
|
- Files:
|
|
131
145
|
- `docs/testing/regression-matrix.md`
|
|
132
146
|
- Purpose:
|
|
133
|
-
- Full regression matrix against the local
|
|
147
|
+
- Full regression matrix against the local Compose environment; must be 0 FAIL.
|
|
134
148
|
- How to record:
|
|
135
|
-
- Add
|
|
136
|
-
-
|
|
149
|
+
- Add rows by regression ID (R-xxx), area, steps/command, expected, status, evidence.
|
|
150
|
+
- New or unexecuted rows must stay `TODO`.
|
|
151
|
+
- `PASS` is valid only after real execution with non-placeholder evidence.
|
|
152
|
+
- If any row is not `PASS`, or evidence is placeholder text, gate fails.
|
|
153
|
+
|
|
154
|
+
## Branch Policy
|
|
155
|
+
|
|
156
|
+
- `dev` is the only long-lived integration branch.
|
|
157
|
+
- Temporary branches are preferred when parallel worktrees or concurrent tasks would otherwise collide on `dev`.
|
|
158
|
+
- Temporary branches should be small, task-scoped, and rebased frequently against latest `dev`.
|
|
159
|
+
- Final integration target remains `dev`; temporary branches are not release branches.
|
|
160
|
+
|
|
161
|
+
## CI Trigger Strategy
|
|
162
|
+
|
|
163
|
+
- Prefer split Jenkins behavior:
|
|
164
|
+
- Branch or MR validation job for build/test/lint/typecheck and optional non-deploy runtime checks.
|
|
165
|
+
- `dev` integration/deploy job for actual deployment-triggering pushes.
|
|
166
|
+
- Avoid duplicate deploy triggers from both merge acceptance events and `dev` push events.
|
|
137
167
|
|
|
138
168
|
## Commands
|
|
139
169
|
|
|
140
170
|
```bash
|
|
141
171
|
pip install pyyaml requests
|
|
142
|
-
python3
|
|
143
|
-
python3
|
|
144
|
-
python3
|
|
145
|
-
python3
|
|
146
|
-
python3
|
|
147
|
-
python3
|
|
148
|
-
python3
|
|
149
|
-
python3
|
|
150
|
-
python3
|
|
172
|
+
python3 docs/tools/autopipeline/ap.py run build
|
|
173
|
+
python3 docs/tools/autopipeline/ap.py run test
|
|
174
|
+
python3 docs/tools/autopipeline/ap.py run lint
|
|
175
|
+
python3 docs/tools/autopipeline/ap.py run typecheck
|
|
176
|
+
python3 docs/tools/autopipeline/ap.py run docker_build
|
|
177
|
+
python3 docs/tools/autopipeline/ap.py runtime-up
|
|
178
|
+
python3 docs/tools/autopipeline/ap.py wait-health
|
|
179
|
+
python3 docs/tools/autopipeline/ap.py run smoke
|
|
180
|
+
python3 docs/tools/autopipeline/ap.py run regression
|
|
181
|
+
python3 docs/tools/autopipeline/ap.py runtime-down
|
|
182
|
+
python3 docs/tools/autopipeline/ap.py verify-jenkins
|
|
183
|
+
python3 docs/tools/autopipeline/ap.py verify-jenkins-build --git-ref HEAD
|
|
184
|
+
python3 docs/tools/autopipeline/ap.py wait-health --scope prod
|
|
185
|
+
python3 docs/tools/autopipeline/ap.py verify-api-docs
|
|
186
|
+
python3 docs/tools/autopipeline/ap.py check-matrix
|
|
187
|
+
python3 docs/tools/autopipeline/ap.py gen-summary T0001-1
|
|
188
|
+
python3 docs/tools/autopipeline/ap.py commit-push T0001-1 --msg "T0001-1: <summary>" --require-runtime-health --require-jenkins --require-matrix
|
|
151
189
|
```
|
|
152
190
|
|
|
191
|
+
## Quality Gate Expectations
|
|
192
|
+
|
|
193
|
+
- Backend quality gate: `commands.test` must pass.
|
|
194
|
+
- Frontend quality gate: at minimum `commands.build`, `commands.lint`, and `commands.typecheck` must pass.
|
|
195
|
+
- Regression matrix rows must start as `TODO` until actually executed.
|
|
196
|
+
- `PASS` requires real evidence, not placeholders.
|
|
197
|
+
- Before final commit/push, clean temporary logs, screenshots, generated artifacts, and cache by-products. `.local/` may remain when it is the intended local runtime data directory.
|
|
198
|
+
|
|
153
199
|
## Publish (NPM)
|
|
154
200
|
|
|
155
201
|
1. Sync assets and basic check:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: auto-coding-skill
|
|
3
|
-
description: Use for strict
|
|
3
|
+
description: Use for strict Go fullstack monorepo engineering workflow in Claude/Codex. Initialize docs, fill docs/ENGINEERING.md frontmatter once, then execute design->implement->local-docker-gates->jenkins-trigger->verify.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Auto Coding Skill (Claude + Codex)
|
|
7
7
|
|
|
8
|
-
This skill
|
|
8
|
+
This branch specializes the skill for Go backend + frontend monorepo projects that build Docker images locally and use Jenkins pipelines to auto-deploy after push. It supports both Claude and Codex. During design, research, implementation, verification, and delivery, prefer already available MCP servers, installed skills, plugins, and app connectors over ad-hoc manual work whenever they can complete the task reliably.
|
|
9
9
|
|
|
10
10
|
Default to multi-agent execution when the client supports it. Break work into independent design, research, implementation, validation, and documentation subtasks so Claude/Codex can run them in parallel whenever that reduces cycle time without weakening control of the main task.
|
|
11
11
|
|
|
@@ -28,6 +28,7 @@ Typical examples:
|
|
|
28
28
|
- Documentation/library lookup: prefer official docs and MCP-backed doc tools.
|
|
29
29
|
- Project management or knowledge base updates: prefer Linear/Notion connectors if available.
|
|
30
30
|
- Browser/runtime verification: prefer Playwright/browser tools if available.
|
|
31
|
+
- Pipeline and deployment verification: prefer Jenkins-capable connectors, browser automation, or project-integrated tools if available.
|
|
31
32
|
|
|
32
33
|
## Collaboration policy
|
|
33
34
|
|
|
@@ -69,29 +70,71 @@ Fill only:
|
|
|
69
70
|
This contains all manual fields:
|
|
70
71
|
- `commands.*`
|
|
71
72
|
- `runtime.*`
|
|
73
|
+
- `jenkins.*`
|
|
72
74
|
- `docs.*`
|
|
73
75
|
|
|
74
76
|
Do not duplicate config in other md/yaml files.
|
|
75
77
|
|
|
78
|
+
## Branch policy
|
|
79
|
+
|
|
80
|
+
- `dev` remains the only long-lived integration branch.
|
|
81
|
+
- Default behavior stays `dev`-first when there is no parallel work conflict.
|
|
82
|
+
- If Claude or Codex is operating in a derived worktree, detached HEAD, or any parallel task context where another thread is already changing `dev`, prefer creating a temporary branch from the latest `dev` before editing.
|
|
83
|
+
- Name temporary branches after the task, preferably `codex/<task-id>-<slug>` such as `codex/t0005-domestic-payment-site`.
|
|
84
|
+
- Keep the temporary branch scoped to one task, complete design/implementation/verification there, then merge or rebase it back onto `dev` only after local gates pass.
|
|
85
|
+
- Do not treat temporary branches as release branches; the final integration target is still `dev`.
|
|
86
|
+
- In temporary-branch mode, work in small, closed-loop slices. Each slice should have a clear scope, synchronized docs, the relevant local validation, and a commit that can stand on its own.
|
|
87
|
+
- Rebase temporary branches frequently against the latest `dev` to keep merge surfaces small.
|
|
88
|
+
|
|
89
|
+
## CI trigger strategy
|
|
90
|
+
|
|
91
|
+
- Prefer a split Jenkins model when parallel worktrees are active:
|
|
92
|
+
- MR or branch validation job: build/test/lint/typecheck and optional non-deploy runtime checks on temporary branches or merge requests.
|
|
93
|
+
- `dev` integration/deploy job: trigger only from pushes that land on `dev`.
|
|
94
|
+
- Do not rely on merge-request acceptance events to drive production deployment when a `dev` push event already exists; that commonly creates duplicate builds around merge time.
|
|
95
|
+
|
|
76
96
|
## Execution order
|
|
77
97
|
|
|
78
|
-
1) `
|
|
79
|
-
2) `docs/
|
|
80
|
-
3) `docs/
|
|
81
|
-
4)
|
|
82
|
-
5)
|
|
83
|
-
6)
|
|
84
|
-
7)
|
|
85
|
-
8)
|
|
98
|
+
1) choose branch mode (`dev` directly, or temporary branch if parallel worktree rules apply)
|
|
99
|
+
2) `docs/ENGINEERING.md`
|
|
100
|
+
3) `docs/tasks/taskbook.md`
|
|
101
|
+
4) `docs/design/**`
|
|
102
|
+
5) implementation
|
|
103
|
+
6) local build/test/lint gates
|
|
104
|
+
7) start and validate local Docker Compose runtime
|
|
105
|
+
8) update API docs + regression matrix + bug list + summary
|
|
106
|
+
9) verify Jenkins config / Jenkinsfile readiness
|
|
107
|
+
10) if temporary-branch mode is used, close one small slice at a time with reviewable commits and rebase regularly onto `dev`
|
|
108
|
+
11) merge/rebase temporary branch back to latest `dev` when temporary-branch mode was used
|
|
109
|
+
12) commit/push to trigger Jenkins
|
|
110
|
+
13) verify Jenkins pipeline + target environment health, preferably with `verify-jenkins-build --git-ref HEAD` (strict deploy check by default; use `--allow-no-deploy` only for docs-only sync verification)
|
|
86
111
|
|
|
87
112
|
## Commands
|
|
88
113
|
|
|
89
114
|
```bash
|
|
90
|
-
python3
|
|
91
|
-
python3
|
|
92
|
-
python3
|
|
93
|
-
python3
|
|
94
|
-
python3
|
|
95
|
-
python3
|
|
96
|
-
python3
|
|
115
|
+
python3 docs/tools/autopipeline/ap.py run build
|
|
116
|
+
python3 docs/tools/autopipeline/ap.py run test
|
|
117
|
+
python3 docs/tools/autopipeline/ap.py run lint
|
|
118
|
+
python3 docs/tools/autopipeline/ap.py run typecheck
|
|
119
|
+
python3 docs/tools/autopipeline/ap.py run docker_build
|
|
120
|
+
python3 docs/tools/autopipeline/ap.py runtime-up
|
|
121
|
+
python3 docs/tools/autopipeline/ap.py wait-health
|
|
122
|
+
python3 docs/tools/autopipeline/ap.py run smoke
|
|
123
|
+
python3 docs/tools/autopipeline/ap.py run regression
|
|
124
|
+
python3 docs/tools/autopipeline/ap.py runtime-down
|
|
125
|
+
python3 docs/tools/autopipeline/ap.py verify-jenkins
|
|
126
|
+
python3 docs/tools/autopipeline/ap.py verify-jenkins-build --git-ref HEAD
|
|
127
|
+
python3 docs/tools/autopipeline/ap.py wait-health --scope prod
|
|
128
|
+
python3 docs/tools/autopipeline/ap.py verify-api-docs
|
|
129
|
+
python3 docs/tools/autopipeline/ap.py check-matrix
|
|
130
|
+
python3 docs/tools/autopipeline/ap.py gen-summary T0001-1
|
|
131
|
+
python3 docs/tools/autopipeline/ap.py commit-push T0001-1 --msg "T0001-1: <summary>" --require-runtime-health --require-jenkins --require-matrix
|
|
97
132
|
```
|
|
133
|
+
|
|
134
|
+
## Quality gate expectations
|
|
135
|
+
|
|
136
|
+
- Gate-4: backend must pass `commands.test`; frontend must at least pass `commands.build`, `commands.lint`, and `commands.typecheck`. Frontend automated tests are added incrementally when the repo gains them.
|
|
137
|
+
- Gate-9: `docs/testing/regression-matrix.md` rows must start as `TODO` until they are actually executed.
|
|
138
|
+
- A matrix row can be marked `PASS` only after real execution, and `Evidence` must contain non-placeholder logs, screenshots, or report paths.
|
|
139
|
+
- `python3 docs/tools/autopipeline/ap.py check-matrix` should be treated as a hard gate; placeholder evidence is equivalent to incomplete regression.
|
|
140
|
+
- Before the final commit/push, clean temporary files, logs, screenshots, generated verification artifacts, cache directories, and similar by-products created during the task. The only persistent local runtime data that may remain is `.local/`.
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
project:
|
|
3
3
|
name: ""
|
|
4
4
|
repo_root: "."
|
|
5
|
+
stack: "go-fullstack-monorepo"
|
|
6
|
+
backend_dir: ""
|
|
7
|
+
frontend_dir: ""
|
|
8
|
+
go_main: ""
|
|
9
|
+
dockerfile: ""
|
|
10
|
+
jenkinsfile: "Jenkinsfile"
|
|
5
11
|
|
|
6
12
|
commands:
|
|
7
13
|
build: ""
|
|
@@ -9,6 +15,9 @@ commands:
|
|
|
9
15
|
lint: ""
|
|
10
16
|
typecheck: ""
|
|
11
17
|
format: ""
|
|
18
|
+
docker_build: ""
|
|
19
|
+
compose_up: ""
|
|
20
|
+
compose_down: ""
|
|
12
21
|
smoke: ""
|
|
13
22
|
regression: ""
|
|
14
23
|
|
|
@@ -21,7 +30,22 @@ runtime:
|
|
|
21
30
|
health_base_url: ""
|
|
22
31
|
health_path: ""
|
|
23
32
|
env_file: ""
|
|
24
|
-
startup_timeout_sec:
|
|
33
|
+
startup_timeout_sec: 120
|
|
34
|
+
|
|
35
|
+
jenkins:
|
|
36
|
+
job_name: ""
|
|
37
|
+
job_url: ""
|
|
38
|
+
trigger_branch: ""
|
|
39
|
+
image_repository: ""
|
|
40
|
+
image_tag_strategy: ""
|
|
41
|
+
deploy_env: ""
|
|
42
|
+
deploy_timeout_sec: 1800
|
|
43
|
+
prod_health_base_url: ""
|
|
44
|
+
prod_health_path: ""
|
|
45
|
+
api_user: ""
|
|
46
|
+
api_token: ""
|
|
47
|
+
api_user_env: "JENKINS_USER"
|
|
48
|
+
api_token_env: "JENKINS_TOKEN"
|
|
25
49
|
|
|
26
50
|
docs:
|
|
27
51
|
taskbook: "docs/tasks/taskbook.md"
|
|
@@ -37,17 +61,21 @@ docs:
|
|
|
37
61
|
# docs/ENGINEERING.md — AutoPipeline Gates (Source of Truth)
|
|
38
62
|
|
|
39
63
|
目标:把一次任务固化为不可跳过的流水线:
|
|
40
|
-
读任务 → 写DD → 实现 →
|
|
41
|
-
本地 Docker 启动验证 →
|
|
42
|
-
记录 Bug 并新增自动化回归 → 任务总结落盘 → commit → push
|
|
64
|
+
读任务 → 写DD → 实现 → 本地构建/测试通过 → 静态分析+Review落盘 → 更新 API Markdown+接口变更清单 →
|
|
65
|
+
本地 Docker Compose 启动验证 → 本地健康检查 → 对本地环境全量回归 + 回归矩阵 0 fail →
|
|
66
|
+
记录 Bug 并新增自动化回归 → 任务总结落盘 → commit → push 触发 Jenkins → Jenkins 构建镜像并更新目标环境 →
|
|
67
|
+
生产健康检查通过
|
|
43
68
|
|
|
44
|
-
|
|
69
|
+
规则:任一步骤失败或缺产物,禁止进入下一步;本地 compose 验证未通过禁止 commit;Jenkins 未成功或生产健康检查未通过,任务不视为完成。
|
|
70
|
+
|
|
71
|
+
补充规则:
|
|
72
|
+
- 每次任务闭环后,必须清理临时文件、临时目录、日志、截图、回归中间产物、构建缓存等非必要产物;仅 `.local/` 下的本地运行数据允许保留。
|
|
45
73
|
|
|
46
74
|
---
|
|
47
75
|
|
|
48
76
|
## 0. 配置填写(必须)
|
|
49
77
|
|
|
50
|
-
先填写 `docs/ENGINEERING.md` frontmatter 中的所有空值(例如 Docker
|
|
78
|
+
先填写 `docs/ENGINEERING.md` frontmatter 中的所有空值(例如 Go/前端目录、Docker 文件、Compose 服务、Jenkins Job、健康检查地址、命令)。
|
|
51
79
|
禁止在其他 md/yaml 重复维护这些配置。
|
|
52
80
|
|
|
53
81
|
---
|
|
@@ -81,6 +109,7 @@ docs:
|
|
|
81
109
|
- 能用权威工具直接完成时,不重复手写中间数据。
|
|
82
110
|
- 工具不可用、无权限、结果不可靠时,才回退到本地命令或手工处理。
|
|
83
111
|
- 选择工具时优先“已安装且当前项目可直接使用”的能力,而不是重新造流程。
|
|
112
|
+
- 查看 Jenkins、知识库、设计稿、页面、缺陷系统时,优先使用现成的连接器或 MCP,而不是手工拼接上下文。
|
|
84
113
|
|
|
85
114
|
---
|
|
86
115
|
|
|
@@ -102,17 +131,24 @@ docs:
|
|
|
102
131
|
Gate-1 读任务:只从 taskbook 取范围与验收;缺信息先补 taskbook
|
|
103
132
|
Gate-2 写DD:无DD禁止写代码;DD必须含 时序图/ER图/接口时序(Mermaid)
|
|
104
133
|
Gate-3 实现:严格按DD;接口变更必须同步 API Markdown
|
|
105
|
-
Gate-4 本地CI
|
|
134
|
+
Gate-4 本地CI:后端必须通过 `commands.test`;前端至少通过 `commands.build`、`commands.lint`、`commands.typecheck`;前端自动化测试能力逐步补齐
|
|
106
135
|
Gate-5 静态分析+Review:静态分析通过;docs/reviews/ 生成记录
|
|
107
136
|
Gate-6 文档:更新 api.md + 追加 api-change-log.md
|
|
108
|
-
Gate-7
|
|
137
|
+
Gate-7 本地运行:必须用项目 Compose 启动本地 Docker 环境;失败先修复再继续
|
|
109
138
|
Gate-8 健康检查:本地容器启动后必须健康检查通过
|
|
110
|
-
Gate-9 全量回归:按 API Markdown
|
|
111
|
-
Gate-10
|
|
112
|
-
Gate-11
|
|
139
|
+
Gate-9 全量回归:按 API Markdown 对本地 Compose 环境全量回归;回归矩阵仅可在真实执行后标记 PASS,且必须附证据;发现问题必须写 bug-list 并新增自动化回归用例
|
|
140
|
+
Gate-10 Jenkins 准备:Jenkinsfile、Job 配置、镜像仓库策略必须可用
|
|
141
|
+
Gate-11 任务总结:必须生成 docs/tasks/summaries/<TASK_ID>.md
|
|
142
|
+
Gate-12 提交触发:本地门禁全过且临时产物已清理后,才允许 commit+push
|
|
143
|
+
Gate-13 流水线验证:push 后必须确认 Jenkins 自动构建、镜像发布、目标环境更新成功
|
|
144
|
+
Gate-14 完成:生产健康检查通过并补齐部署记录后,任务才完成
|
|
113
145
|
|
|
114
146
|
---
|
|
115
147
|
|
|
116
148
|
## 3. Repo 工具入口
|
|
117
149
|
|
|
118
|
-
统一用 `python3
|
|
150
|
+
统一用 `python3 docs/tools/autopipeline/ap.py <command>` 执行。
|
|
151
|
+
|
|
152
|
+
补充:
|
|
153
|
+
- `commands.smoke` / `commands.regression` 可以封装 repo 脚本,但必须真正在本地运行目标系统。
|
|
154
|
+
- `docs/testing/regression-matrix.md` 中的 `PASS` 只在真实执行并填入证据后允许保留;占位符证据会被视为未完成。
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
Follow docs/ENGINEERING.md strictly. Source of truth: docs/ENGINEERING.md.
|
|
2
2
|
Prefer already available MCP servers, installed skills, plugins, and app connectors during design, research, verification, and documentation workflows.
|
|
3
3
|
Prefer multi-agent mode whenever the task can be split into independent parallel subtasks without weakening integration control.
|
|
4
|
+
For Go monorepo + Jenkins projects, local Docker Compose validation must pass before commit, and push is expected to trigger Jenkins pipeline verification.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
Follow docs/ENGINEERING.md strictly. Source of truth: docs/ENGINEERING.md.
|
|
2
2
|
Prefer already available MCP servers, installed skills, plugins, and app connectors during design, research, verification, and documentation workflows.
|
|
3
3
|
Prefer multi-agent mode whenever the task can be split into independent parallel subtasks without weakening integration control.
|
|
4
|
+
For Go monorepo + Jenkins projects, local Docker Compose validation must pass before commit, and push is expected to trigger Jenkins pipeline verification.
|
package/cli/assets/skill/data/templates/docs/deployment/deploy-records/_TEMPLATE-DEPLOY-RECORD.md
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Deploy Record — <Task ID> — YYYY-MM-DD
|
|
2
2
|
|
|
3
|
+
## 1. Local Compose Validation
|
|
3
4
|
- Docker compose file:
|
|
4
5
|
- Docker service:
|
|
5
6
|
- Container name:
|
|
6
7
|
- Image/tag:
|
|
7
8
|
- Exposed port:
|
|
8
9
|
- Env file:
|
|
10
|
+
- Local health URL:
|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## 2. Jenkins Pipeline
|
|
13
|
+
- Jenkins job name:
|
|
14
|
+
- Jenkins build number:
|
|
15
|
+
- Jenkins build URL:
|
|
16
|
+
- Trigger branch:
|
|
17
|
+
- Trigger commit:
|
|
18
|
+
- Image repository:
|
|
19
|
+
- Published image tag:
|
|
20
|
+
- Deploy environment:
|
|
21
|
+
|
|
22
|
+
## 3. Evidence
|
|
11
23
|
- docker ps / compose ps:
|
|
24
|
+
- local_health_check:
|
|
12
25
|
- smoke_test:
|
|
13
26
|
- api_regression:
|
|
14
|
-
-
|
|
27
|
+
- jenkins_result:
|
|
28
|
+
- production_health_check:
|
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Deployment Runbook(本地 Compose 验证 + Jenkins 自动部署)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- runtime
|
|
5
|
-
-
|
|
6
|
-
- runtime.health_base_url / runtime.health_path
|
|
7
|
-
- runtime.env_file / runtime.startup_timeout_sec
|
|
3
|
+
统一读取:`docs/ENGINEERING.md` frontmatter
|
|
4
|
+
- `runtime.*`:本地 Compose 启动与健康检查
|
|
5
|
+
- `jenkins.*`:Jenkins Job、镜像仓库、目标环境、生产健康检查
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
执行顺序:
|
|
8
|
+
1. 本地构建、测试、lint、typecheck 通过
|
|
9
|
+
2. 本地 `docker compose` 启动目标服务
|
|
10
|
+
3. 本地 health / smoke / regression 全部通过
|
|
11
|
+
4. `commit + push`
|
|
12
|
+
5. Jenkins 自动触发,完成镜像构建、镜像推送、目标环境更新
|
|
13
|
+
6. 检查 Jenkins 结果与目标环境健康状态
|
|
14
|
+
|
|
15
|
+
完成条件:
|
|
16
|
+
- 本地 Compose 验证通过
|
|
17
|
+
- Jenkins Pipeline 成功
|
|
18
|
+
- 目标环境健康检查通过
|
|
19
|
+
- `docs/deployment/deploy-records/<TASK_ID>-YYYYMMDD.md` 证据补齐
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
## 2. Endpoint Index(目录)
|
|
14
14
|
| Method | Path | Summary | Auth | Request | Response |
|
|
15
15
|
|---|---|---|---|---|---|
|
|
16
|
-
|
|
|
16
|
+
| <METHOD> | <PATH> | <Summary> | <Yes/No> | <Request shape> | <Response shape> |
|
|
17
17
|
|
|
18
18
|
## 3. Endpoints
|
|
19
19
|
|
|
20
|
-
### 3.1
|
|
21
|
-
**Summary**:
|
|
20
|
+
### 3.1 <METHOD> <PATH>
|
|
21
|
+
**Summary**: <Summary>
|
|
22
22
|
|
|
23
23
|
**Request**
|
|
24
24
|
- Headers: -
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
- Body: -
|
|
27
27
|
|
|
28
28
|
**Response**
|
|
29
|
-
-
|
|
29
|
+
- <Status>: <Meaning>
|
|
30
30
|
- Example:
|
|
31
31
|
```json
|
|
32
|
-
{
|
|
32
|
+
{}
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## 4. Changelog
|
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
- Resolved/Deferred(deferred 必须给出后续任务):
|
|
8
8
|
|
|
9
9
|
## 2. 代码质量
|
|
10
|
-
## 3.
|
|
11
|
-
## 4.
|
|
12
|
-
## 5.
|
|
13
|
-
## 6.
|
|
14
|
-
## 7.
|
|
10
|
+
## 3. 本地构建与测试质量(后端 test + 前端 build/lint/typecheck)
|
|
11
|
+
## 4. 本地 Compose 验证(health / smoke / regression)
|
|
12
|
+
## 5. 接口契约(API Markdown + change-log)
|
|
13
|
+
## 6. Jenkins 准备度(Jenkinsfile / job config / image strategy)
|
|
14
|
+
## 7. 安全与性能
|
|
15
|
+
## 8. 风险与回滚
|
|
16
|
+
## 9. 结论(Pass / Blocked)
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- 目标:
|
|
12
12
|
- 验收结论:PASS / FAIL(FAIL 必须说明原因与后续计划)
|
|
13
13
|
|
|
14
|
-
## 2.
|
|
14
|
+
## 2. 变更概览(代码/配置/本地运行/Jenkins)
|
|
15
15
|
- 关键改动点:
|
|
16
16
|
- 影响模块:
|
|
17
17
|
- 兼容性影响(是否破坏兼容、迁移方案):
|
|
@@ -29,19 +29,23 @@
|
|
|
29
29
|
|
|
30
30
|
## 5. 质量门禁证据(必须可追溯)
|
|
31
31
|
- 项目配置:`docs/ENGINEERING.md`(frontmatter)
|
|
32
|
-
-
|
|
33
|
-
-
|
|
32
|
+
- 后端测试:`commands.test`
|
|
33
|
+
- 前端构建:`commands.build`
|
|
34
|
+
- 静态分析:`commands.lint`
|
|
35
|
+
- 前端类型检查:`commands.typecheck`
|
|
34
36
|
- Review 文档:`docs/reviews/<TASK_ID>-<timestamp>.md`
|
|
35
37
|
- API 文档:`docs/interfaces/api.md`
|
|
36
|
-
-
|
|
38
|
+
- Jenkins 配置检查:`Jenkinsfile` + `jenkins.*`
|
|
39
|
+
- 回归矩阵:`docs/testing/regression-matrix.md`(全量 PASS,0 fail,且每项必须有真实证据)
|
|
37
40
|
|
|
38
41
|
## 6. Bug 清单与回归用例
|
|
39
42
|
- 新增/确认的 Bug(写入 `docs/bugs/bug-list.md`):
|
|
40
43
|
- 新增自动化回归用例(引用回归矩阵ID):
|
|
41
44
|
|
|
42
|
-
## 7.
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
+
## 7. 本地运行与 Jenkins 部署记录
|
|
46
|
+
- 本地 Compose 验证:health / smoke / regression 结果
|
|
47
|
+
- Jenkins 部署记录:`docs/deployment/deploy-records/<TASK_ID>-YYYYMMDD.md`
|
|
48
|
+
- Docker compose / image / Jenkins Job 变更(若有):
|
|
45
49
|
|
|
46
50
|
## 8. 风险与回滚
|
|
47
51
|
- 风险:
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
规则:
|
|
4
4
|
1) 所有任务都写在本文件(持续续写,不另起任务文件)
|
|
5
|
-
2) 允许拆子任务:每个子任务也必须走全流程(DD→实现→测试→review→接口文档→本地
|
|
5
|
+
2) 允许拆子任务:每个子任务也必须走全流程(DD→实现→测试→review→接口文档→本地 Compose 验证→回归→总结→commit→push→Jenkins 验证)
|
|
6
6
|
3) 每个任务必须有明确验收与证据(日志/报告/文件路径)
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
## Task T0001 — <Title>
|
|
11
|
-
- 状态:Planned | Designing | Implementing | Testing | Reviewing | Runtime Verifying | Done
|
|
11
|
+
- 状态:Planned | Designing | Implementing | Testing | Reviewing | Runtime Verifying | Pipeline Verifying | Done
|
|
12
12
|
- 范围(In scope):
|
|
13
13
|
- 非目标(Out of scope):
|
|
14
14
|
- 验收标准(必须可执行):
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
- API 文档:`docs/interfaces/api.md`
|
|
25
25
|
- API Change Log:`docs/interfaces/api-change-log.md`
|
|
26
26
|
- 本地CI:粘贴摘要或给出文件路径
|
|
27
|
-
-
|
|
28
|
-
-
|
|
27
|
+
- 本地 Compose 验证:命令输出或 Review 文档摘要
|
|
28
|
+
- Jenkins 部署记录:`docs/deployment/deploy-records/T0001-YYYYMMDD.md`
|
|
29
|
+
- 回归矩阵:`docs/testing/regression-matrix.md`(全量PASS,且每项必须有真实证据)
|
|
29
30
|
- Bug清单(如有):`docs/bugs/bug-list.md`
|
|
30
31
|
- 任务总结(强制):`docs/tasks/summaries/T0001.md`
|
|
31
32
|
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
# Regression Matrix
|
|
1
|
+
# Regression Matrix(本地 Compose 环境回归矩阵:必须全量 PASS,0 fail)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> 1. 仅记录当前真实实现,不要预填伪接口或目标态接口。
|
|
4
|
+
> 2. `Status` 允许值:`TODO` / `PASS` / `FAIL`。
|
|
5
|
+
> 3. 新增或未执行项默认填写 `TODO`,不得预填 `PASS`。
|
|
6
|
+
> 4. `PASS` 只允许在真实执行后填写,且 `Evidence` 不得保留占位符。
|
|
7
|
+
> 5. `python3 docs/tools/autopipeline/ap.py check-matrix` 会把非 `PASS` 行和占位符证据视为未完成。
|
|
8
|
+
|
|
9
|
+
| ID | Area | Endpoint/Feature | Test Type | Steps / Command | Expected | Status(TODO/PASS/FAIL) | Evidence |
|
|
4
10
|
|---|---|---|---|---|---|---|---|
|
|
5
|
-
| R-001 | API |
|
|
11
|
+
| R-001 | API | <Endpoint or feature> | <smoke/regression/manual> | <command or manual steps> | <expected result> | TODO | <fill-with-log-path-screenshot-or-report> |
|
|
@@ -5,17 +5,104 @@
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
|
+
import base64
|
|
8
9
|
import datetime as _dt
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import time
|
|
13
|
+
import urllib.parse
|
|
14
|
+
import urllib.request
|
|
9
15
|
from pathlib import Path
|
|
10
16
|
from typing import Optional, List
|
|
11
17
|
|
|
12
|
-
from core import APError, ensure_git_repo, copy_tree, run, load_yaml, find_config, run_shell
|
|
18
|
+
from core import APError, ensure_git_repo, copy_tree, run, load_yaml, find_config, run_shell, http_get_status
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
def _skill_root() -> Path:
|
|
16
22
|
return Path(__file__).resolve().parent.parent
|
|
17
23
|
|
|
18
24
|
|
|
25
|
+
def _compose_base_args(runtime_cfg: dict) -> List[str]:
|
|
26
|
+
args = ["docker", "compose"]
|
|
27
|
+
compose_file = str(runtime_cfg.get("docker_compose_file") or "").strip()
|
|
28
|
+
env_file = str(runtime_cfg.get("env_file") or "").strip()
|
|
29
|
+
if compose_file:
|
|
30
|
+
args.extend(["-f", compose_file])
|
|
31
|
+
if env_file:
|
|
32
|
+
args.extend(["--env-file", env_file])
|
|
33
|
+
return args
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _join_url(base: str, path: str) -> str:
|
|
37
|
+
base = str(base or "").strip().rstrip("/")
|
|
38
|
+
path = str(path or "").strip()
|
|
39
|
+
if not base or not path:
|
|
40
|
+
raise APError("Health URL config incomplete. Fill base url and path in docs/ENGINEERING.md.")
|
|
41
|
+
if not path.startswith("/"):
|
|
42
|
+
path = "/" + path
|
|
43
|
+
return base + path
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _run_configured_command(repo: Path, cfg: dict, name: str) -> bool:
|
|
47
|
+
commands = (cfg.get("commands") or {})
|
|
48
|
+
command = str(commands.get(name) or "").strip()
|
|
49
|
+
if not command:
|
|
50
|
+
return False
|
|
51
|
+
print(f"[run] {name}: {command}")
|
|
52
|
+
run_shell(command, cwd=repo)
|
|
53
|
+
print(f"[run] OK: {name}")
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _jenkins_basic_auth_headers(cfg: dict) -> dict:
|
|
58
|
+
jenkins_cfg = (cfg.get("jenkins") or {})
|
|
59
|
+
direct_user = str(jenkins_cfg.get("api_user") or "").strip()
|
|
60
|
+
direct_token = str(jenkins_cfg.get("api_token") or "").strip()
|
|
61
|
+
user_env = str(jenkins_cfg.get("api_user_env") or "JENKINS_USER").strip() or "JENKINS_USER"
|
|
62
|
+
token_env = str(jenkins_cfg.get("api_token_env") or "JENKINS_TOKEN").strip() or "JENKINS_TOKEN"
|
|
63
|
+
user = direct_user or os.getenv(user_env) or os.getenv("JENKINS_USER")
|
|
64
|
+
token = direct_token or os.getenv(token_env) or os.getenv("JENKINS_TOKEN") or os.getenv("JENKINS_PASSWORD")
|
|
65
|
+
if not user or not token:
|
|
66
|
+
raise APError(
|
|
67
|
+
f"Missing Jenkins API credentials. Fill jenkins.api_user / jenkins.api_token in docs/ENGINEERING.md, "
|
|
68
|
+
f"or set env vars {user_env} and {token_env}."
|
|
69
|
+
)
|
|
70
|
+
raw = f"{user}:{token}".encode("utf-8")
|
|
71
|
+
auth = base64.b64encode(raw).decode("ascii")
|
|
72
|
+
return {"Authorization": f"Basic {auth}"}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _jenkins_api_get_json(url: str, cfg: dict, timeout_s: int = 15) -> dict:
|
|
76
|
+
headers = {"Accept": "application/json"}
|
|
77
|
+
headers.update(_jenkins_basic_auth_headers(cfg))
|
|
78
|
+
req = urllib.request.Request(url, headers=headers, method="GET")
|
|
79
|
+
try:
|
|
80
|
+
with urllib.request.urlopen(req, timeout=timeout_s) as resp:
|
|
81
|
+
data = resp.read().decode("utf-8")
|
|
82
|
+
except Exception as exc:
|
|
83
|
+
raise APError(f"Jenkins API request failed: {url}\n{exc}") from exc
|
|
84
|
+
try:
|
|
85
|
+
return json.loads(data)
|
|
86
|
+
except json.JSONDecodeError as exc:
|
|
87
|
+
raise APError(f"Jenkins API returned non-JSON response: {url}\n{exc}") from exc
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _resolve_git_short_sha(repo: Path, ref: str) -> str:
|
|
91
|
+
result = run(["git", "rev-parse", "--short=12", ref], cwd=repo, check=False)
|
|
92
|
+
value = result.stdout.strip()
|
|
93
|
+
if value:
|
|
94
|
+
return value
|
|
95
|
+
return ref.strip()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _jenkins_builds_api_url(job_url: str, max_builds: int) -> str:
|
|
99
|
+
base = str(job_url or "").strip().rstrip("/")
|
|
100
|
+
if not base:
|
|
101
|
+
raise APError("Missing jenkins.job_url in docs/ENGINEERING.md")
|
|
102
|
+
tree = f"builds[number,result,building,description,url]{{0,{max_builds}}}"
|
|
103
|
+
return f"{base}/api/json?tree={urllib.parse.quote(tree, safe='=,')}"
|
|
104
|
+
|
|
105
|
+
|
|
19
106
|
def cmd_install(args: argparse.Namespace) -> None:
|
|
20
107
|
repo = Path(args.repo).resolve()
|
|
21
108
|
templates = _skill_root() / "data" / "templates"
|
|
@@ -26,10 +113,11 @@ def cmd_install(args: argparse.Namespace) -> None:
|
|
|
26
113
|
if args.bridges:
|
|
27
114
|
copy_tree(templates / "bridges", repo)
|
|
28
115
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
copy_tree(Path(__file__).resolve(),
|
|
32
|
-
copy_tree(Path(__file__).resolve().parent / "core.py",
|
|
116
|
+
tools_dir = repo / "docs" / "tools" / "autopipeline"
|
|
117
|
+
tools_dir.mkdir(parents=True, exist_ok=True)
|
|
118
|
+
copy_tree(Path(__file__).resolve(), tools_dir / "ap.py")
|
|
119
|
+
copy_tree(Path(__file__).resolve().parent / "core.py", tools_dir / "core.py")
|
|
120
|
+
copy_tree(Path(__file__).resolve().parent / "http_checks.py", tools_dir / "http_checks.py")
|
|
33
121
|
|
|
34
122
|
gi = repo / ".gitignore"
|
|
35
123
|
secret_line = "docs/ENGINEERING.md"
|
|
@@ -41,7 +129,7 @@ def cmd_install(args: argparse.Namespace) -> None:
|
|
|
41
129
|
gi.write_text(secret_line + "\n", encoding="utf-8")
|
|
42
130
|
|
|
43
131
|
print(f"[install] OK: scaffold installed into {repo}")
|
|
44
|
-
print("[install] Next: edit docs/ENGINEERING.md frontmatter and fill
|
|
132
|
+
print("[install] Next: edit docs/ENGINEERING.md frontmatter and fill project/runtime/jenkins fields")
|
|
45
133
|
|
|
46
134
|
|
|
47
135
|
def _infer_title(taskbook: Path, task_id: str) -> str:
|
|
@@ -92,7 +180,7 @@ def cmd_gen_summary(args: argparse.Namespace) -> None:
|
|
|
92
180
|
- 目标:TODO
|
|
93
181
|
- 验收结论:PASS / FAIL — TODO
|
|
94
182
|
|
|
95
|
-
## 2.
|
|
183
|
+
## 2. 变更概览(代码/配置/本地运行/Jenkins)
|
|
96
184
|
### Git change snapshot
|
|
97
185
|
- Staged files:
|
|
98
186
|
{('- ' + staged.replace('\n','\n- ')) if staged else '- (none)'}
|
|
@@ -107,11 +195,18 @@ def cmd_gen_summary(args: argparse.Namespace) -> None:
|
|
|
107
195
|
- 变更记录位置:`{api_change_log}`
|
|
108
196
|
|
|
109
197
|
## 5. 质量门禁证据(必须可追溯)
|
|
110
|
-
-
|
|
111
|
-
-
|
|
198
|
+
- 后端测试:`commands.test` — TODO
|
|
199
|
+
- 前端构建:`commands.build` — TODO
|
|
200
|
+
- 静态分析:`commands.lint` — TODO
|
|
201
|
+
- 前端类型检查:`commands.typecheck` — TODO
|
|
112
202
|
- Review 文档:TODO
|
|
113
203
|
- DD 文档:TODO
|
|
114
|
-
-
|
|
204
|
+
- Jenkins 准备:TODO
|
|
205
|
+
- 回归矩阵:`{regression_matrix}`(全量 PASS,0 fail,且每项必须有真实证据)
|
|
206
|
+
|
|
207
|
+
## 6. 本地运行与 Jenkins 部署记录
|
|
208
|
+
- Local compose:TODO
|
|
209
|
+
- Jenkins build / deploy:TODO
|
|
115
210
|
"""
|
|
116
211
|
|
|
117
212
|
out_file.write_text(content, encoding="utf-8")
|
|
@@ -128,6 +223,28 @@ def cmd_check_matrix(args: argparse.Namespace) -> None:
|
|
|
128
223
|
|
|
129
224
|
rows = 0
|
|
130
225
|
fail = []
|
|
226
|
+
|
|
227
|
+
def evidence_missing(value: str) -> bool:
|
|
228
|
+
stripped = value.strip()
|
|
229
|
+
lower = stripped.lower()
|
|
230
|
+
if not stripped:
|
|
231
|
+
return True
|
|
232
|
+
if stripped.startswith("<") and stripped.endswith(">"):
|
|
233
|
+
return True
|
|
234
|
+
placeholder_tokens = [
|
|
235
|
+
"todo",
|
|
236
|
+
"tbd",
|
|
237
|
+
"pending",
|
|
238
|
+
"replace-with",
|
|
239
|
+
"paste log path",
|
|
240
|
+
"paste evidence",
|
|
241
|
+
"fill-with",
|
|
242
|
+
"待补",
|
|
243
|
+
"待填",
|
|
244
|
+
"占位",
|
|
245
|
+
]
|
|
246
|
+
return any(token in lower for token in placeholder_tokens)
|
|
247
|
+
|
|
131
248
|
for line in matrix.read_text(encoding="utf-8").splitlines():
|
|
132
249
|
s = line.strip()
|
|
133
250
|
if not s.startswith("|"):
|
|
@@ -144,6 +261,10 @@ def cmd_check_matrix(args: argparse.Namespace) -> None:
|
|
|
144
261
|
rows += 1
|
|
145
262
|
if status != "PASS":
|
|
146
263
|
fail.append((rid, status or "(empty)"))
|
|
264
|
+
continue
|
|
265
|
+
evidence = cols[7] if len(cols) > 7 else ""
|
|
266
|
+
if evidence_missing(evidence):
|
|
267
|
+
fail.append((rid, "PASS-without-evidence"))
|
|
147
268
|
|
|
148
269
|
if rows == 0:
|
|
149
270
|
raise APError(f"No regression rows found in matrix: {matrix}")
|
|
@@ -162,8 +283,7 @@ def _load_cfg(repo: Path) -> dict:
|
|
|
162
283
|
|
|
163
284
|
def cmd_run(args: argparse.Namespace) -> None:
|
|
164
285
|
"""
|
|
165
|
-
Run
|
|
166
|
-
build | test | lint | typecheck | format | smoke | regression
|
|
286
|
+
Run any configured gate command by name.
|
|
167
287
|
Commands are read from docs/ENGINEERING.md frontmatter.
|
|
168
288
|
"""
|
|
169
289
|
repo = Path(args.repo).resolve()
|
|
@@ -182,6 +302,150 @@ def cmd_run(args: argparse.Namespace) -> None:
|
|
|
182
302
|
print(f"[run] OK: {name}")
|
|
183
303
|
|
|
184
304
|
|
|
305
|
+
def cmd_runtime_up(args: argparse.Namespace) -> None:
|
|
306
|
+
repo = Path(args.repo).resolve()
|
|
307
|
+
cfg = _load_cfg(repo)
|
|
308
|
+
runtime_cfg = (cfg.get("runtime") or {})
|
|
309
|
+
if _run_configured_command(repo, cfg, "compose_up"):
|
|
310
|
+
return
|
|
311
|
+
compose_args = _compose_base_args(runtime_cfg) + ["up", "-d"]
|
|
312
|
+
docker_service = str(runtime_cfg.get("docker_service") or "").strip()
|
|
313
|
+
if docker_service:
|
|
314
|
+
compose_args.append(docker_service)
|
|
315
|
+
print(f"[runtime-up] {' '.join(compose_args)}")
|
|
316
|
+
run(compose_args, cwd=repo)
|
|
317
|
+
print("[runtime-up] OK")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def cmd_runtime_down(args: argparse.Namespace) -> None:
|
|
321
|
+
repo = Path(args.repo).resolve()
|
|
322
|
+
cfg = _load_cfg(repo)
|
|
323
|
+
runtime_cfg = (cfg.get("runtime") or {})
|
|
324
|
+
if _run_configured_command(repo, cfg, "compose_down"):
|
|
325
|
+
return
|
|
326
|
+
compose_args = _compose_base_args(runtime_cfg) + ["down", "--remove-orphans"]
|
|
327
|
+
print(f"[runtime-down] {' '.join(compose_args)}")
|
|
328
|
+
run(compose_args, cwd=repo)
|
|
329
|
+
print("[runtime-down] OK")
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def cmd_wait_health(args: argparse.Namespace) -> None:
|
|
333
|
+
repo = Path(args.repo).resolve()
|
|
334
|
+
cfg = _load_cfg(repo)
|
|
335
|
+
scope = args.scope
|
|
336
|
+
if scope == "runtime":
|
|
337
|
+
runtime_cfg = (cfg.get("runtime") or {})
|
|
338
|
+
url = _join_url(str(runtime_cfg.get("health_base_url") or ""), str(runtime_cfg.get("health_path") or ""))
|
|
339
|
+
timeout_s = int(runtime_cfg.get("startup_timeout_sec") or 120)
|
|
340
|
+
else:
|
|
341
|
+
jenkins_cfg = (cfg.get("jenkins") or {})
|
|
342
|
+
url = _join_url(
|
|
343
|
+
str(jenkins_cfg.get("prod_health_base_url") or ""),
|
|
344
|
+
str(jenkins_cfg.get("prod_health_path") or "")
|
|
345
|
+
)
|
|
346
|
+
timeout_s = int(jenkins_cfg.get("deploy_timeout_sec") or 1800)
|
|
347
|
+
|
|
348
|
+
deadline = time.time() + timeout_s
|
|
349
|
+
last_error = "(none)"
|
|
350
|
+
while time.time() < deadline:
|
|
351
|
+
try:
|
|
352
|
+
status = http_get_status(url, timeout_s=5)
|
|
353
|
+
last_error = f"HTTP {status}"
|
|
354
|
+
if 200 <= status < 400:
|
|
355
|
+
print(f"[wait-health] OK: {scope} {url} -> {status}")
|
|
356
|
+
return
|
|
357
|
+
except Exception as exc: # pragma: no cover - depends on runtime env
|
|
358
|
+
last_error = str(exc)
|
|
359
|
+
time.sleep(2)
|
|
360
|
+
raise APError(f"Health check timeout for {scope}: {url}\nLast result: {last_error}")
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def cmd_verify_jenkins(args: argparse.Namespace) -> None:
|
|
364
|
+
repo = Path(args.repo).resolve()
|
|
365
|
+
cfg = _load_cfg(repo)
|
|
366
|
+
project_cfg = (cfg.get("project") or {})
|
|
367
|
+
jenkins_cfg = (cfg.get("jenkins") or {})
|
|
368
|
+
jenkinsfile = Path(repo, str(project_cfg.get("jenkinsfile") or "Jenkinsfile"))
|
|
369
|
+
if not jenkinsfile.exists():
|
|
370
|
+
raise APError(f"Jenkinsfile not found: {jenkinsfile}")
|
|
371
|
+
|
|
372
|
+
required = [
|
|
373
|
+
("jenkins.job_name", jenkins_cfg.get("job_name")),
|
|
374
|
+
("jenkins.job_url", jenkins_cfg.get("job_url")),
|
|
375
|
+
("jenkins.trigger_branch", jenkins_cfg.get("trigger_branch")),
|
|
376
|
+
("jenkins.image_repository", jenkins_cfg.get("image_repository")),
|
|
377
|
+
("jenkins.image_tag_strategy", jenkins_cfg.get("image_tag_strategy")),
|
|
378
|
+
("jenkins.deploy_env", jenkins_cfg.get("deploy_env")),
|
|
379
|
+
("jenkins.prod_health_base_url", jenkins_cfg.get("prod_health_base_url")),
|
|
380
|
+
("jenkins.prod_health_path", jenkins_cfg.get("prod_health_path")),
|
|
381
|
+
]
|
|
382
|
+
missing = [name for name, value in required if not str(value or "").strip()]
|
|
383
|
+
if missing:
|
|
384
|
+
raise APError("Missing Jenkins config: " + ", ".join(missing))
|
|
385
|
+
print(f"[verify-jenkins] OK: {jenkinsfile}")
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def cmd_verify_jenkins_build(args: argparse.Namespace) -> None:
|
|
389
|
+
repo = Path(args.repo).resolve()
|
|
390
|
+
cfg = _load_cfg(repo)
|
|
391
|
+
jenkins_cfg = (cfg.get("jenkins") or {})
|
|
392
|
+
job_url = str(jenkins_cfg.get("job_url") or "").strip()
|
|
393
|
+
if not job_url:
|
|
394
|
+
raise APError("Missing jenkins.job_url in docs/ENGINEERING.md")
|
|
395
|
+
|
|
396
|
+
git_ref = str(args.git_ref or "HEAD").strip()
|
|
397
|
+
git_short_sha = _resolve_git_short_sha(repo, git_ref)
|
|
398
|
+
max_builds = int(args.max_builds or 20)
|
|
399
|
+
timeout_s = int(args.timeout_sec or 300)
|
|
400
|
+
poll_s = int(args.poll_sec or 5)
|
|
401
|
+
|
|
402
|
+
deadline = time.time() + timeout_s
|
|
403
|
+
matched = None
|
|
404
|
+
api_url = _jenkins_builds_api_url(job_url, max_builds)
|
|
405
|
+
|
|
406
|
+
while time.time() < deadline:
|
|
407
|
+
payload = _jenkins_api_get_json(api_url, cfg)
|
|
408
|
+
builds = payload.get("builds") or []
|
|
409
|
+
matched = next((b for b in builds if git_short_sha in str(b.get("description") or "")), None)
|
|
410
|
+
if matched and not matched.get("building"):
|
|
411
|
+
break
|
|
412
|
+
time.sleep(poll_s)
|
|
413
|
+
|
|
414
|
+
if not matched:
|
|
415
|
+
raise APError(
|
|
416
|
+
f"No Jenkins build found for commit {git_short_sha} under {job_url}. "
|
|
417
|
+
f"Checked latest {max_builds} builds for up to {timeout_s}s."
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
if matched.get("building"):
|
|
421
|
+
raise APError(
|
|
422
|
+
f"Jenkins build for commit {git_short_sha} is still running: "
|
|
423
|
+
f"#{matched.get('number')} {matched.get('url')}"
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
result = str(matched.get("result") or "").strip().upper()
|
|
427
|
+
description = str(matched.get("description") or "").strip()
|
|
428
|
+
if result != "SUCCESS":
|
|
429
|
+
raise APError(
|
|
430
|
+
f"Jenkins build for commit {git_short_sha} did not succeed: "
|
|
431
|
+
f"#{matched.get('number')} result={result or '(empty)'} {matched.get('url')}"
|
|
432
|
+
)
|
|
433
|
+
if not args.allow_no_deploy and description.startswith("no-deploy:"):
|
|
434
|
+
raise APError(
|
|
435
|
+
f"Jenkins build for commit {git_short_sha} succeeded but did not deploy: "
|
|
436
|
+
f"#{matched.get('number')} {description}"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
print(
|
|
440
|
+
"[verify-jenkins-build] OK: "
|
|
441
|
+
f"commit={git_short_sha} "
|
|
442
|
+
f"build=#{matched.get('number')} "
|
|
443
|
+
f"result={result} "
|
|
444
|
+
f"description={description} "
|
|
445
|
+
f"url={matched.get('url')}"
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
185
449
|
def cmd_verify_api_docs(args: argparse.Namespace) -> None:
|
|
186
450
|
"""Ensure API markdown doc and change-log exist."""
|
|
187
451
|
repo = Path(args.repo).resolve()
|
|
@@ -209,9 +473,15 @@ def cmd_commit_push(args: argparse.Namespace) -> None:
|
|
|
209
473
|
if not summary.exists():
|
|
210
474
|
raise APError(
|
|
211
475
|
f"Task summary missing: {summary}\n"
|
|
212
|
-
f"Generate: python3
|
|
476
|
+
f"Generate: python3 docs/tools/autopipeline/ap.py gen-summary {task_id}"
|
|
213
477
|
)
|
|
214
478
|
|
|
479
|
+
if args.require_runtime_health:
|
|
480
|
+
cmd_wait_health(argparse.Namespace(repo=str(repo), scope="runtime"))
|
|
481
|
+
|
|
482
|
+
if args.require_jenkins:
|
|
483
|
+
cmd_verify_jenkins(argparse.Namespace(repo=str(repo)))
|
|
484
|
+
|
|
215
485
|
if args.require_matrix:
|
|
216
486
|
cmd_check_matrix(argparse.Namespace(repo=str(repo)))
|
|
217
487
|
|
|
@@ -222,7 +492,7 @@ def cmd_commit_push(args: argparse.Namespace) -> None:
|
|
|
222
492
|
|
|
223
493
|
run(["git", "commit", "-m", msg], cwd=repo)
|
|
224
494
|
run(["git", "push"], cwd=repo)
|
|
225
|
-
print("[commit-push] OK")
|
|
495
|
+
print("[commit-push] OK - push completed, Jenkins should auto-trigger")
|
|
226
496
|
|
|
227
497
|
|
|
228
498
|
def main(argv: Optional[List[str]] = None) -> int:
|
|
@@ -246,12 +516,35 @@ def main(argv: Optional[List[str]] = None) -> int:
|
|
|
246
516
|
s.add_argument("name")
|
|
247
517
|
s.set_defaults(func=cmd_run)
|
|
248
518
|
|
|
519
|
+
s = sp.add_parser("runtime-up")
|
|
520
|
+
s.set_defaults(func=cmd_runtime_up)
|
|
521
|
+
|
|
522
|
+
s = sp.add_parser("runtime-down")
|
|
523
|
+
s.set_defaults(func=cmd_runtime_down)
|
|
524
|
+
|
|
525
|
+
s = sp.add_parser("wait-health")
|
|
526
|
+
s.add_argument("--scope", choices=["runtime", "prod"], default="runtime")
|
|
527
|
+
s.set_defaults(func=cmd_wait_health)
|
|
528
|
+
|
|
529
|
+
s = sp.add_parser("verify-jenkins")
|
|
530
|
+
s.set_defaults(func=cmd_verify_jenkins)
|
|
531
|
+
|
|
532
|
+
s = sp.add_parser("verify-jenkins-build")
|
|
533
|
+
s.add_argument("--git-ref", default="HEAD")
|
|
534
|
+
s.add_argument("--max-builds", type=int, default=20)
|
|
535
|
+
s.add_argument("--timeout-sec", type=int, default=300)
|
|
536
|
+
s.add_argument("--poll-sec", type=int, default=5)
|
|
537
|
+
s.add_argument("--allow-no-deploy", action="store_true")
|
|
538
|
+
s.set_defaults(func=cmd_verify_jenkins_build)
|
|
539
|
+
|
|
249
540
|
s = sp.add_parser("verify-api-docs")
|
|
250
541
|
s.set_defaults(func=cmd_verify_api_docs)
|
|
251
542
|
|
|
252
543
|
s = sp.add_parser("commit-push")
|
|
253
544
|
s.add_argument("task_id")
|
|
254
545
|
s.add_argument("--msg", required=True)
|
|
546
|
+
s.add_argument("--require-runtime-health", action="store_true")
|
|
547
|
+
s.add_argument("--require-jenkins", action="store_true")
|
|
255
548
|
s.add_argument("--require-matrix", action="store_true")
|
|
256
549
|
s.set_defaults(func=cmd_commit_push)
|
|
257
550
|
|
|
@@ -104,7 +104,7 @@ def find_config(repo: Path) -> Path:
|
|
|
104
104
|
return c
|
|
105
105
|
raise APError(
|
|
106
106
|
"Project config not found. Create docs/ENGINEERING.md "
|
|
107
|
-
"and put commands + runtime fields in YAML frontmatter."
|
|
107
|
+
"and put commands + runtime + jenkins fields in YAML frontmatter."
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import urllib.error
|
|
7
|
+
import urllib.request
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CheckError(RuntimeError):
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def fetch(method: str, url: str, headers: dict[str, str] | None = None) -> tuple[int, str]:
|
|
15
|
+
req = urllib.request.Request(url, headers=headers or {}, method=method)
|
|
16
|
+
try:
|
|
17
|
+
with urllib.request.urlopen(req, timeout=10) as response:
|
|
18
|
+
return response.status, response.read().decode("utf-8", errors="replace")
|
|
19
|
+
except urllib.error.HTTPError as exc:
|
|
20
|
+
return exc.code, exc.read().decode("utf-8", errors="replace")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cmd_status(args: argparse.Namespace) -> None:
|
|
24
|
+
status, body = fetch("GET", args.url)
|
|
25
|
+
if status != args.expect:
|
|
26
|
+
raise CheckError(f"GET {args.url} expected {args.expect}, got {status}: {body[:400]}")
|
|
27
|
+
print(f"[status] OK: {args.url} -> {status}")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def cmd_text_contains(args: argparse.Namespace) -> None:
|
|
31
|
+
status, body = fetch("GET", args.url)
|
|
32
|
+
if status != args.expect:
|
|
33
|
+
raise CheckError(f"GET {args.url} expected {args.expect}, got {status}: {body[:400]}")
|
|
34
|
+
if args.contains not in body:
|
|
35
|
+
raise CheckError(f"Response from {args.url} does not contain expected text: {args.contains}")
|
|
36
|
+
print(f"[text-contains] OK: {args.url}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cmd_json_field(args: argparse.Namespace) -> None:
|
|
40
|
+
status, body = fetch("GET", args.url, headers={"Accept": "application/json"})
|
|
41
|
+
if status != args.expect:
|
|
42
|
+
raise CheckError(f"GET {args.url} expected {args.expect}, got {status}: {body[:400]}")
|
|
43
|
+
try:
|
|
44
|
+
payload = json.loads(body)
|
|
45
|
+
except json.JSONDecodeError as exc:
|
|
46
|
+
raise CheckError(f"Response from {args.url} is not valid JSON: {exc}") from exc
|
|
47
|
+
|
|
48
|
+
current = payload
|
|
49
|
+
for part in args.path.split("."):
|
|
50
|
+
if isinstance(current, dict) and part in current:
|
|
51
|
+
current = current[part]
|
|
52
|
+
continue
|
|
53
|
+
raise CheckError(f"JSON path not found: {args.path}")
|
|
54
|
+
|
|
55
|
+
if str(current) != args.equals:
|
|
56
|
+
raise CheckError(f"JSON field {args.path} expected {args.equals}, got {current}")
|
|
57
|
+
print(f"[json-field] OK: {args.url} {args.path}={current}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def main() -> int:
|
|
61
|
+
parser = argparse.ArgumentParser(prog="http_checks")
|
|
62
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
63
|
+
|
|
64
|
+
s = sub.add_parser("status")
|
|
65
|
+
s.add_argument("url")
|
|
66
|
+
s.add_argument("--expect", type=int, default=200)
|
|
67
|
+
s.set_defaults(func=cmd_status)
|
|
68
|
+
|
|
69
|
+
s = sub.add_parser("text-contains")
|
|
70
|
+
s.add_argument("url")
|
|
71
|
+
s.add_argument("--contains", required=True)
|
|
72
|
+
s.add_argument("--expect", type=int, default=200)
|
|
73
|
+
s.set_defaults(func=cmd_text_contains)
|
|
74
|
+
|
|
75
|
+
s = sub.add_parser("json-field")
|
|
76
|
+
s.add_argument("url")
|
|
77
|
+
s.add_argument("--path", required=True)
|
|
78
|
+
s.add_argument("--equals", required=True)
|
|
79
|
+
s.add_argument("--expect", type=int, default=200)
|
|
80
|
+
s.set_defaults(func=cmd_json_field)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
args = parser.parse_args()
|
|
84
|
+
args.func(args)
|
|
85
|
+
return 0
|
|
86
|
+
except CheckError as exc:
|
|
87
|
+
print(f"[ERROR] {exc}")
|
|
88
|
+
return 2
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
raise SystemExit(main())
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elvis1513/auto-coding-skill",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "CLI installer for auto-coding-skill (Claude Code + Codex CLI).",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "CLI installer for auto-coding-skill (Claude Code + Codex CLI) with Go fullstack + Jenkins workflow support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"autocoding": "cli/src/index.js"
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
"engineering",
|
|
30
30
|
"workflow",
|
|
31
31
|
"ci",
|
|
32
|
+
"jenkins",
|
|
33
|
+
"go",
|
|
34
|
+
"monorepo",
|
|
32
35
|
"docker",
|
|
33
36
|
"local-testing"
|
|
34
37
|
],
|