@namewta/speculo 0.1.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/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +58 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +60 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +11 -0
- package/dist/src/utils.js.map +1 -0
- package/package.json +54 -0
- package/speculo/.speculo/.config/LESSONS.md +9 -0
- package/speculo/.speculo/.config/RULES.md +9 -0
- package/speculo/.speculo/.config/adr/.gitkeep +1 -0
- package/speculo/.speculo/.config/context/.gitkeep +1 -0
- package/speculo/.speculo/archive/dev/.gitkeep +0 -0
- package/speculo/.speculo/archive/doc/.gitkeep +1 -0
- package/speculo/.speculo/commands/.gitkeep +0 -0
- package/speculo/.speculo/dev/.gitkeep +0 -0
- package/speculo/.speculo/dev/docs-sync-state.json +14 -0
- package/speculo/.speculo/dev-status.json +3 -0
- package/speculo/.speculo/doc/.gitkeep +1 -0
- package/speculo/.speculo/doc-status.json +3 -0
- package/speculo/commands/archive.md +53 -0
- package/speculo/commands/caveman.md +43 -0
- package/speculo/commands/grill-me.md +42 -0
- package/speculo/commands/handoff.md +42 -0
- package/speculo/commands/scaffold-exercises.md +50 -0
- package/speculo/commands/status.md +51 -0
- package/speculo/commands/write-a-skill.md +46 -0
- package/speculo/skills/caveman/SKILL.md +38 -0
- package/speculo/skills/caveman/references/compression-rules.md +102 -0
- package/speculo/skills/github-npm-ops/SKILL.md +53 -0
- package/speculo/skills/github-npm-ops/references/ci-and-security-ops.md +178 -0
- package/speculo/skills/github-npm-ops/references/failure-recovery.md +132 -0
- package/speculo/skills/github-npm-ops/references/issue-pr-triage.md +219 -0
- package/speculo/skills/github-npm-ops/references/package-json-checklist.md +171 -0
- package/speculo/skills/github-npm-ops/references/preflight-checklist.md +39 -0
- package/speculo/skills/github-npm-ops/references/publish-detection.md +68 -0
- package/speculo/skills/github-npm-ops/references/release-notes-injection.md +236 -0
- package/speculo/skills/github-npm-ops/references/release-pipeline.md +108 -0
- package/speculo/skills/github-npm-ops/references/setup-npm-token.md +63 -0
- package/speculo/skills/github-npm-ops/references/troubleshooting-playbook.md +305 -0
- package/speculo/skills/github-npm-ops/references/version-bump-flow.md +232 -0
- package/speculo/skills/github-npm-ops/references/workflow-yaml-reference.md +268 -0
- package/speculo/skills/grill-me/SKILL.md +40 -0
- package/speculo/skills/handoff/SKILL.md +41 -0
- package/speculo/skills/scaffold-exercises/SKILL.md +41 -0
- package/speculo/skills/scaffold-exercises/references/exercise-structure.md +85 -0
- package/speculo/skills/scaffold-exercises/references/lint-and-git.md +54 -0
- package/speculo/skills/speculo-write/SKILL.md +53 -0
- package/speculo/skills/speculo-write/references/asset-selection-sop.md +65 -0
- package/speculo/skills/speculo-write/references/command-authoring-sop.md +92 -0
- package/speculo/skills/speculo-write/references/migration-sop.md +101 -0
- package/speculo/skills/speculo-write/references/persistence-contract-sop.md +123 -0
- package/speculo/skills/speculo-write/references/skill-authoring-sop.md +195 -0
- package/speculo/skills/speculo-write/references/validation-checklist.md +73 -0
- package/speculo/skills/speculo-write/references/workflow-authoring-sop.md +130 -0
- package/speculo/workflows/dev/00-INDEX.md +56 -0
- package/speculo/workflows/dev/01-grill-with-docs/01-grill-with-docs.md +79 -0
- package/speculo/workflows/dev/01-grill-with-docs/ADR-FORMAT.md +49 -0
- package/speculo/workflows/dev/01-grill-with-docs/CONTEXT-FORMAT.md +65 -0
- package/speculo/workflows/dev/01-grill-with-docs/grill-context-scan.md +30 -0
- package/speculo/workflows/dev/01-grill-with-docs/grill-decision.md +38 -0
- package/speculo/workflows/dev/02-prd/02-prd.md +64 -0
- package/speculo/workflows/dev/02-prd/prd-synthesis.md +30 -0
- package/speculo/workflows/dev/02-prd/prd-zoom-out.md +29 -0
- package/speculo/workflows/dev/03-tdd/03-tdd.md +80 -0
- package/speculo/workflows/dev/03-tdd/deep-modules.md +33 -0
- package/speculo/workflows/dev/03-tdd/interface-design.md +31 -0
- package/speculo/workflows/dev/03-tdd/mocking.md +60 -0
- package/speculo/workflows/dev/03-tdd/refactoring.md +10 -0
- package/speculo/workflows/dev/03-tdd/tdd-finish.md +28 -0
- package/speculo/workflows/dev/03-tdd/tdd-loop.md +33 -0
- package/speculo/workflows/dev/03-tdd/tdd-plan.md +30 -0
- package/speculo/workflows/dev/03-tdd/tests.md +61 -0
- package/speculo/workflows/dev/D-docs-sync/D-docs-sync.md +97 -0
- package/speculo/workflows/dev/D-docs-sync/agents-contract.md +95 -0
- package/speculo/workflows/dev/D-docs-sync/changelog-contract.md +155 -0
- package/speculo/workflows/dev/D-docs-sync/docs-sync-diff.md +50 -0
- package/speculo/workflows/dev/D-docs-sync/docs-sync-finish.md +33 -0
- package/speculo/workflows/dev/D-docs-sync/docs-sync-state.md +32 -0
- package/speculo/workflows/dev/D-docs-sync/docs-sync-update.md +35 -0
- package/speculo/workflows/dev/D-docs-sync/readme-contract.md +124 -0
- package/speculo/workflows/dev/D-docs-sync/state-json-schema.md +155 -0
- package/speculo/workflows/dev/H-diagnose/H-diagnose.md +80 -0
- package/speculo/workflows/dev/H-diagnose/diagnose-fix.md +34 -0
- package/speculo/workflows/dev/H-diagnose/diagnose-guide.md +114 -0
- package/speculo/workflows/dev/H-diagnose/diagnose-loop.md +32 -0
- package/speculo/workflows/dev/H-diagnose/scripts/hitl-loop.template.sh +41 -0
- package/speculo/workflows/dev/I-to-issues/I-to-issues.md +70 -0
- package/speculo/workflows/dev/I-to-issues/issues-slices.md +31 -0
- package/speculo/workflows/dev/R-review/R-review.md +82 -0
- package/speculo/workflows/dev/R-review/review-setup.md +39 -0
- package/speculo/workflows/dev/R-review/review-two-axis.md +33 -0
- package/speculo/workflows/dev/_templates/diagnosis-template.md +19 -0
- package/speculo/workflows/dev/_templates/docs-sync-report-template.md +28 -0
- package/speculo/workflows/dev/_templates/docs-sync-state-template.json +14 -0
- package/speculo/workflows/dev/_templates/grill-context-map-template.md +19 -0
- package/speculo/workflows/dev/_templates/grill-decision-log-template.md +19 -0
- package/speculo/workflows/dev/_templates/issues-slices-template.md +19 -0
- package/speculo/workflows/dev/_templates/prd-overview-template.md +19 -0
- package/speculo/workflows/dev/_templates/prd-template.md +25 -0
- package/speculo/workflows/dev/_templates/regression-template.md +19 -0
- package/speculo/workflows/dev/_templates/review-report-template.md +16 -0
- package/speculo/workflows/dev/_templates/review-sources-template.md +24 -0
- package/speculo/workflows/dev/_templates/tdd-log-template.md +16 -0
- package/speculo/workflows/dev/_templates/tdd-plan-template.md +19 -0
- package/speculo/workflows/dev/_templates/tdd-verification-template.md +16 -0
- package/speculo/workflows/doc/00-INDEX.md +51 -0
- package/speculo/workflows/doc/B-writing-beats/B-writing-beats.md +77 -0
- package/speculo/workflows/doc/B-writing-beats/writing-beats-append.md +31 -0
- package/speculo/workflows/doc/B-writing-beats/writing-beats-options.md +29 -0
- package/speculo/workflows/doc/E-edit-article/E-edit-article.md +77 -0
- package/speculo/workflows/doc/E-edit-article/edit-article-plan.md +30 -0
- package/speculo/workflows/doc/E-edit-article/edit-article-rewrite.md +31 -0
- package/speculo/workflows/doc/F-writing-fragments/F-writing-fragments.md +78 -0
- package/speculo/workflows/doc/F-writing-fragments/writing-fragments-interview.md +32 -0
- package/speculo/workflows/doc/F-writing-fragments/writing-fragments-log.md +29 -0
- package/speculo/workflows/doc/S-writing-shape/S-writing-shape.md +79 -0
- package/speculo/workflows/doc/S-writing-shape/writing-shape-block.md +32 -0
- package/speculo/workflows/doc/S-writing-shape/writing-shape-opening.md +27 -0
- package/speculo/workflows/doc/_templates/edit-article-plan-template.md +24 -0
- package/speculo/workflows/doc/_templates/edit-article-template.md +6 -0
- package/speculo/workflows/doc/_templates/writing-article-template.md +6 -0
- package/speculo/workflows/doc/_templates/writing-beat-options-template.md +20 -0
- package/speculo/workflows/doc/_templates/writing-fragments-template.md +6 -0
- package/speculo/workflows/doc/_templates/writing-interview-log-template.md +20 -0
- package/speculo/workflows/doc/_templates/writing-shape-log-template.md +24 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Release 故障排查手册
|
|
2
|
+
|
|
3
|
+
基于真实失败记录的排查流程。遇到问题先按这个顺序走。
|
|
4
|
+
|
|
5
|
+
## 统一诊断流程
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. 找到失败的 run
|
|
9
|
+
gh run list --workflow release.yml --limit 5 \
|
|
10
|
+
--json databaseId,conclusion,displayTitle
|
|
11
|
+
|
|
12
|
+
# 2. 查看失败步骤的日志
|
|
13
|
+
gh run view <databaseId> --log-failed 2>&1 \
|
|
14
|
+
| grep -E "npm error|npm warn publish|##\[error\]|Error:" \
|
|
15
|
+
| head -30
|
|
16
|
+
|
|
17
|
+
# 3. 按下面的错误表对应修复
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 错误分类与修复
|
|
21
|
+
|
|
22
|
+
### 1. `E404 Not Found - PUT /<pkg>`
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
npm error code E404
|
|
26
|
+
npm error 404 Not Found - PUT https://registry.npmjs.org/my-cli
|
|
27
|
+
npm error 404 The requested resource 'my-cli@0.0.2' could not be found
|
|
28
|
+
or you do not have permission to access it.
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**两种原因:**
|
|
32
|
+
|
|
33
|
+
- **包名被占用**:别人已注册这个名字,你没权限写入
|
|
34
|
+
- **私有包但未创建 scope/org**:scope 不存在
|
|
35
|
+
|
|
36
|
+
**诊断:**
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm view <pkg-name> # 有内容说明被占
|
|
40
|
+
npm owner ls <pkg-name> # 看当前 owner
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**修复:**
|
|
44
|
+
|
|
45
|
+
- 改用 scoped 包名:`@yourname/pkg-name`
|
|
46
|
+
- 同步更新 `package.json` 的 `name` 字段
|
|
47
|
+
- 若要用现有名字:联系当前 owner 做 transfer,或走 `npm support`
|
|
48
|
+
|
|
49
|
+
### 2. `E403 Two-factor authentication required`
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
npm error code E403
|
|
53
|
+
npm error 403 403 Forbidden - PUT https://registry.npmjs.org/@scope%2fmy-cli
|
|
54
|
+
- Two-factor authentication or granular access token
|
|
55
|
+
with bypass 2fa enabled is required to publish packages.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**原因:** 使用了 Classic Token 或未开启 2FA bypass 的 Granular Token。
|
|
59
|
+
|
|
60
|
+
**修复:**
|
|
61
|
+
|
|
62
|
+
1. npm → Access Tokens → **Generate New Token** → **Granular Access Token**
|
|
63
|
+
2. 勾选 **Bypass two-factor authentication**
|
|
64
|
+
3. 选择目标包或 scope,permission 为 **Read and write**
|
|
65
|
+
4. 复制新 token,更新 GitHub Secret `NPM_TOKEN`
|
|
66
|
+
5. 重新触发 workflow(删旧 tag 重推)
|
|
67
|
+
|
|
68
|
+
详见 [setup-npm-token.md](./setup-npm-token.md)。
|
|
69
|
+
|
|
70
|
+
### 3. `EUSAGE Can't generate provenance for new or private package`
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
npm error code EUSAGE
|
|
74
|
+
npm error Can't generate provenance for new or private package,
|
|
75
|
+
you must set `access` to public.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**原因:** Scoped 包默认 private,首次发布未加 `--access public`。
|
|
79
|
+
|
|
80
|
+
**修复:**
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
- run: npm publish --provenance --access public
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 4. `E422 Error verifying sigstore provenance bundle`
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
npm error code E422
|
|
90
|
+
npm error 422 Unprocessable Entity - PUT https://registry.npmjs.org/@scope%2fmy-cli
|
|
91
|
+
- Error verifying sigstore provenance bundle:
|
|
92
|
+
Failed to validate repository information:
|
|
93
|
+
package.json: "repository.url" is "", expected to match
|
|
94
|
+
"https://github.com/owner/my-cli" from provenance
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**原因:** `package.json` 缺 `repository.url`,或值与实际 GitHub 仓库不匹配。
|
|
98
|
+
|
|
99
|
+
**修复:**
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{
|
|
103
|
+
"repository": {
|
|
104
|
+
"type": "git",
|
|
105
|
+
"url": "https://github.com/owner/my-cli"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- URL 不要带 `.git` 后缀也能通过(npm 会归一化)
|
|
111
|
+
- 注意大小写:虽然 GitHub 不区分,但 provenance 严格校验,保持与 workflow 运行环境一致
|
|
112
|
+
|
|
113
|
+
### 5. `Tag version does not match package.json version`
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
Tag version (0.0.2) does not match package.json version (0.0.1)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**原因:** 打 tag 前忘记 bump `package.json`,或 bump 后没 commit。
|
|
120
|
+
|
|
121
|
+
**修复:**
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 删除错误 tag
|
|
125
|
+
git tag -d v0.0.2
|
|
126
|
+
git push origin :refs/tags/v0.0.2
|
|
127
|
+
|
|
128
|
+
# 修复 package.json
|
|
129
|
+
npm version 0.0.2 --no-git-tag-version # 只改文件,不打 tag
|
|
130
|
+
git add package.json && git commit -m "chore: bump version to 0.0.2"
|
|
131
|
+
git push origin main
|
|
132
|
+
|
|
133
|
+
# 重新打 tag
|
|
134
|
+
git tag v0.0.2
|
|
135
|
+
git push origin v0.0.2
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 6. `npm warn publish "bin[name]" script name ... was invalid and removed`
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
npm warn publish "bin[my-cli]" script name dist/cli/index.js was invalid and removed
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**原因:** `bin` 路径没有 `./` 前缀,或 npm 版本对该路径格式敏感。
|
|
145
|
+
|
|
146
|
+
**修复:** 确认 `package.json` 已经是 `"./dist/cli/index.js"`。如果已经正确但警告仍在,可能是 npm 的误报——检查 npm publish 最终有没有返回 `+ @scope/pkg@version`,有就成功了。
|
|
147
|
+
|
|
148
|
+
### 7. Workflow 根本没触发
|
|
149
|
+
|
|
150
|
+
**诊断:**
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
gh run list --workflow release.yml --limit 3
|
|
154
|
+
# 如果最新的那个不在列表里,就是没触发
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**原因:**
|
|
158
|
+
|
|
159
|
+
- Tag 名不匹配 `v*` 模式
|
|
160
|
+
- Tag 已存在,再次 push 被 Git 拒绝
|
|
161
|
+
- 仓库关闭了 Actions
|
|
162
|
+
|
|
163
|
+
**修复:**
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 确认 tag 推到远端了
|
|
167
|
+
git ls-remote --tags origin | grep v0.0.2
|
|
168
|
+
|
|
169
|
+
# 如果旧 tag 残留,先删
|
|
170
|
+
git tag -d v0.0.2
|
|
171
|
+
git push origin :refs/tags/v0.0.2
|
|
172
|
+
|
|
173
|
+
# 重新打
|
|
174
|
+
git tag v0.0.2
|
|
175
|
+
git push origin v0.0.2
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 8. 安装 pnpm 或 Node 失败
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
Error: Unable to find 'pnpm' executable
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**修复:** 在 setup 里加:
|
|
185
|
+
|
|
186
|
+
```yaml
|
|
187
|
+
- uses: pnpm/action-setup@v2
|
|
188
|
+
with:
|
|
189
|
+
version: 9 # 匹配 packageManager 字段或 lockfile 格式
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
如果 `package.json` 里写了 `packageManager: "pnpm@9.x.x"`,`pnpm/action-setup` 可不传 `version`,它会自动读取。
|
|
193
|
+
|
|
194
|
+
### 9. `npm ERR! 403 Forbidden - PUT` 但 token 正常
|
|
195
|
+
|
|
196
|
+
可能是包名大小写问题。npm 内部是小写,但 `package.json` 里写了大写:
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{ "name": "MyCli" } // ❌
|
|
200
|
+
{ "name": "my-cli" } // ✅
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 10. GitHub Release 正文为空 / 仅一行 `Full Changelog`
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
Release v0.0.10 (Latest)
|
|
207
|
+
Full Changelog: v0.0.9...v0.0.10
|
|
208
|
+
[Source code (zip)] [Source code (tar.gz)]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**原因**:`softprops/action-gh-release@v2` 只设了 `generate_release_notes: true`,而 GitHub 自动生成器从 PR 标题 + label 抽取 changelog。直 push main 的仓库没有 PR 流量,生成器只剩 compare 链接可写。
|
|
212
|
+
|
|
213
|
+
**修复(workflow 改造)**:在 Create GitHub Release 之前加一步从 `CHANGELOGS.md` 抽取段落,再用 `body_path` 注入。完整 YAML 见 [workflow-yaml-reference.md](workflow-yaml-reference.md) 的 `Extract release notes from CHANGELOGS.md` 步骤;模式说明见 [release-notes-injection.md](release-notes-injection.md)。
|
|
214
|
+
|
|
215
|
+
**修复(事后回填历史 Release,无需重发版)**:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
VERSION="0.0.10"
|
|
219
|
+
awk -v v="$VERSION" '
|
|
220
|
+
$0 ~ "^## \\["v"\\]" { found=1; print; next }
|
|
221
|
+
found && /^## \[/ { exit }
|
|
222
|
+
found && /^---[[:space:]]*$/ { next }
|
|
223
|
+
found { print }
|
|
224
|
+
' CHANGELOGS.md > /tmp/notes.md && \
|
|
225
|
+
gh release edit "v$VERSION" --notes-file /tmp/notes.md && \
|
|
226
|
+
rm /tmp/notes.md
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**诊断**:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Release body 长度过短就是出问题了
|
|
233
|
+
gh release view v0.0.10 --json body --jq '.body | length'
|
|
234
|
+
|
|
235
|
+
# workflow 里 Extract release notes 步骤是否存在并 success
|
|
236
|
+
gh run view <run-id> --json jobs \
|
|
237
|
+
--jq '.jobs[].steps[] | select(.name | test("Extract|Release"))'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 11. `Extract release notes` 步骤报 "release-notes.md is empty"
|
|
241
|
+
|
|
242
|
+
**根因**:CHANGELOG 段落标题与 tag 不匹配,awk 抽空。
|
|
243
|
+
|
|
244
|
+
**自查**:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
# 实际 CHANGELOG 标题
|
|
248
|
+
grep '^## \[' CHANGELOGS.md | head -5
|
|
249
|
+
|
|
250
|
+
# 当前 tag 应当对应的标题
|
|
251
|
+
echo "## [${GITHUB_REF_NAME#v}]"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
常见问题:
|
|
255
|
+
|
|
256
|
+
- 全角中括号 `【0.0.10】` 而非半角 `[0.0.10]`
|
|
257
|
+
- 版本号有 `v` 前缀(`## [v0.0.10]`)— awk 期望去前缀的纯数字
|
|
258
|
+
- 段落写错位置(`[Unreleased]` 段还没迁移到正式版本段)
|
|
259
|
+
|
|
260
|
+
## 发布成功但找不到包
|
|
261
|
+
|
|
262
|
+
典型场景:workflow 显示绿色,但 `npm view @scope/pkg` 返回 404。
|
|
263
|
+
|
|
264
|
+
- **传播延迟**:npm CDN 有 30–60 秒缓存延迟,等一下再查
|
|
265
|
+
- **日志确认**:`gh run view <id> --log | grep "+ @scope"`——有 `+ @scope/pkg@version` 才算真的成功
|
|
266
|
+
- **provenance 页面**:https://search.sigstore.dev/ 能查到签名日志
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# 等待 + 轮询
|
|
270
|
+
for i in {1..10}; do
|
|
271
|
+
npm view @scope/my-cli version 2>/dev/null && break
|
|
272
|
+
sleep 10
|
|
273
|
+
done
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## 已发布但需要撤回
|
|
277
|
+
|
|
278
|
+
npm **24 小时内**可以 `unpublish`(仅限无依赖项):
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
npm unpublish @scope/my-cli@0.0.2
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
超过 24 小时或有依赖者只能 `deprecate`:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm deprecate @scope/my-cli@0.0.2 "Security issue, use 0.0.3+"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**npm 不允许重发同一个 version**——即使 unpublish 过了。必须 bump 版本号。
|
|
291
|
+
|
|
292
|
+
## 排查 checklist
|
|
293
|
+
|
|
294
|
+
按顺序检查,90% 的问题在前三项:
|
|
295
|
+
|
|
296
|
+
1. [ ] `NPM_TOKEN` 是 Granular Token、bypass 2FA、包含目标 scope 的 read+write
|
|
297
|
+
2. [ ] `package.json` 有 `repository.url` 且匹配 GitHub repo
|
|
298
|
+
3. [ ] Workflow 含 `permissions: id-token: write` 和 `--access public`
|
|
299
|
+
4. [ ] Tag 版本与 `package.json` version 字符一致
|
|
300
|
+
5. [ ] Publish step 在 Release step 之前
|
|
301
|
+
6. [ ] Workflow 触发器是 `on.push.tags: ['v*']`,没写 branches
|
|
302
|
+
7. [ ] `bin` 路径以 `./` 开头
|
|
303
|
+
8. [ ] `files` 字段不排除构建产物
|
|
304
|
+
9. [ ] **Workflow 包含 `Extract release notes from CHANGELOGS.md` 步骤,且 `Create GitHub Release` 设了 `body_path: release-notes.md`**
|
|
305
|
+
10. [ ] **CHANGELOGS.md 顶部有当前版本对应的 `## [X.Y.Z]` 段落(半角中括号)**
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# 版本 bump → release commit → tag → push 端到端 SOP
|
|
2
|
+
|
|
3
|
+
把"想发个新版本"翻译成可复现的几条命令。本文档假设仓库已有 `release.yml`(`v*` tag 触发,含 release-notes 注入)。
|
|
4
|
+
|
|
5
|
+
## 0. 前置检查
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. main 干净,没有未提交改动
|
|
9
|
+
git status
|
|
10
|
+
|
|
11
|
+
# 2. 当前 HEAD 与 origin/main 的关系
|
|
12
|
+
git log origin/main..HEAD --oneline
|
|
13
|
+
|
|
14
|
+
# 3. 上一次发布的 tag
|
|
15
|
+
git tag --sort=-version:refname | head -3
|
|
16
|
+
|
|
17
|
+
# 4. CHANGELOG 顶部 [Unreleased] 段落已写好本次内容
|
|
18
|
+
head -30 CHANGELOGS.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
任意一项异常先解决再发版:工作区不干净 → 先提交或暂存;`[Unreleased]` 为空 → 先做 docs-sync。
|
|
22
|
+
|
|
23
|
+
## 1. 维护 CHANGELOG
|
|
24
|
+
|
|
25
|
+
`CHANGELOGS.md` 顶部必须始终保留 `[Unreleased]` 段落。新版本发布时:
|
|
26
|
+
|
|
27
|
+
1. 把 `[Unreleased]` 内容**整段迁移**到新建的 `## [X.Y.Z] — YYYY-MM-DD` 标题下
|
|
28
|
+
2. 重新插入空的 `[Unreleased]` 段落
|
|
29
|
+
3. 在文件末尾的"版本链接"段更新 compare 链接
|
|
30
|
+
|
|
31
|
+
```diff
|
|
32
|
+
## [Unreleased]
|
|
33
|
+
+
|
|
34
|
+
+---
|
|
35
|
+
+
|
|
36
|
+
+## [0.0.10] — 2026-05-17
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- ...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```diff
|
|
43
|
+
-- [Unreleased](https://github.com/owner/repo/compare/v0.0.9...HEAD)
|
|
44
|
+
+- [Unreleased](https://github.com/owner/repo/compare/v0.0.10...HEAD)
|
|
45
|
+
+- [0.0.10](https://github.com/owner/repo/releases/tag/v0.0.10)
|
|
46
|
+
- [0.0.9](https://github.com/owner/repo/releases/tag/v0.0.9)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
完整 CHANGELOG 契约见 [docs-sync skill](../../docs-sync/SKILL.md) 或 docs-sync 的 changelog-contract.md。
|
|
50
|
+
|
|
51
|
+
## 2. bump package.json
|
|
52
|
+
|
|
53
|
+
**用 `npm version` 还是手改?**
|
|
54
|
+
|
|
55
|
+
| 场景 | 推荐 |
|
|
56
|
+
|------|------|
|
|
57
|
+
| 单一 package.json,简单 bump | `npm version <major\|minor\|patch> --no-git-tag-version` |
|
|
58
|
+
| Monorepo / 多文件版本号要同步 | 手改 + `pnpm -r`(或脚本) |
|
|
59
|
+
| 已写好 CHANGELOG,只想改 package.json 一处 | 手改 |
|
|
60
|
+
|
|
61
|
+
`--no-git-tag-version` 的关键作用:**只改文件,不自动打 tag**。我们要把 tag 打在 release commit 上,不是在 bump 这一刻。
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# 等价于手改 package.json 的 version 字段
|
|
65
|
+
npm version 0.0.10 --no-git-tag-version
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 3. release commit(原子提交)
|
|
69
|
+
|
|
70
|
+
把 `package.json` + `CHANGELOGS.md` 一并提交,使用 Conventional Commits 的 `chore(release):` 前缀。
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
git add package.json CHANGELOGS.md
|
|
74
|
+
git commit -m "chore(release): v0.0.10" \
|
|
75
|
+
-m "迁移 [Unreleased] 段落到 0.0.10:<一两句关键变更摘要>"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**为什么必须是同一个 commit**:
|
|
79
|
+
- tag 要精确指向这个 commit
|
|
80
|
+
- workflow 的 `Verify tag matches package version` 步骤会校验该 commit 上的 `package.json` 版本与 tag 一致
|
|
81
|
+
- 利于后续 `git revert` 一键回滚发布
|
|
82
|
+
|
|
83
|
+
## 4. push main
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git push origin main
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
push **必须先于** tag,否则远端 tag 会指向 origin 看不到的 commit,GitHub Release 创建时会找不到对应 commit 树。
|
|
90
|
+
|
|
91
|
+
## 5. 打 tag 并精确指向 release commit
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# 显式指定 commit SHA,避免 HEAD 已经向前移动
|
|
95
|
+
RELEASE_COMMIT=$(git log -1 --format=%H --grep="^chore(release): v0.0.10")
|
|
96
|
+
|
|
97
|
+
git tag v0.0.10 "$RELEASE_COMMIT"
|
|
98
|
+
git push origin v0.0.10
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**为什么要指定 SHA 而不是直接 `git tag v0.0.10`**:
|
|
102
|
+
|
|
103
|
+
如果 release commit 之后又做了几笔 docs / state 推进 commit,`HEAD` 不再是 release commit。直接 `git tag` 会把 tag 打在 docs commit 上,导致:
|
|
104
|
+
|
|
105
|
+
- `Verify tag matches package version` 仍然通过(版本号对得上)
|
|
106
|
+
- 但 `gh release view --json targetCommitish` 指向 docs commit
|
|
107
|
+
- 长远看 release tag 与"真正的发布动作"脱钩
|
|
108
|
+
|
|
109
|
+
简单场景下(release commit 就是 HEAD)直接 `git tag v0.0.10` 也对,但养成显式 SHA 的习惯更稳。
|
|
110
|
+
|
|
111
|
+
## 6. 监控 workflow
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# 实时跟踪
|
|
115
|
+
gh run watch
|
|
116
|
+
|
|
117
|
+
# 或周期性查询
|
|
118
|
+
gh run list --workflow release.yml --limit 3 \
|
|
119
|
+
--json databaseId,status,conclusion,displayTitle,headBranch
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
预期顺序(11 步,缺一不可):
|
|
123
|
+
|
|
124
|
+
1. Set up job
|
|
125
|
+
2. Checkout
|
|
126
|
+
3. Setup environment
|
|
127
|
+
4. **Verify tag matches package version**
|
|
128
|
+
5. Run lint
|
|
129
|
+
6. Run tests
|
|
130
|
+
7. Build
|
|
131
|
+
8. **Verify bin entry**
|
|
132
|
+
9. **Extract release notes from CHANGELOGS.md** ← v1.1.0 新增
|
|
133
|
+
10. **Publish to npm**
|
|
134
|
+
11. **Create GitHub Release**
|
|
135
|
+
|
|
136
|
+
## 7. 三端验证
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# 1. npm 包已上线(CDN 30–60 秒延迟)
|
|
140
|
+
npm view @scope/pkg version
|
|
141
|
+
npm view @scope/pkg dist-tags
|
|
142
|
+
|
|
143
|
+
# 2. GitHub Release 存在 + 正文非空
|
|
144
|
+
gh release view v0.0.10 --json name,tagName,isDraft,isPrerelease,body \
|
|
145
|
+
--jq '{name, tagName, isDraft, isPrerelease, body_length: (.body | length), body_preview: (.body | .[0:200])}'
|
|
146
|
+
|
|
147
|
+
# 3. workflow conclusion=success 且所有步骤 success
|
|
148
|
+
gh run view <run-id> --json conclusion,jobs \
|
|
149
|
+
--jq '{conclusion, steps: [.jobs[].steps[] | {name, conclusion}]}'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
任一缺失视为发布残次品:
|
|
153
|
+
|
|
154
|
+
| 缺失项 | 修复 |
|
|
155
|
+
|--------|------|
|
|
156
|
+
| npm 包查不到 | 等 60 秒再查;仍无 → 看 workflow `Publish to npm` 步骤日志 |
|
|
157
|
+
| Release 正文为空 / 仅一行 Full Changelog | 跑 [release-notes-injection.md](release-notes-injection.md) §3 的事后修复 |
|
|
158
|
+
| workflow `Extract release notes` 失败 | CHANGELOG 标题与 tag 不匹配,核对 `## [X.Y.Z]` 是否存在 |
|
|
159
|
+
|
|
160
|
+
## 8. 失败回滚
|
|
161
|
+
|
|
162
|
+
### 8.1 publish 前任意步骤失败
|
|
163
|
+
|
|
164
|
+
最干净:删 tag 重打。
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git tag -d v0.0.10
|
|
168
|
+
git push origin :refs/tags/v0.0.10
|
|
169
|
+
|
|
170
|
+
# 修问题 → 重新 commit(若 release commit 也要改,用 amend)
|
|
171
|
+
git commit --amend
|
|
172
|
+
git push origin main --force-with-lease # 仅当 amend 了已 push 的 commit 时
|
|
173
|
+
|
|
174
|
+
# 重新打
|
|
175
|
+
git tag v0.0.10 <commit-sha>
|
|
176
|
+
git push origin v0.0.10
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 8.2 publish 已成功但后续步骤失败
|
|
180
|
+
|
|
181
|
+
npm 已上线 → **不要**改版本号,只补后续动作。
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# 抽取 CHANGELOG 段落
|
|
185
|
+
awk -v v="0.0.10" '...' CHANGELOGS.md > /tmp/notes.md
|
|
186
|
+
|
|
187
|
+
# 手动创建 GitHub Release
|
|
188
|
+
gh release create v0.0.10 --notes-file /tmp/notes.md --latest
|
|
189
|
+
|
|
190
|
+
# 或编辑已有 Release(如果 action 半成品创建过空 Release)
|
|
191
|
+
gh release edit v0.0.10 --notes-file /tmp/notes.md
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### 8.3 publish 失败但 npm 上其实有了(罕见)
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# 确认状态
|
|
198
|
+
npm view @scope/pkg@0.0.10 version
|
|
199
|
+
|
|
200
|
+
# 如果有,说明上传完但 npm 返回失败码,直接当作 8.2 处理(已发,只补 Release)
|
|
201
|
+
# 如果无,按 8.1 处理(重打 tag)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### 8.4 npm 不允许重发同一 version
|
|
205
|
+
|
|
206
|
+
铁律:即使 `npm unpublish` 过,**24h 内同一版本号也不能再发**。若 publish 真上传成功但需要修代码:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# bump 到下一个补丁
|
|
210
|
+
npm version 0.0.11 --no-git-tag-version
|
|
211
|
+
git add package.json CHANGELOGS.md
|
|
212
|
+
git commit -m "chore(release): v0.0.11"
|
|
213
|
+
git push origin main
|
|
214
|
+
git tag v0.0.11
|
|
215
|
+
git push origin v0.0.11
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## 9. CHECKLIST
|
|
219
|
+
|
|
220
|
+
发版前过一遍:
|
|
221
|
+
|
|
222
|
+
- [ ] `git status` 干净
|
|
223
|
+
- [ ] CHANGELOG 顶部已迁移 `[Unreleased]` 到新版本段
|
|
224
|
+
- [ ] 底部 compare 链接已更新
|
|
225
|
+
- [ ] `package.json` version 与即将打的 tag 严格一致(去掉 `v` 前缀)
|
|
226
|
+
- [ ] `pnpm check` 本地通过(lint + test + build)
|
|
227
|
+
- [ ] `pnpm build:check-bin` 通过(若是 CLI 包)
|
|
228
|
+
- [ ] release commit 已 push 到 origin/main
|
|
229
|
+
- [ ] tag 指向 release commit(不是 docs commit / HEAD 漂移后的 commit)
|
|
230
|
+
- [ ] tag 已 push,`gh run list` 看到 workflow 触发
|
|
231
|
+
- [ ] workflow 11 步全 success
|
|
232
|
+
- [ ] 三端验证齐全:npm 有 / Release body 非空 / workflow success
|