@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.
@@ -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