@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,236 @@
|
|
|
1
|
+
# CHANGELOG → GitHub Release notes 注入
|
|
2
|
+
|
|
3
|
+
让每次 `git push origin vX.Y.Z` 触发的 GitHub Release **正文非空**、**与 CHANGELOG 段落严格一致**的标准方案。
|
|
4
|
+
|
|
5
|
+
## 1. 为什么 GitHub Release 默认是空的
|
|
6
|
+
|
|
7
|
+
`softprops/action-gh-release@v2` 仅设 `generate_release_notes: true` 时,GitHub 内部调用的是 **REST API `POST /repos/{owner}/{repo}/releases/generate-notes`**,该接口的输入是「上一个 tag → 当前 tag」之间的 **commit + PR + label**,优先取 PR 标题做分类。
|
|
8
|
+
|
|
9
|
+
两种典型场景下生成器会"贫瘠":
|
|
10
|
+
|
|
11
|
+
| 场景 | 生成器输出 | 原因 |
|
|
12
|
+
|------|----------|------|
|
|
13
|
+
| 直 push main(无 PR 流程) | 仅 `Full Changelog: vA...vB` 一行 | 生成器没有 PR 可分类,只能给 compare 链接 |
|
|
14
|
+
| 有 PR 但仓库没有 `.github/release.yml` | 全部 PR 都被塞进 "What's Changed" 一节 | 没有 label → 章节映射 |
|
|
15
|
+
|
|
16
|
+
而仓库通常已经维护了 `CHANGELOGS.md`(Keep a Changelog 格式)。**正确做法是把 CHANGELOG 段落作为 Release 正文的事实来源**,自动生成器仅作为补充链接。
|
|
17
|
+
|
|
18
|
+
## 2. 注入方案
|
|
19
|
+
|
|
20
|
+
核心思路:在 workflow 里加一步,从 `CHANGELOGS.md` 抽取当前 tag 对应的版本段落,写到 `release-notes.md`,再通过 `softprops/action-gh-release` 的 `body_path` 注入。
|
|
21
|
+
|
|
22
|
+
### 2.1 完整 workflow 步骤
|
|
23
|
+
|
|
24
|
+
加到 `Verify bin entry` 之后、`Publish to npm` 之前(也可以放 publish 之后,只要在 Create GitHub Release 之前):
|
|
25
|
+
|
|
26
|
+
```yaml
|
|
27
|
+
- name: Extract release notes from CHANGELOGS.md
|
|
28
|
+
run: |
|
|
29
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
30
|
+
awk -v v="$VERSION" '
|
|
31
|
+
$0 ~ "^## \\["v"\\]" { found=1; print; next }
|
|
32
|
+
found && /^## \[/ { exit }
|
|
33
|
+
found && /^---[[:space:]]*$/ { next }
|
|
34
|
+
found { print }
|
|
35
|
+
' CHANGELOGS.md > release-notes.md
|
|
36
|
+
|
|
37
|
+
if [ ! -s release-notes.md ]; then
|
|
38
|
+
echo "::warning::CHANGELOGS.md 中未找到 [$VERSION] 段落,回退到自动生成。"
|
|
39
|
+
echo "See [CHANGELOGS.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOGS.md) for details." > release-notes.md
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
echo "--- Release notes preview ---"
|
|
43
|
+
cat release-notes.md
|
|
44
|
+
echo "-----------------------------"
|
|
45
|
+
|
|
46
|
+
- name: Create GitHub Release
|
|
47
|
+
uses: softprops/action-gh-release@v2
|
|
48
|
+
if: success()
|
|
49
|
+
with:
|
|
50
|
+
tag_name: ${{ github.ref_name }}
|
|
51
|
+
name: Release ${{ github.ref_name }}
|
|
52
|
+
body_path: release-notes.md # ← 关键
|
|
53
|
+
generate_release_notes: true # ← 与 body_path 共存,不冲突
|
|
54
|
+
draft: false
|
|
55
|
+
prerelease: false
|
|
56
|
+
env:
|
|
57
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2.2 awk 抽取规则解释
|
|
61
|
+
|
|
62
|
+
```awk
|
|
63
|
+
$0 ~ "^## \\["v"\\]" { found=1; print; next } # 1. 命中 "## [X.Y.Z]" 起始锚点,开关打开,打印当行
|
|
64
|
+
found && /^## \[/ { exit } # 2. 已在段落内,遇到下一个 "## [" 立刻退出
|
|
65
|
+
found && /^---[[:space:]]*$/ { next } # 3. 跳过 "---" 分隔符(GitHub Release 不需要)
|
|
66
|
+
found { print } # 4. 段落内的其他行原样输出
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
边界用例:
|
|
70
|
+
|
|
71
|
+
- **段落是 CHANGELOG 末尾**:没有下一个 `## [`,awk 跑到 EOF 自然结束
|
|
72
|
+
- **存在 `[Unreleased]` 段落**:不会被误抓,因为锚点要求精确匹配 `[X.Y.Z]` 形式
|
|
73
|
+
- **版本号含连字符(rc / beta)**:`0.0.10-rc1` 也能匹配,因为 awk 把 `v` 当字面量
|
|
74
|
+
- **CHANGELOG 缺该段**:`-s` 测试为空 → 写入兜底文案 + 输出 warning
|
|
75
|
+
|
|
76
|
+
### 2.3 `body_path` + `generate_release_notes` 共存语义
|
|
77
|
+
|
|
78
|
+
`softprops/action-gh-release@v2` 在两者同时存在时:
|
|
79
|
+
|
|
80
|
+
- **正文头部**:`body_path` 文件内容(你的 CHANGELOG 段落)
|
|
81
|
+
- **正文尾部**:GitHub 自动生成的 "What's Changed" + "Full Changelog" compare 链接
|
|
82
|
+
|
|
83
|
+
这是最优组合:**人写的精炼说明 + 机器可追溯的 PR/commit 索引**。
|
|
84
|
+
|
|
85
|
+
## 3. 事后修复历史 Release(已发布版本正文为空)
|
|
86
|
+
|
|
87
|
+
GitHub Release 的正文(body)**事后可改**(tag 不可改、npm 包不可改、但 Release body 可改)。两种方式:
|
|
88
|
+
|
|
89
|
+
### 3.1 命令行(推荐)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# 一行命令:抽取 → 回填 → 清理
|
|
93
|
+
VERSION="0.0.10"
|
|
94
|
+
awk -v v="$VERSION" '
|
|
95
|
+
$0 ~ "^## \\["v"\\]" { found=1; print; next }
|
|
96
|
+
found && /^## \[/ { exit }
|
|
97
|
+
found && /^---[[:space:]]*$/ { next }
|
|
98
|
+
found { print }
|
|
99
|
+
' CHANGELOGS.md > /tmp/notes.md && \
|
|
100
|
+
gh release edit "v$VERSION" --notes-file /tmp/notes.md && \
|
|
101
|
+
rm /tmp/notes.md
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### 3.2 GitHub UI
|
|
105
|
+
|
|
106
|
+
进入 `https://github.com/<owner>/<repo>/releases/tag/vX.Y.Z`,右上角铅笔图标 → 粘贴 CHANGELOG 段落 → Update release。
|
|
107
|
+
|
|
108
|
+
### 3.3 批量回填多个历史版本
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
for v in 0.0.7 0.0.8 0.0.9; do
|
|
112
|
+
awk -v ver="$v" '
|
|
113
|
+
$0 ~ "^## \\["ver"\\]" { found=1; print; next }
|
|
114
|
+
found && /^## \[/ { exit }
|
|
115
|
+
found && /^---[[:space:]]*$/ { next }
|
|
116
|
+
found { print }
|
|
117
|
+
' CHANGELOGS.md > /tmp/notes.md
|
|
118
|
+
|
|
119
|
+
if [ -s /tmp/notes.md ]; then
|
|
120
|
+
gh release edit "v$v" --notes-file /tmp/notes.md
|
|
121
|
+
echo "✓ v$v"
|
|
122
|
+
else
|
|
123
|
+
echo "✗ v$v: CHANGELOG 中无对应段落,跳过"
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
rm -f /tmp/notes.md
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## 4. 进阶:走 PR 流程的项目
|
|
130
|
+
|
|
131
|
+
若仓库以 PR 为主、合并产生 commit,推荐 **同时**保留 CHANGELOG 注入 + 配置 `.github/release.yml` 让自动生成器分类更友好。
|
|
132
|
+
|
|
133
|
+
### 4.1 `.github/release.yml`(GitHub 原生分类配置)
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
# .github/release.yml
|
|
137
|
+
changelog:
|
|
138
|
+
exclude:
|
|
139
|
+
labels:
|
|
140
|
+
- ignore-for-release
|
|
141
|
+
- dependencies # 可选:依赖升级单独成节
|
|
142
|
+
categories:
|
|
143
|
+
- title: 🚀 Features
|
|
144
|
+
labels:
|
|
145
|
+
- feat
|
|
146
|
+
- feature
|
|
147
|
+
- enhancement
|
|
148
|
+
- title: 🐛 Bug Fixes
|
|
149
|
+
labels:
|
|
150
|
+
- fix
|
|
151
|
+
- bug
|
|
152
|
+
- title: 📚 Documentation
|
|
153
|
+
labels:
|
|
154
|
+
- docs
|
|
155
|
+
- documentation
|
|
156
|
+
- title: ♻️ Refactoring
|
|
157
|
+
labels:
|
|
158
|
+
- refactor
|
|
159
|
+
- title: 🧪 Testing
|
|
160
|
+
labels:
|
|
161
|
+
- test
|
|
162
|
+
- title: 🔧 Maintenance
|
|
163
|
+
labels:
|
|
164
|
+
- chore
|
|
165
|
+
- ci
|
|
166
|
+
- title: Other Changes
|
|
167
|
+
labels:
|
|
168
|
+
- "*"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
效果:`generate_release_notes` 自动产出的 "What's Changed" 会被分类到上述章节中,与 CHANGELOG 段落叠加在 Release 正文内。
|
|
172
|
+
|
|
173
|
+
### 4.2 配套的 PR label 自动化
|
|
174
|
+
|
|
175
|
+
参考仓库已有 `.github/labeler.yml` + `.github/workflows/label-pr.yml`,基于改动路径自动打 label。也可让 PR 模板提示作者按 Conventional Commits 选择类型。
|
|
176
|
+
|
|
177
|
+
## 5. 多语言 CHANGELOG 处理
|
|
178
|
+
|
|
179
|
+
如果项目同时维护 `CHANGELOG.md` + `CHANGELOG-ZH.md`,Release 正文应优先取**主语言**(通常英文)。两种策略:
|
|
180
|
+
|
|
181
|
+
### A. workflow 选择主语言(简单)
|
|
182
|
+
|
|
183
|
+
```yaml
|
|
184
|
+
- name: Extract release notes
|
|
185
|
+
run: |
|
|
186
|
+
# 主语言文件名作为输入
|
|
187
|
+
awk -v v="${GITHUB_REF_NAME#v}" '...' CHANGELOG.md > release-notes.md
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### B. workflow 拼接双语(完整)
|
|
191
|
+
|
|
192
|
+
```yaml
|
|
193
|
+
- name: Extract release notes (bilingual)
|
|
194
|
+
run: |
|
|
195
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
196
|
+
EXTRACT='$0 ~ "^## \\["v"\\]" { found=1; print; next } found && /^## \[/ { exit } found && /^---[[:space:]]*$/ { next } found { print }'
|
|
197
|
+
|
|
198
|
+
awk -v v="$VERSION" "$EXTRACT" CHANGELOG.md > /tmp/en.md
|
|
199
|
+
awk -v v="$VERSION" "$EXTRACT" CHANGELOG-ZH.md > /tmp/zh.md
|
|
200
|
+
|
|
201
|
+
{
|
|
202
|
+
cat /tmp/en.md
|
|
203
|
+
echo
|
|
204
|
+
echo "---"
|
|
205
|
+
echo
|
|
206
|
+
echo "### 中文版本说明"
|
|
207
|
+
echo
|
|
208
|
+
cat /tmp/zh.md
|
|
209
|
+
} > release-notes.md
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## 6. 常见坑位
|
|
213
|
+
|
|
214
|
+
| 坑 | 现象 | 解法 |
|
|
215
|
+
|---|------|------|
|
|
216
|
+
| `body_path` 文件不存在 | action 失败 `Could not find the body_path` | 加 `if [ ! -s release-notes.md ]` 兜底写入 |
|
|
217
|
+
| awk 没匹配到 | release-notes.md 为空,Release 正文兜底文案 | 检查 CHANGELOG 段落标题与 tag 是否对应(`v0.0.10` ↔ `## [0.0.10]`) |
|
|
218
|
+
| awk 多匹配(同一版本写了两次) | Release 正文重复内容 | CHANGELOG 是 source of truth,先去重 |
|
|
219
|
+
| 全角中括号 `【】` 被当锚点 | 抽取失败 | CHANGELOG 标题统一用半角 `[` |
|
|
220
|
+
| body_path 文件含 BOM | Release 渲染异常 | 用 `sed -i '1s/^\xef\xbb\xbf//' release-notes.md` 去 BOM |
|
|
221
|
+
|
|
222
|
+
## 7. 验证
|
|
223
|
+
|
|
224
|
+
每次发布后必查:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
# Release body 必须非空且包含 CHANGELOG 关键章节
|
|
228
|
+
gh release view "v$VERSION" --json body --jq '.body' | head -20
|
|
229
|
+
|
|
230
|
+
# 三端齐绿才算发布完成
|
|
231
|
+
npm view @scope/pkg version
|
|
232
|
+
gh release view "v$VERSION" --json isDraft,isPrerelease,publishedAt
|
|
233
|
+
gh run view <run-id> --json conclusion,jobs --jq '{conclusion, steps: [.jobs[].steps[] | select(.name | contains("Extract") or contains("Release")) | {name, conclusion}]}'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`Extract release notes` 步骤未 success → Release 正文将走兜底文案,需排查 awk 是否抽空。
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# npm + GitHub Release 编排
|
|
2
|
+
|
|
3
|
+
把一次 npm 包发布拆成可验证的单向流水线:前置检查 -> 普通改动 commit -> docs-sync -> release commit -> tag -> workflow 监控 -> 三端验证 -> docs-sync 基线推进。
|
|
4
|
+
|
|
5
|
+
## Iron Law
|
|
6
|
+
|
|
7
|
+
- 禁止提交破坏构建的代码;release 前必须运行仓库声明的 lint / test / build 或等价质量闸。
|
|
8
|
+
- docs-sync 必须由调用方按 `workflows/dev/D-docs-sync/D-docs-sync.md` 执行,基于 `.speculo/dev/docs-sync-state.json#last_sync_sha..HEAD` 的 git diff。
|
|
9
|
+
- tag 必须精确指向 release commit,即包含 `package.json` version bump 与 CHANGELOG 迁移的 commit;禁止指向后续 docs / state commit。
|
|
10
|
+
- npm 已成功上传后,同一 version 不可重发;不要通过删 tag 或 unpublish 试图覆盖。
|
|
11
|
+
|
|
12
|
+
## Phase 0 — 前置检测
|
|
13
|
+
|
|
14
|
+
读取 `preflight-checklist.md` 和 `publish-detection.md`,固定确认:
|
|
15
|
+
|
|
16
|
+
- 当前分支是发布分支,默认 `main`
|
|
17
|
+
- 工作区干净,远端可达,`gh auth status` 正常
|
|
18
|
+
- Node 与包管理器满足仓库声明
|
|
19
|
+
- `.github/workflows/release.yml` 存在
|
|
20
|
+
- 已确定 `PUBLISH_TO_NPM=true | false`
|
|
21
|
+
- `.speculo/dev/docs-sync-state.json` 存在且可解析;不存在时转 `dev/D-docs-sync` 首次运行流程
|
|
22
|
+
- 目标 tag `vX.Y.Z` 不存在,除非正在执行明确的失败恢复
|
|
23
|
+
|
|
24
|
+
任一项不通过就停止,输出修复建议。
|
|
25
|
+
|
|
26
|
+
## Phase 1 — 普通改动 commit
|
|
27
|
+
|
|
28
|
+
- 运行仓库质量闸:优先 `pnpm check`,否则按项目脚本组合 lint / test / build。
|
|
29
|
+
- 审查提交粒度;不把无关修改塞进一个 commit。
|
|
30
|
+
- 暂存显式路径,避免 `git add .` 纳入噪声。
|
|
31
|
+
- 使用 Conventional Commits;body 说明做了什么和为什么。
|
|
32
|
+
|
|
33
|
+
## Phase 2 — Docs Sync
|
|
34
|
+
|
|
35
|
+
- 由调用方执行 `workflows/dev/D-docs-sync/D-docs-sync.md`。
|
|
36
|
+
- 只修改 tracked docs 中需要同步的文档。
|
|
37
|
+
- CHANGELOG 类文档只写 `[Unreleased]`,保留该段落。
|
|
38
|
+
- 本阶段不推进 docs-sync state 到 release commit;最终基线推进放到 Phase 6。
|
|
39
|
+
|
|
40
|
+
## Phase 3 — 版本 bump + release commit + tag
|
|
41
|
+
|
|
42
|
+
读取 `version-bump-flow.md`。
|
|
43
|
+
|
|
44
|
+
- 将 CHANGELOG 的 `[Unreleased]` 内容迁移到 `## [X.Y.Z] - YYYY-MM-DD` 或项目既有等价格式,并重新保留空 `[Unreleased]`。
|
|
45
|
+
- 将 `package.json#version` 改为 `X.Y.Z`;tag 使用 `vX.Y.Z`。
|
|
46
|
+
- 运行质量闸;除非调用方明确记录外部预演证据,不跳过。
|
|
47
|
+
- release commit 只包含版本 bump 与 CHANGELOG 迁移,提交信息为 `chore(release): vX.Y.Z`。
|
|
48
|
+
- 记录 `RELEASE_COMMIT_SHA`。
|
|
49
|
+
- 先 push 发布分支,再 `git tag vX.Y.Z $RELEASE_COMMIT_SHA`,再 push tag。
|
|
50
|
+
|
|
51
|
+
## Phase 4 — 监控 release workflow
|
|
52
|
+
|
|
53
|
+
用 `gh run list` 找到 head SHA 等于 `RELEASE_COMMIT_SHA` 的 release run,再 `gh run watch --exit-status`。
|
|
54
|
+
|
|
55
|
+
重点检查:
|
|
56
|
+
|
|
57
|
+
- Verify tag matches package version
|
|
58
|
+
- lint / tests / build / bin entry
|
|
59
|
+
- Extract release notes from CHANGELOG
|
|
60
|
+
- Publish to npm(仅 `PUBLISH_TO_NPM=true`)
|
|
61
|
+
- Create GitHub Release
|
|
62
|
+
|
|
63
|
+
失败时进入 `failure-recovery.md` 和 `troubleshooting-playbook.md`。
|
|
64
|
+
|
|
65
|
+
## Phase 5 — 三端验证
|
|
66
|
+
|
|
67
|
+
始终验证:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
gh release view "vX.Y.Z" --json name,tagName,isDraft,isPrerelease,body
|
|
71
|
+
gh run list --workflow release.yml --limit 1 --json conclusion,headSha
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
判定:
|
|
75
|
+
|
|
76
|
+
- Release 非 draft、非 prerelease
|
|
77
|
+
- Release body 非空,且包含 CHANGELOG 段落;仅 `Full Changelog` 一行视为残缺
|
|
78
|
+
- workflow conclusion 为 success,headSha 等于 `RELEASE_COMMIT_SHA`
|
|
79
|
+
|
|
80
|
+
仅当 `PUBLISH_TO_NPM=true` 时验证:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
npm view "<package-name>" version
|
|
84
|
+
npm view "<package-name>" dist-tags
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
判定 version 与 `dist-tags.latest` 均为 `X.Y.Z`。
|
|
88
|
+
|
|
89
|
+
## Phase 6 — 推进 docs-sync 基线
|
|
90
|
+
|
|
91
|
+
仅当 Phase 1-5 全绿时执行。本 skill 不直接写 `.speculo/`;把基线推进交给调用方的 release workflow 或 `workflows/dev/D-docs-sync/D-docs-sync.md`,只向其提供以下取值:
|
|
92
|
+
|
|
93
|
+
- `last_sync_sha` 推进到 `RELEASE_COMMIT_SHA`。
|
|
94
|
+
- `previous_sync_sha` 使用推进前的 `last_sync_sha`。
|
|
95
|
+
- `synced_docs` 使用 Phase 2 实际修改的文档列表;空同步为 `[]`。
|
|
96
|
+
|
|
97
|
+
调用方把 state 推进作为 release tag 之后的普通 docs commit,tag 不移动;写入机制(原子写、schema 校验)由 D-docs-sync workflow 负责。
|
|
98
|
+
|
|
99
|
+
## 完成报告
|
|
100
|
+
|
|
101
|
+
报告至少包含:
|
|
102
|
+
|
|
103
|
+
- 版本号、release commit SHA、tag SHA
|
|
104
|
+
- workflow run id 与结论
|
|
105
|
+
- GitHub Release 检查结果
|
|
106
|
+
- npm 检查结果或跳过原因
|
|
107
|
+
- docs-sync state old/new baseline
|
|
108
|
+
- 失败恢复或后续建议
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# 配置 npm Token 与 GitHub Secret
|
|
2
|
+
|
|
3
|
+
npm 现在强制要求发布时通过 2FA 或等效机制。Classic token 在多数账号设置下会返回 `E403`,必须改用 **Granular Access Token**。
|
|
4
|
+
|
|
5
|
+
## 生成 Granular Access Token
|
|
6
|
+
|
|
7
|
+
1. 登录 [npmjs.com](https://www.npmjs.com),点头像 → **Access Tokens**
|
|
8
|
+
2. 点 **Generate New Token** → 选择 **Granular Access Token**
|
|
9
|
+
3. 关键字段:
|
|
10
|
+
|
|
11
|
+
| 字段 | 推荐值 |
|
|
12
|
+
| ----------------------- | ------------------------------------------------------------------------------------------ |
|
|
13
|
+
| Token name | `<package>-ci-release`(可识别用途) |
|
|
14
|
+
| Expiration | 90–365 天;设提醒到期前轮换 |
|
|
15
|
+
| Packages and scopes | 选 `Only select packages and scopes`,勾选目标包或整个 scope(如 `@scope`) |
|
|
16
|
+
| Permissions | **Read and write** |
|
|
17
|
+
| Allowed organizations | 如果在组织下发布,勾选对应组织 |
|
|
18
|
+
| Bypass two-factor auth | 勾选(CI 无法交互完成 2FA) |
|
|
19
|
+
| Allowed IP ranges | 留空(GitHub Actions runner IP 不固定) |
|
|
20
|
+
|
|
21
|
+
4. 点 **Generate**,**立刻复制** token,关掉页面就再也看不到了
|
|
22
|
+
|
|
23
|
+
## 配置到 GitHub Secret
|
|
24
|
+
|
|
25
|
+
1. 仓库 → **Settings** → **Secrets and variables** → **Actions**
|
|
26
|
+
2. 点 **New repository secret**
|
|
27
|
+
3. `Name: NPM_TOKEN`,`Value: <粘贴 token>`
|
|
28
|
+
4. 点 **Add secret**
|
|
29
|
+
|
|
30
|
+
## 在 workflow 中引用
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
- name: Publish to npm
|
|
34
|
+
run: npm publish --provenance --access public
|
|
35
|
+
env:
|
|
36
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
重点:变量名必须是 `NODE_AUTH_TOKEN`(`actions/setup-node` 约定),不是 `NPM_TOKEN`。Secret 的名字可以是 `NPM_TOKEN`,但要映射过来。
|
|
40
|
+
|
|
41
|
+
## 验证 token 生效
|
|
42
|
+
|
|
43
|
+
本地不需要这个 token,它只在 CI 使用。如果想本地预演:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
echo "//registry.npmjs.org/:_authToken=<token>" >> ~/.npmrc
|
|
47
|
+
npm whoami # 应返回你的 npm 用户名
|
|
48
|
+
npm publish --dry-run --access public
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
预演完记得把 `.npmrc` 里的这行删掉。
|
|
52
|
+
|
|
53
|
+
## 轮换策略
|
|
54
|
+
|
|
55
|
+
- Token 到期前 7 天收到邮件提醒
|
|
56
|
+
- 轮换步骤:生成新 token → 更新 GitHub Secret → 在 npm 上 revoke 旧 token
|
|
57
|
+
- 如果怀疑泄露:立即在 npm Access Tokens 页面点 **Revoke**,workflow 会在下次运行时失败,再补生成新 token
|
|
58
|
+
|
|
59
|
+
## 常见误区
|
|
60
|
+
|
|
61
|
+
- **不要用 Classic Token 的 `Automation` 类型** — 某些账号策略下仍会触发 2FA 检查
|
|
62
|
+
- **不要把 token 写进 `.npmrc` 提交** — 只能走 `secrets`
|
|
63
|
+
- **不要与个人 publish token 混用** — CI 用专用 token,出问题可独立撤销
|