@cleocode/cleo 2026.5.106 → 2026.5.108
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/dist/cli/index.js +471 -2138
- package/dist/cli/index.js.map +3 -3
- package/package.json +11 -11
- package/templates/HANDOFF-REDIRECT-STUB.md +0 -37
- package/templates/hooks/commit-msg +0 -213
- package/templates/hooks/pre-push +0 -87
- package/templates/hooks/pre-push.t1595-extension.sh +0 -140
- package/templates/workflows/README.md +0 -157
- package/templates/workflows/release-fanout.yml.tmpl +0 -214
- package/templates/workflows/release-prepare.yml.tmpl +0 -296
- package/templates/workflows/release-publish.yml.tmpl +0 -388
- package/templates/workflows/release-rollback.yml.tmpl +0 -322
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
# CLEO release pipeline — Publish + tag workflow.
|
|
2
|
-
#
|
|
3
|
-
# Generated from packages/cleo/templates/workflows/release-publish.yml.tmpl by
|
|
4
|
-
# `cleo init --workflows` (T9531). DO NOT EDIT the rendered file directly —
|
|
5
|
-
# extend via `.workflow-overrides.yml` per SPEC-T9345 R-260.
|
|
6
|
-
#
|
|
7
|
-
# Implements SPEC-T9345-release-pipeline-v2.md §5.2 (R-220 — R-235, R-260 —
|
|
8
|
-
# R-263). Triggers on push to main when the commit subject matches the
|
|
9
|
-
# canonical `release: prepare v<version>` prefix produced by
|
|
10
|
-
# release-prepare.yml. Tags ONLY after `gh pr view` confirms state=MERGED
|
|
11
|
-
# AND mergeCommit.oid == $GITHUB_SHA — this eliminates the F6 race
|
|
12
|
-
# (tag-on-pre-merge-SHA) by construction.
|
|
13
|
-
#
|
|
14
|
-
# Placeholders (replaced at scaffold time — see workflows/README.md):
|
|
15
|
-
# <NODE_VERSION> Node.js version (e.g. "22.x")
|
|
16
|
-
# <INSTALL_CMD> Install command (e.g. "pnpm install --frozen-lockfile")
|
|
17
|
-
# <LINT_CMD> tool:lint (ADR-061 resolved)
|
|
18
|
-
# <TYPECHECK_CMD> tool:typecheck (ADR-061 resolved)
|
|
19
|
-
# <TEST_CMD> tool:test (ADR-061 resolved)
|
|
20
|
-
# <BUILD_CMD> tool:build (ADR-061 resolved)
|
|
21
|
-
# <NPM_PUBLISH_CMD> Publish command (e.g. "pnpm publish --access public --tag latest")
|
|
22
|
-
# <PUBLISHERS> Space-separated publisher list (e.g. "npm cargo")
|
|
23
|
-
# (Angle-brackets in this doc comment are intentional — they avoid being
|
|
24
|
-
# substituted by the {{...}} regex pass. The placeholders below use {{...}}.)
|
|
25
|
-
|
|
26
|
-
name: Release Publish
|
|
27
|
-
|
|
28
|
-
# R-220: push to main filtered to version-file paths only — bypasses unrelated
|
|
29
|
-
# main commits without spinning up the full matrix.
|
|
30
|
-
# R-221: workflow_dispatch with `version` input for manual re-runs and
|
|
31
|
-
# idempotency tests.
|
|
32
|
-
on:
|
|
33
|
-
push:
|
|
34
|
-
branches:
|
|
35
|
-
- main
|
|
36
|
-
paths:
|
|
37
|
-
- 'package.json'
|
|
38
|
-
- 'packages/*/package.json'
|
|
39
|
-
- 'Cargo.toml'
|
|
40
|
-
- 'packages/*/Cargo.toml'
|
|
41
|
-
- 'pyproject.toml'
|
|
42
|
-
- 'packages/*/pyproject.toml'
|
|
43
|
-
workflow_dispatch:
|
|
44
|
-
inputs:
|
|
45
|
-
version:
|
|
46
|
-
description: 'Version to publish (e.g. v2026.6.0)'
|
|
47
|
-
type: string
|
|
48
|
-
required: true
|
|
49
|
-
|
|
50
|
-
# R-222: workflow-level grants the minimum read+tag scopes. `packages: write`
|
|
51
|
-
# is granted PER-JOB on publish-and-tag only — fanout/reconcile jobs MUST
|
|
52
|
-
# NOT inherit publish scope.
|
|
53
|
-
permissions:
|
|
54
|
-
contents: write
|
|
55
|
-
id-token: write
|
|
56
|
-
|
|
57
|
-
# R-230: concurrent publishes against the same version queue rather than
|
|
58
|
-
# collide. cancel-in-progress=false preserves in-flight publishes.
|
|
59
|
-
concurrency:
|
|
60
|
-
group: release-publish-${{ github.sha }}
|
|
61
|
-
cancel-in-progress: false
|
|
62
|
-
|
|
63
|
-
# R-262: bash everywhere — no cross-platform shell drift across the matrix.
|
|
64
|
-
defaults:
|
|
65
|
-
run:
|
|
66
|
-
shell: bash
|
|
67
|
-
|
|
68
|
-
jobs:
|
|
69
|
-
# R-223 (1/4) + R-224: classify the triggering commit. If the subject
|
|
70
|
-
# doesn't match `release: prepare v<version>`, ALL downstream jobs skip
|
|
71
|
-
# (`should_publish=false`). Manual workflow_dispatch always publishes.
|
|
72
|
-
detect:
|
|
73
|
-
name: Detect release commit
|
|
74
|
-
runs-on: ubuntu-latest
|
|
75
|
-
timeout-minutes: 3
|
|
76
|
-
outputs:
|
|
77
|
-
should_publish: ${{ steps.classify.outputs.should_publish }}
|
|
78
|
-
version: ${{ steps.classify.outputs.version }}
|
|
79
|
-
steps:
|
|
80
|
-
# R-263: third-party Actions pinned to major+minor.
|
|
81
|
-
- name: Checkout
|
|
82
|
-
uses: actions/checkout@v4.1
|
|
83
|
-
with:
|
|
84
|
-
fetch-depth: 0
|
|
85
|
-
timeout-minutes: 5
|
|
86
|
-
|
|
87
|
-
- name: Classify triggering commit
|
|
88
|
-
id: classify
|
|
89
|
-
run: |
|
|
90
|
-
set -euo pipefail
|
|
91
|
-
if [ -n "${{ inputs.version }}" ]; then
|
|
92
|
-
# Manual dispatch — trust the input.
|
|
93
|
-
echo "should_publish=true" >> "$GITHUB_OUTPUT"
|
|
94
|
-
echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
|
|
95
|
-
exit 0
|
|
96
|
-
fi
|
|
97
|
-
BEFORE="${{ github.event.before }}"
|
|
98
|
-
SHA="${{ github.sha }}"
|
|
99
|
-
if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
|
|
100
|
-
BEFORE="$SHA^"
|
|
101
|
-
fi
|
|
102
|
-
if subject=$(git log --format=%s "$BEFORE..$SHA" 2>/dev/null | grep -E '^release: prepare v' | head -1); then
|
|
103
|
-
VERSION=$(echo "$subject" | sed -E 's/^release: prepare (v[^ ]+).*/\1/')
|
|
104
|
-
echo "should_publish=true" >> "$GITHUB_OUTPUT"
|
|
105
|
-
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
106
|
-
else
|
|
107
|
-
echo "should_publish=false" >> "$GITHUB_OUTPUT"
|
|
108
|
-
echo "version=" >> "$GITHUB_OUTPUT"
|
|
109
|
-
fi
|
|
110
|
-
timeout-minutes: 2
|
|
111
|
-
|
|
112
|
-
# R-223 (2/4) + R-225: matrix expansion across the five T1737 platform
|
|
113
|
-
# tuples. fail-fast=true so a single platform failure prevents the
|
|
114
|
-
# publish-and-tag job from running (R-228).
|
|
115
|
-
build-matrix:
|
|
116
|
-
name: Build & test (${{ matrix.platform }})
|
|
117
|
-
needs: detect
|
|
118
|
-
if: needs.detect.outputs.should_publish == 'true'
|
|
119
|
-
strategy:
|
|
120
|
-
fail-fast: true
|
|
121
|
-
matrix:
|
|
122
|
-
include:
|
|
123
|
-
- platform: linux-x64
|
|
124
|
-
runner: ubuntu-latest
|
|
125
|
-
- platform: linux-arm64
|
|
126
|
-
runner: ubuntu-22.04-arm
|
|
127
|
-
- platform: macos-x64
|
|
128
|
-
runner: macos-15-intel
|
|
129
|
-
- platform: macos-arm64
|
|
130
|
-
runner: macos-14
|
|
131
|
-
- platform: windows-x64
|
|
132
|
-
runner: windows-latest
|
|
133
|
-
runs-on: ${{ matrix.runner }}
|
|
134
|
-
timeout-minutes: 30
|
|
135
|
-
steps:
|
|
136
|
-
- name: Checkout
|
|
137
|
-
uses: actions/checkout@v4.1
|
|
138
|
-
with:
|
|
139
|
-
fetch-depth: 0
|
|
140
|
-
timeout-minutes: 5
|
|
141
|
-
|
|
142
|
-
- name: Set up Node.js
|
|
143
|
-
uses: actions/setup-node@v4.0
|
|
144
|
-
with:
|
|
145
|
-
node-version: '{{NODE_VERSION}}'
|
|
146
|
-
timeout-minutes: 3
|
|
147
|
-
|
|
148
|
-
- name: Install dependencies
|
|
149
|
-
run: {{INSTALL_CMD}}
|
|
150
|
-
timeout-minutes: 10
|
|
151
|
-
|
|
152
|
-
- name: Lint
|
|
153
|
-
run: {{LINT_CMD}}
|
|
154
|
-
timeout-minutes: 5
|
|
155
|
-
|
|
156
|
-
- name: Typecheck
|
|
157
|
-
run: {{TYPECHECK_CMD}}
|
|
158
|
-
timeout-minutes: 5
|
|
159
|
-
|
|
160
|
-
- name: Test
|
|
161
|
-
run: {{TEST_CMD}}
|
|
162
|
-
timeout-minutes: 15
|
|
163
|
-
|
|
164
|
-
- name: Build
|
|
165
|
-
run: {{BUILD_CMD}}
|
|
166
|
-
timeout-minutes: 10
|
|
167
|
-
|
|
168
|
-
# R-225: integration smoke — `cleo --version` and `cleo briefing --json
|
|
169
|
-
# | jq .ok`. ANTHROPIC_API_KEY is scoped to the environment so an
|
|
170
|
-
# unmerged PR can't smoke-test against production credentials.
|
|
171
|
-
- name: Integration smoke
|
|
172
|
-
run: |
|
|
173
|
-
set -euo pipefail
|
|
174
|
-
./bin/cleo --version
|
|
175
|
-
./bin/cleo briefing --json | jq '.ok'
|
|
176
|
-
env:
|
|
177
|
-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
178
|
-
timeout-minutes: 5
|
|
179
|
-
|
|
180
|
-
# R-232: on failure the matrix slot artifacts are uploaded for
|
|
181
|
-
# 30-day retention so operators can triage cross-platform regressions.
|
|
182
|
-
- name: Upload build artifact
|
|
183
|
-
if: always()
|
|
184
|
-
uses: actions/upload-artifact@v4.3
|
|
185
|
-
with:
|
|
186
|
-
name: cleo-${{ needs.detect.outputs.version }}-${{ matrix.platform }}
|
|
187
|
-
path: |
|
|
188
|
-
dist/
|
|
189
|
-
packages/*/dist/
|
|
190
|
-
if-no-files-found: warn
|
|
191
|
-
retention-days: 30
|
|
192
|
-
timeout-minutes: 5
|
|
193
|
-
|
|
194
|
-
# R-223 (3/4) + R-226: publish + tag. Gated by `environment: cleo-publish`
|
|
195
|
-
# (Letta §3.4 step 7) for reviewer-policy enforcement. R-228 enforces
|
|
196
|
-
# that any matrix slot failure blocks this job via the `needs` edge.
|
|
197
|
-
publish-and-tag:
|
|
198
|
-
name: Publish & tag
|
|
199
|
-
needs:
|
|
200
|
-
- detect
|
|
201
|
-
- build-matrix
|
|
202
|
-
if: needs.detect.outputs.should_publish == 'true'
|
|
203
|
-
runs-on: ubuntu-latest
|
|
204
|
-
environment: cleo-publish
|
|
205
|
-
timeout-minutes: 25
|
|
206
|
-
# R-222: packages:write scoped to THIS job only.
|
|
207
|
-
permissions:
|
|
208
|
-
contents: write
|
|
209
|
-
packages: write
|
|
210
|
-
id-token: write
|
|
211
|
-
env:
|
|
212
|
-
VERSION: ${{ needs.detect.outputs.version }}
|
|
213
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
214
|
-
# `{{PUBLISHERS}}` is a render-time placeholder. Hoisting it into env
|
|
215
|
-
# turns the downstream `contains(env.PUBLISHERS, 'npm')` into a
|
|
216
|
-
# dynamic expression so actionlint doesn't flag it as a constant `if:`.
|
|
217
|
-
PUBLISHERS: '{{PUBLISHERS}}'
|
|
218
|
-
steps:
|
|
219
|
-
- name: Checkout
|
|
220
|
-
uses: actions/checkout@v4.1
|
|
221
|
-
with:
|
|
222
|
-
fetch-depth: 0
|
|
223
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
224
|
-
timeout-minutes: 5
|
|
225
|
-
|
|
226
|
-
- name: Configure git identity
|
|
227
|
-
run: |
|
|
228
|
-
git config user.email "actions@github.com"
|
|
229
|
-
git config user.name "GitHub Actions"
|
|
230
|
-
timeout-minutes: 1
|
|
231
|
-
|
|
232
|
-
# R-229 (CRITICAL — F6 race elimination): poll `gh pr view` and assert
|
|
233
|
-
# state=MERGED AND mergeCommit.oid == $GITHUB_SHA BEFORE issuing any
|
|
234
|
-
# `git tag`. Mismatch fails with E_TAG_MISMATCH and never pushes.
|
|
235
|
-
- name: Confirm PR merge state (R-229)
|
|
236
|
-
id: confirm
|
|
237
|
-
run: |
|
|
238
|
-
set -euo pipefail
|
|
239
|
-
PR_NUMBER=$(gh pr list \
|
|
240
|
-
--search "${{ github.sha }}" \
|
|
241
|
-
--state merged \
|
|
242
|
-
--json number \
|
|
243
|
-
--jq '.[0].number // empty')
|
|
244
|
-
if [ -z "$PR_NUMBER" ]; then
|
|
245
|
-
echo "::error::E_TAG_MISMATCH no merged PR found for SHA ${{ github.sha }}"
|
|
246
|
-
exit 1
|
|
247
|
-
fi
|
|
248
|
-
PR_JSON=$(gh pr view "$PR_NUMBER" --json state,mergeCommit)
|
|
249
|
-
STATE=$(echo "$PR_JSON" | jq -r '.state')
|
|
250
|
-
MERGE_OID=$(echo "$PR_JSON" | jq -r '.mergeCommit.oid // ""')
|
|
251
|
-
if [ "$STATE" != "MERGED" ]; then
|
|
252
|
-
echo "::error::E_TAG_MISMATCH PR #$PR_NUMBER state=$STATE (expected MERGED)"
|
|
253
|
-
exit 1
|
|
254
|
-
fi
|
|
255
|
-
if [ -z "$MERGE_OID" ] || [ "$MERGE_OID" != "${{ github.sha }}" ]; then
|
|
256
|
-
echo "::error::E_TAG_MISMATCH PR #$PR_NUMBER merge_oid=$MERGE_OID expected=${{ github.sha }}"
|
|
257
|
-
exit 1
|
|
258
|
-
fi
|
|
259
|
-
echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
|
260
|
-
echo "merge_oid=$MERGE_OID" >> "$GITHUB_OUTPUT"
|
|
261
|
-
timeout-minutes: 5
|
|
262
|
-
|
|
263
|
-
- name: Set up Node.js
|
|
264
|
-
uses: actions/setup-node@v4.0
|
|
265
|
-
with:
|
|
266
|
-
node-version: '{{NODE_VERSION}}'
|
|
267
|
-
registry-url: 'https://registry.npmjs.org'
|
|
268
|
-
timeout-minutes: 3
|
|
269
|
-
|
|
270
|
-
- name: Install dependencies
|
|
271
|
-
run: {{INSTALL_CMD}}
|
|
272
|
-
timeout-minutes: 10
|
|
273
|
-
|
|
274
|
-
- name: Build
|
|
275
|
-
run: {{BUILD_CMD}}
|
|
276
|
-
timeout-minutes: 10
|
|
277
|
-
|
|
278
|
-
# R-226(b): npm publish — gated on PUBLISHERS env containing "npm".
|
|
279
|
-
- name: Publish npm packages
|
|
280
|
-
if: contains(env.PUBLISHERS, 'npm')
|
|
281
|
-
run: {{NPM_PUBLISH_CMD}}
|
|
282
|
-
env:
|
|
283
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
284
|
-
timeout-minutes: 10
|
|
285
|
-
|
|
286
|
-
# R-226(c): cargo publish — gated on PUBLISHERS env containing "cargo".
|
|
287
|
-
- name: Publish cargo crates
|
|
288
|
-
if: contains(env.PUBLISHERS, 'cargo')
|
|
289
|
-
run: cargo publish --token "${{ secrets.CARGO_TOKEN }}"
|
|
290
|
-
timeout-minutes: 10
|
|
291
|
-
|
|
292
|
-
# R-226(d): tag the merge commit (NOT $GITHUB_SHA directly — they're
|
|
293
|
-
# equivalent here but using the confirm step's output makes the
|
|
294
|
-
# F6-eliminating proof explicit).
|
|
295
|
-
- name: Tag release
|
|
296
|
-
run: |
|
|
297
|
-
set -euo pipefail
|
|
298
|
-
git tag "$VERSION" "${{ steps.confirm.outputs.merge_oid }}"
|
|
299
|
-
git push origin "$VERSION"
|
|
300
|
-
timeout-minutes: 5
|
|
301
|
-
|
|
302
|
-
# R-226(e): GitHub Release with auto-generated notes, matrix
|
|
303
|
-
# artifacts attached. fail_on_unmatched_files=true asserts artifacts
|
|
304
|
-
# actually exist.
|
|
305
|
-
- name: Download matrix artifacts
|
|
306
|
-
uses: actions/download-artifact@v4.1
|
|
307
|
-
with:
|
|
308
|
-
path: release-artifacts/
|
|
309
|
-
merge-multiple: false
|
|
310
|
-
timeout-minutes: 5
|
|
311
|
-
|
|
312
|
-
- name: Create GitHub Release
|
|
313
|
-
uses: softprops/action-gh-release@v2.0
|
|
314
|
-
with:
|
|
315
|
-
tag_name: ${{ env.VERSION }}
|
|
316
|
-
target_commitish: ${{ steps.confirm.outputs.merge_oid }}
|
|
317
|
-
generate_release_notes: true
|
|
318
|
-
fail_on_unmatched_files: true
|
|
319
|
-
files: |
|
|
320
|
-
release-artifacts/**/*
|
|
321
|
-
env:
|
|
322
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
323
|
-
|
|
324
|
-
# R-233: partial-publish failure handler — emits a critical issue when
|
|
325
|
-
# one registry succeeded but a later step failed.
|
|
326
|
-
- name: Emit partial-publish incident on failure
|
|
327
|
-
if: failure()
|
|
328
|
-
continue-on-error: true
|
|
329
|
-
run: |
|
|
330
|
-
gh issue create \
|
|
331
|
-
--label release-incident \
|
|
332
|
-
--title "Partial publish failure: ${VERSION}" \
|
|
333
|
-
--body "release-publish.yml failed AFTER one or more registries had succeeded. Manual reconciliation required: re-run reconcile or run \`cleo release reconcile ${VERSION}\` from a local checkout. Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
334
|
-
env:
|
|
335
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
336
|
-
timeout-minutes: 5
|
|
337
|
-
|
|
338
|
-
# R-223 (4/4) + R-227: provenance reconciliation. NON-BLOCKING —
|
|
339
|
-
# continue-on-error: true at the job level so reconcile failure cannot
|
|
340
|
-
# roll back the tag/publish. Failure opens a backfill issue.
|
|
341
|
-
reconcile:
|
|
342
|
-
name: Reconcile provenance
|
|
343
|
-
needs:
|
|
344
|
-
- detect
|
|
345
|
-
- publish-and-tag
|
|
346
|
-
if: needs.detect.outputs.should_publish == 'true'
|
|
347
|
-
runs-on: ubuntu-latest
|
|
348
|
-
timeout-minutes: 10
|
|
349
|
-
continue-on-error: true
|
|
350
|
-
env:
|
|
351
|
-
VERSION: ${{ needs.detect.outputs.version }}
|
|
352
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
353
|
-
steps:
|
|
354
|
-
- name: Checkout
|
|
355
|
-
uses: actions/checkout@v4.1
|
|
356
|
-
with:
|
|
357
|
-
fetch-depth: 0
|
|
358
|
-
timeout-minutes: 5
|
|
359
|
-
|
|
360
|
-
- name: Set up Node.js
|
|
361
|
-
uses: actions/setup-node@v4.0
|
|
362
|
-
with:
|
|
363
|
-
node-version: '{{NODE_VERSION}}'
|
|
364
|
-
registry-url: 'https://registry.npmjs.org'
|
|
365
|
-
timeout-minutes: 3
|
|
366
|
-
|
|
367
|
-
- name: Install cleo (freshly published version)
|
|
368
|
-
run: npm install -g "@cleocode/cleo@${VERSION#v}"
|
|
369
|
-
timeout-minutes: 5
|
|
370
|
-
|
|
371
|
-
- name: Reconcile release provenance
|
|
372
|
-
id: rec
|
|
373
|
-
continue-on-error: true
|
|
374
|
-
run: cleo release reconcile "$VERSION" --from-workflow --json
|
|
375
|
-
timeout-minutes: 5
|
|
376
|
-
|
|
377
|
-
# R-227: reconcile failure opens a backfill issue but MUST NOT roll
|
|
378
|
-
# back the tag or the publish.
|
|
379
|
-
- name: Open provenance-backfill issue on failure
|
|
380
|
-
if: steps.rec.outcome == 'failure'
|
|
381
|
-
run: |
|
|
382
|
-
gh issue create \
|
|
383
|
-
--label release-incident \
|
|
384
|
-
--title "Provenance backfill needed for ${VERSION}" \
|
|
385
|
-
--body "release-publish.yml reconcile job failed for ${VERSION}. Tag and publish completed successfully; only the provenance graph is incomplete. To recover, run \`cleo release reconcile ${VERSION} --backfill\` from a local checkout. Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
|
|
386
|
-
env:
|
|
387
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
388
|
-
timeout-minutes: 5
|