@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,322 +0,0 @@
|
|
|
1
|
-
# CLEO release pipeline — Rollback workflow.
|
|
2
|
-
#
|
|
3
|
-
# Generated from packages/cleo/templates/workflows/release-rollback.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.4 (R-250 — R-257, R-260 —
|
|
8
|
-
# R-263). Triggers ONLY on `workflow_dispatch` with `version`, `mode`
|
|
9
|
-
# (`metadata-only|full`), and `reason` inputs. For `mode=full`, opens a
|
|
10
|
-
# revert PR against `main` (NEVER pushes directly to `main` per ADR-065),
|
|
11
|
-
# then runs `npm deprecate` (best-effort) and finally records the rollback
|
|
12
|
-
# in the provenance graph via `cleo release reconcile --rollback`.
|
|
13
|
-
#
|
|
14
|
-
# Placeholders (replaced at scaffold time — see workflows/README.md):
|
|
15
|
-
# <NODE_VERSION> Node.js version (e.g. "22.x")
|
|
16
|
-
# <PUBLISHERS> Space-separated publisher list (e.g. "npm cargo")
|
|
17
|
-
# <NPM_PACKAGES> Space-separated npm package names to deprecate
|
|
18
|
-
# (e.g. "@cleocode/cleo @cleocode/core")
|
|
19
|
-
# <CARGO_CRATES> Space-separated cargo crate names to yank
|
|
20
|
-
# (e.g. "cleo-core cleo-cli")
|
|
21
|
-
# (Angle-brackets in this doc comment are intentional — they avoid being
|
|
22
|
-
# substituted by the {{...}} regex pass. The placeholders below use {{...}}.)
|
|
23
|
-
|
|
24
|
-
name: Release Rollback
|
|
25
|
-
|
|
26
|
-
# R-250: workflow_dispatch only — rollback is an explicit operator action,
|
|
27
|
-
# never an automatic response to push/release events.
|
|
28
|
-
# R-251: required inputs `version`, `mode` (choice), `reason` (free text).
|
|
29
|
-
on:
|
|
30
|
-
workflow_dispatch:
|
|
31
|
-
inputs:
|
|
32
|
-
version:
|
|
33
|
-
description: 'Version to roll back (e.g. v2026.6.0)'
|
|
34
|
-
type: string
|
|
35
|
-
required: true
|
|
36
|
-
mode:
|
|
37
|
-
description: 'Rollback mode — metadata-only deprecates packages; full also opens a revert PR'
|
|
38
|
-
type: choice
|
|
39
|
-
required: true
|
|
40
|
-
options:
|
|
41
|
-
- metadata-only
|
|
42
|
-
- full
|
|
43
|
-
reason:
|
|
44
|
-
description: 'Human-readable reason recorded with the rollback'
|
|
45
|
-
type: string
|
|
46
|
-
required: true
|
|
47
|
-
|
|
48
|
-
# R-252: workflow-level grants minimum read scope. The `revert` and
|
|
49
|
-
# `deprecate` jobs escalate per-job. `pull-requests: write` and
|
|
50
|
-
# `packages: write` MUST NOT be inherited workflow-wide so reconcile
|
|
51
|
-
# CANNOT silently widen its blast radius.
|
|
52
|
-
permissions:
|
|
53
|
-
contents: read
|
|
54
|
-
|
|
55
|
-
# R-257: serialize rollbacks against the same version. cancel-in-progress
|
|
56
|
-
# is FALSE so an in-flight rollback completes rather than leaving the
|
|
57
|
-
# revert PR / npm deprecate state half-applied.
|
|
58
|
-
concurrency:
|
|
59
|
-
group: rollback-${{ inputs.version }}
|
|
60
|
-
cancel-in-progress: false
|
|
61
|
-
|
|
62
|
-
# R-262: bash everywhere — no cross-platform shell drift.
|
|
63
|
-
defaults:
|
|
64
|
-
run:
|
|
65
|
-
shell: bash
|
|
66
|
-
|
|
67
|
-
jobs:
|
|
68
|
-
# R-253 (1/4): validate the rollback request. Confirms the tag exists
|
|
69
|
-
# and resolves the release commit SHA so the `revert` job has a fixed
|
|
70
|
-
# target. A missing tag MUST fail fast — no downstream side effects.
|
|
71
|
-
validate:
|
|
72
|
-
name: Validate rollback request
|
|
73
|
-
runs-on: ubuntu-latest
|
|
74
|
-
timeout-minutes: 5
|
|
75
|
-
outputs:
|
|
76
|
-
release_sha: ${{ steps.find.outputs.release_sha }}
|
|
77
|
-
steps:
|
|
78
|
-
# R-263: third-party Actions pinned to major+minor.
|
|
79
|
-
- name: Checkout
|
|
80
|
-
uses: actions/checkout@v4.1
|
|
81
|
-
with:
|
|
82
|
-
fetch-depth: 0
|
|
83
|
-
timeout-minutes: 5
|
|
84
|
-
|
|
85
|
-
- name: Resolve release commit SHA
|
|
86
|
-
id: find
|
|
87
|
-
run: |
|
|
88
|
-
set -euo pipefail
|
|
89
|
-
VERSION="${{ inputs.version }}"
|
|
90
|
-
if ! git rev-parse --verify "$VERSION" >/dev/null 2>&1; then
|
|
91
|
-
echo "::error::Tag $VERSION does not exist — cannot roll back a release that was never tagged."
|
|
92
|
-
exit 1
|
|
93
|
-
fi
|
|
94
|
-
# The release commit is the commit the tag points to (peeled).
|
|
95
|
-
RELEASE_SHA=$(git rev-list -n 1 "$VERSION")
|
|
96
|
-
if [ -z "$RELEASE_SHA" ]; then
|
|
97
|
-
echo "::error::Could not resolve commit SHA for tag $VERSION"
|
|
98
|
-
exit 1
|
|
99
|
-
fi
|
|
100
|
-
echo "release_sha=$RELEASE_SHA" >> "$GITHUB_OUTPUT"
|
|
101
|
-
echo "Resolved $VERSION -> $RELEASE_SHA"
|
|
102
|
-
timeout-minutes: 2
|
|
103
|
-
|
|
104
|
-
# R-253 (2/4) + R-254: open a revert PR for `mode=full`. NEVER pushes
|
|
105
|
-
# directly to `main` (ADR-065). The PR title MUST be
|
|
106
|
-
# `Revert release <version>: <reason>` and MUST carry the `rollback`
|
|
107
|
-
# label so branch-protection / triage automation can route it.
|
|
108
|
-
revert:
|
|
109
|
-
name: Open revert PR
|
|
110
|
-
needs: validate
|
|
111
|
-
if: inputs.mode == 'full'
|
|
112
|
-
runs-on: ubuntu-latest
|
|
113
|
-
timeout-minutes: 15
|
|
114
|
-
# R-252: per-job escalation — `contents: write` for the revert
|
|
115
|
-
# branch commit; `pull-requests: write` for `gh pr create`. NO
|
|
116
|
-
# `packages: write` here — that belongs to the `deprecate` job only.
|
|
117
|
-
permissions:
|
|
118
|
-
contents: write
|
|
119
|
-
pull-requests: write
|
|
120
|
-
env:
|
|
121
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
122
|
-
VERSION: ${{ inputs.version }}
|
|
123
|
-
REASON: ${{ inputs.reason }}
|
|
124
|
-
RELEASE_SHA: ${{ needs.validate.outputs.release_sha }}
|
|
125
|
-
steps:
|
|
126
|
-
- name: Checkout
|
|
127
|
-
uses: actions/checkout@v4.1
|
|
128
|
-
with:
|
|
129
|
-
fetch-depth: 0
|
|
130
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
131
|
-
timeout-minutes: 5
|
|
132
|
-
|
|
133
|
-
- name: Configure git identity
|
|
134
|
-
run: |
|
|
135
|
-
git config user.email "actions@github.com"
|
|
136
|
-
git config user.name "GitHub Actions"
|
|
137
|
-
timeout-minutes: 1
|
|
138
|
-
|
|
139
|
-
# R-254: cut `revert/<version>` branch, run `git revert -n` against
|
|
140
|
-
# the resolved release commit, commit with the canonical subject.
|
|
141
|
-
# Branch is cut from the action's checked-out main HEAD so the
|
|
142
|
-
# revert applies on top of current main.
|
|
143
|
-
- name: Create revert branch and commit
|
|
144
|
-
run: |
|
|
145
|
-
set -euo pipefail
|
|
146
|
-
BRANCH="revert/${VERSION}"
|
|
147
|
-
git checkout -b "$BRANCH"
|
|
148
|
-
# `-n` (no-commit) lets us craft the subject line ourselves.
|
|
149
|
-
# `-m 1` selects the first parent when the release commit is a
|
|
150
|
-
# merge (the bump-PR merge into main), which is the canonical
|
|
151
|
-
# release-commit shape for ADR-065 PR-gated releases.
|
|
152
|
-
if git rev-parse --verify "${RELEASE_SHA}^2" >/dev/null 2>&1; then
|
|
153
|
-
git revert -n -m 1 "$RELEASE_SHA"
|
|
154
|
-
else
|
|
155
|
-
git revert -n "$RELEASE_SHA"
|
|
156
|
-
fi
|
|
157
|
-
git commit -m "Revert release ${VERSION}: ${REASON}"
|
|
158
|
-
timeout-minutes: 5
|
|
159
|
-
|
|
160
|
-
# R-254 (continued): push the revert branch — NEVER `git push origin
|
|
161
|
-
# main`. Branch protection on main is the second line of defence,
|
|
162
|
-
# but the workflow MUST NOT rely on it.
|
|
163
|
-
- name: Push revert branch
|
|
164
|
-
run: git push -u origin "revert/${VERSION}"
|
|
165
|
-
timeout-minutes: 5
|
|
166
|
-
|
|
167
|
-
# R-254 (continued): open the revert PR with the canonical title
|
|
168
|
-
# and the `rollback` label. The PR body links back to this
|
|
169
|
-
# workflow run so reviewers can audit the rollback context.
|
|
170
|
-
- name: Open revert PR
|
|
171
|
-
run: |
|
|
172
|
-
set -euo pipefail
|
|
173
|
-
gh pr create \
|
|
174
|
-
--base main \
|
|
175
|
-
--head "revert/${VERSION}" \
|
|
176
|
-
--title "Revert release ${VERSION}: ${REASON}" \
|
|
177
|
-
--label rollback \
|
|
178
|
-
--body "Automated rollback PR for release **${VERSION}**.
|
|
179
|
-
|
|
180
|
-
**Reason**: ${REASON}
|
|
181
|
-
|
|
182
|
-
This PR reverts commit \`${RELEASE_SHA}\` (the merge commit the
|
|
183
|
-
release tag points to). It was opened by the \`release-rollback.yml\`
|
|
184
|
-
workflow — see the workflow run for full context:
|
|
185
|
-
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
186
|
-
|
|
187
|
-
After this PR merges, the package registries have already been marked
|
|
188
|
-
as deprecated by the \`deprecate\` job, and the provenance graph has
|
|
189
|
-
been updated by the \`reconcile-rollback\` job."
|
|
190
|
-
timeout-minutes: 5
|
|
191
|
-
|
|
192
|
-
# R-253 (3/4) + R-255: deprecate published artifacts. Best-effort —
|
|
193
|
-
# `continue-on-error: true` so a transient registry hiccup CANNOT
|
|
194
|
-
# block the reconcile-rollback step. Runs for BOTH `metadata-only`
|
|
195
|
-
# and `full` modes since deprecation is the core rollback signal.
|
|
196
|
-
deprecate:
|
|
197
|
-
name: Deprecate published artifacts
|
|
198
|
-
needs: validate
|
|
199
|
-
runs-on: ubuntu-latest
|
|
200
|
-
timeout-minutes: 15
|
|
201
|
-
continue-on-error: true
|
|
202
|
-
# R-252: per-job `packages: write` for npm deprecate. NO
|
|
203
|
-
# `contents: write` — this job MUST NOT touch git refs.
|
|
204
|
-
permissions:
|
|
205
|
-
contents: read
|
|
206
|
-
packages: write
|
|
207
|
-
env:
|
|
208
|
-
VERSION: ${{ inputs.version }}
|
|
209
|
-
REASON: ${{ inputs.reason }}
|
|
210
|
-
# Hoisted into env so downstream `contains(env.PUBLISHERS, ...)`
|
|
211
|
-
# is a dynamic expression (avoids actionlint constant-expression
|
|
212
|
-
# warnings — same pattern as release-publish.yml.tmpl).
|
|
213
|
-
PUBLISHERS: '{{PUBLISHERS}}'
|
|
214
|
-
NPM_PACKAGES: '{{NPM_PACKAGES}}'
|
|
215
|
-
CARGO_CRATES: '{{CARGO_CRATES}}'
|
|
216
|
-
steps:
|
|
217
|
-
- name: Set up Node.js
|
|
218
|
-
uses: actions/setup-node@v4.0
|
|
219
|
-
with:
|
|
220
|
-
node-version: '{{NODE_VERSION}}'
|
|
221
|
-
registry-url: 'https://registry.npmjs.org'
|
|
222
|
-
timeout-minutes: 3
|
|
223
|
-
|
|
224
|
-
# R-255: `npm deprecate <pkg>@<version> "Rolled back: <reason>"`
|
|
225
|
-
# per published artifact. Failure is NON-FATAL — each iteration
|
|
226
|
-
# is wrapped so one package's failure cannot stop the others.
|
|
227
|
-
- name: Deprecate npm packages
|
|
228
|
-
if: contains(env.PUBLISHERS, 'npm')
|
|
229
|
-
run: |
|
|
230
|
-
set -uo pipefail
|
|
231
|
-
STRIPPED="${VERSION#v}"
|
|
232
|
-
for pkg in ${NPM_PACKAGES}; do
|
|
233
|
-
if npm deprecate "$pkg@$STRIPPED" "Rolled back: ${REASON}"; then
|
|
234
|
-
echo "Deprecated $pkg@$STRIPPED"
|
|
235
|
-
else
|
|
236
|
-
echo "::warning::Failed to deprecate $pkg@$STRIPPED — continuing"
|
|
237
|
-
fi
|
|
238
|
-
done
|
|
239
|
-
env:
|
|
240
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
241
|
-
timeout-minutes: 10
|
|
242
|
-
|
|
243
|
-
# R-255 (extension for cargo): `cargo yank` is the cargo analogue
|
|
244
|
-
# of `npm deprecate`. Also best-effort — a missing crate or a
|
|
245
|
-
# rate-limit response MUST NOT block reconcile.
|
|
246
|
-
- name: Yank cargo crates
|
|
247
|
-
if: contains(env.PUBLISHERS, 'cargo')
|
|
248
|
-
run: |
|
|
249
|
-
set -uo pipefail
|
|
250
|
-
STRIPPED="${VERSION#v}"
|
|
251
|
-
for crate in ${CARGO_CRATES}; do
|
|
252
|
-
if cargo yank --version "$STRIPPED" "$crate"; then
|
|
253
|
-
echo "Yanked $crate@$STRIPPED"
|
|
254
|
-
else
|
|
255
|
-
echo "::warning::Failed to yank $crate@$STRIPPED — continuing"
|
|
256
|
-
fi
|
|
257
|
-
done
|
|
258
|
-
env:
|
|
259
|
-
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_TOKEN }}
|
|
260
|
-
timeout-minutes: 10
|
|
261
|
-
|
|
262
|
-
# R-253 (4/4) + R-256: record the rollback in the provenance graph via
|
|
263
|
-
# `cleo release reconcile <version> --rollback --reason "<reason>"`.
|
|
264
|
-
# This writes `releases.rolled_back_at` and inserts a
|
|
265
|
-
# `release_changes` row with `change_type='revert'`. Runs after
|
|
266
|
-
# `validate` regardless of `revert` / `deprecate` outcome — provenance
|
|
267
|
-
# must record the operator's intent even if individual side-effects
|
|
268
|
-
# failed.
|
|
269
|
-
reconcile-rollback:
|
|
270
|
-
name: Record rollback in provenance graph
|
|
271
|
-
needs:
|
|
272
|
-
- validate
|
|
273
|
-
if: always() && needs.validate.result == 'success'
|
|
274
|
-
runs-on: ubuntu-latest
|
|
275
|
-
timeout-minutes: 10
|
|
276
|
-
env:
|
|
277
|
-
VERSION: ${{ inputs.version }}
|
|
278
|
-
REASON: ${{ inputs.reason }}
|
|
279
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
280
|
-
steps:
|
|
281
|
-
- name: Checkout
|
|
282
|
-
uses: actions/checkout@v4.1
|
|
283
|
-
with:
|
|
284
|
-
fetch-depth: 0
|
|
285
|
-
timeout-minutes: 5
|
|
286
|
-
|
|
287
|
-
- name: Set up Node.js
|
|
288
|
-
uses: actions/setup-node@v4.0
|
|
289
|
-
with:
|
|
290
|
-
node-version: '{{NODE_VERSION}}'
|
|
291
|
-
registry-url: 'https://registry.npmjs.org'
|
|
292
|
-
timeout-minutes: 3
|
|
293
|
-
|
|
294
|
-
- name: Install cleo
|
|
295
|
-
run: npm install -g @cleocode/cleo
|
|
296
|
-
timeout-minutes: 5
|
|
297
|
-
|
|
298
|
-
# R-256: reconcile MUST write rollback marker into `releases` +
|
|
299
|
-
# `release_changes`. The CLI is the canonical writer — workflow
|
|
300
|
-
# YAML never touches the DB directly.
|
|
301
|
-
- name: Record rollback
|
|
302
|
-
run: cleo release reconcile "${VERSION}" --rollback --reason "${REASON}"
|
|
303
|
-
timeout-minutes: 5
|
|
304
|
-
|
|
305
|
-
# Recovery hint in the job summary so an operator who sees a
|
|
306
|
-
# half-applied rollback can finish the job from a local checkout.
|
|
307
|
-
- name: Emit recovery hint
|
|
308
|
-
if: always()
|
|
309
|
-
run: |
|
|
310
|
-
{
|
|
311
|
-
echo "## Release Rollback — ${VERSION}"
|
|
312
|
-
echo ""
|
|
313
|
-
echo "**Mode**: \`${{ inputs.mode }}\`"
|
|
314
|
-
echo "**Reason**: ${REASON}"
|
|
315
|
-
echo ""
|
|
316
|
-
echo "**Recovery commands** (if anything went wrong):"
|
|
317
|
-
echo ""
|
|
318
|
-
echo "\`\`\`"
|
|
319
|
-
echo "cleo release reconcile ${VERSION} --rollback --reason \"${REASON}\""
|
|
320
|
-
echo "\`\`\`"
|
|
321
|
-
} >> "$GITHUB_STEP_SUMMARY"
|
|
322
|
-
timeout-minutes: 1
|