@cleocode/cleo 2026.5.105 → 2026.5.107
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,214 +0,0 @@
|
|
|
1
|
-
# CLEO release pipeline — Best-effort fanout workflow.
|
|
2
|
-
#
|
|
3
|
-
# Generated from packages/cleo/templates/workflows/release-fanout.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.3 (R-240 — R-244, R-260 —
|
|
8
|
-
# R-263). Triggers on `release: published` (the GitHub Release event, NOT
|
|
9
|
-
# `release: created` which fires on drafts). Each downstream job is an
|
|
10
|
-
# independent best-effort fanout — every job carries `continue-on-error:
|
|
11
|
-
# true` so a failed docs deploy, docker retag, sentinel notify, studio
|
|
12
|
-
# rebuild, or nightly trigger CANNOT mark the release itself as failed.
|
|
13
|
-
# These jobs MUST NOT be configured as required status checks (R-244).
|
|
14
|
-
#
|
|
15
|
-
# Placeholders (replaced at scaffold time — see workflows/README.md):
|
|
16
|
-
# <NODE_VERSION> Node.js version (e.g. "22.x")
|
|
17
|
-
# <INSTALL_CMD> Install command (e.g. "pnpm install --frozen-lockfile")
|
|
18
|
-
# <DOCS_BUILD_CMD> Docs build command (e.g. "pnpm --filter @cleocode/docs run build")
|
|
19
|
-
# <ENABLE_DOCS_DEPLOY> Toggle docs-deploy job ("true" | "false")
|
|
20
|
-
# <ENABLE_DOCKER_RETAG> Toggle docker-retag job ("true" | "false")
|
|
21
|
-
# <ENABLE_SENTINEL_NOTIFY> Toggle sentinel-notify job ("true" | "false")
|
|
22
|
-
# <ENABLE_STUDIO_DEPLOY> Toggle studio-deploy job ("true" | "false")
|
|
23
|
-
# <ENABLE_NIGHTLY_TRIGGER> Toggle nightly-trigger job ("true" | "false")
|
|
24
|
-
# <DOCKER_IMAGE> Docker image name (e.g. "cleocode/cleo")
|
|
25
|
-
# <DOCKER_HUB_USER> Docker Hub login user (e.g. "cleocode")
|
|
26
|
-
# <SENTINEL_WEBHOOK_URL> Sentinel webhook URL (HTTPS)
|
|
27
|
-
# <STUDIO_DEPLOY_HOOK> Studio deploy hook URL (HTTPS)
|
|
28
|
-
# (Angle-brackets in this doc comment are intentional — they avoid being
|
|
29
|
-
# substituted by the {{...}} regex pass. The placeholders below use {{...}}.)
|
|
30
|
-
|
|
31
|
-
name: Release Fanout
|
|
32
|
-
|
|
33
|
-
# R-240: trigger on `release: published` ONLY. Draft releases (which fire
|
|
34
|
-
# `release: created`) MUST NOT trigger the fanout — operators expect this
|
|
35
|
-
# to run AFTER the publish + tag workflow has shipped the artifacts.
|
|
36
|
-
on:
|
|
37
|
-
release:
|
|
38
|
-
types: [published]
|
|
39
|
-
|
|
40
|
-
# R-241: workflow-level grants the minimum read scope. Per-job permissions
|
|
41
|
-
# escalate ONLY for the jobs that need them (e.g. `pages: write` for docs
|
|
42
|
-
# deploy). No top-level write scope — fanout is intentionally read-only at
|
|
43
|
-
# the workflow level.
|
|
44
|
-
permissions:
|
|
45
|
-
contents: read
|
|
46
|
-
|
|
47
|
-
# R-243: serialize fanouts against the same release tag. cancel-in-progress
|
|
48
|
-
# is FALSE so an in-flight fanout completes rather than losing partial
|
|
49
|
-
# work (e.g. a half-pushed docker tag).
|
|
50
|
-
concurrency:
|
|
51
|
-
group: fanout-${{ github.event.release.tag_name }}
|
|
52
|
-
cancel-in-progress: false
|
|
53
|
-
|
|
54
|
-
# R-262: bash everywhere — no cross-platform shell drift across the fanout
|
|
55
|
-
# jobs (they all run on ubuntu-latest but the contract is explicit).
|
|
56
|
-
defaults:
|
|
57
|
-
run:
|
|
58
|
-
shell: bash
|
|
59
|
-
|
|
60
|
-
jobs:
|
|
61
|
-
# R-242 (1/5): deploy generated documentation site to GitHub Pages.
|
|
62
|
-
# continue-on-error: true so a docs-build regression cannot fail the
|
|
63
|
-
# release. Disabled by default — flip {{ENABLE_DOCS_DEPLOY}} to "true"
|
|
64
|
-
# at scaffold time. The flag is hoisted into env so the `if:` becomes a
|
|
65
|
-
# dynamic expression (avoids actionlint constant-expression warning).
|
|
66
|
-
docs-deploy:
|
|
67
|
-
name: Deploy docs to GitHub Pages
|
|
68
|
-
runs-on: ubuntu-latest
|
|
69
|
-
continue-on-error: true
|
|
70
|
-
timeout-minutes: 15
|
|
71
|
-
env:
|
|
72
|
-
ENABLE_DOCS_DEPLOY: '{{ENABLE_DOCS_DEPLOY}}'
|
|
73
|
-
if: ${{ env.ENABLE_DOCS_DEPLOY == 'true' }}
|
|
74
|
-
# R-241 (per-job): pages: write + id-token: write granted ONLY for the
|
|
75
|
-
# docs-deploy job — other fanout jobs MUST NOT inherit GitHub Pages
|
|
76
|
-
# write scope.
|
|
77
|
-
permissions:
|
|
78
|
-
contents: read
|
|
79
|
-
pages: write
|
|
80
|
-
id-token: write
|
|
81
|
-
steps:
|
|
82
|
-
# R-263: third-party Actions pinned to major+minor.
|
|
83
|
-
- name: Checkout
|
|
84
|
-
uses: actions/checkout@v4.1
|
|
85
|
-
with:
|
|
86
|
-
fetch-depth: 1
|
|
87
|
-
timeout-minutes: 5
|
|
88
|
-
|
|
89
|
-
- name: Set up Node.js
|
|
90
|
-
uses: actions/setup-node@v4.0
|
|
91
|
-
with:
|
|
92
|
-
node-version: '{{NODE_VERSION}}'
|
|
93
|
-
timeout-minutes: 3
|
|
94
|
-
|
|
95
|
-
- name: Install dependencies
|
|
96
|
-
run: {{INSTALL_CMD}}
|
|
97
|
-
timeout-minutes: 5
|
|
98
|
-
|
|
99
|
-
- name: Build docs
|
|
100
|
-
run: {{DOCS_BUILD_CMD}}
|
|
101
|
-
timeout-minutes: 10
|
|
102
|
-
|
|
103
|
-
- name: Configure Pages
|
|
104
|
-
uses: actions/configure-pages@v5.0
|
|
105
|
-
timeout-minutes: 2
|
|
106
|
-
|
|
107
|
-
- name: Upload Pages artifact
|
|
108
|
-
uses: actions/upload-pages-artifact@v3.0
|
|
109
|
-
with:
|
|
110
|
-
path: docs/dist
|
|
111
|
-
timeout-minutes: 5
|
|
112
|
-
|
|
113
|
-
- name: Deploy to GitHub Pages
|
|
114
|
-
uses: actions/deploy-pages@v4.0
|
|
115
|
-
timeout-minutes: 5
|
|
116
|
-
|
|
117
|
-
# R-242 (2/5): retag the published docker image to `latest`. Failure here
|
|
118
|
-
# does NOT roll back the release — operators can re-run manually.
|
|
119
|
-
docker-retag:
|
|
120
|
-
name: Retag docker image to latest
|
|
121
|
-
runs-on: ubuntu-latest
|
|
122
|
-
continue-on-error: true
|
|
123
|
-
timeout-minutes: 10
|
|
124
|
-
env:
|
|
125
|
-
ENABLE_DOCKER_RETAG: '{{ENABLE_DOCKER_RETAG}}'
|
|
126
|
-
DOCKER_IMAGE: '{{DOCKER_IMAGE}}'
|
|
127
|
-
DOCKER_HUB_USER: '{{DOCKER_HUB_USER}}'
|
|
128
|
-
VERSION: ${{ github.event.release.tag_name }}
|
|
129
|
-
if: ${{ env.ENABLE_DOCKER_RETAG == 'true' }}
|
|
130
|
-
steps:
|
|
131
|
-
- name: Log in to Docker Hub
|
|
132
|
-
run: |
|
|
133
|
-
set -euo pipefail
|
|
134
|
-
echo "${{ secrets.DOCKER_HUB_TOKEN }}" | \
|
|
135
|
-
docker login -u "$DOCKER_HUB_USER" --password-stdin
|
|
136
|
-
timeout-minutes: 3
|
|
137
|
-
|
|
138
|
-
- name: Pull, retag, push
|
|
139
|
-
run: |
|
|
140
|
-
set -euo pipefail
|
|
141
|
-
docker pull "${DOCKER_IMAGE}:${VERSION}"
|
|
142
|
-
docker tag "${DOCKER_IMAGE}:${VERSION}" "${DOCKER_IMAGE}:latest"
|
|
143
|
-
docker push "${DOCKER_IMAGE}:latest"
|
|
144
|
-
timeout-minutes: 5
|
|
145
|
-
|
|
146
|
-
# R-242 (3/5): POST a `release.published` event to the Sentinel webhook
|
|
147
|
-
# so downstream monitoring can pick the release up. Best-effort — a
|
|
148
|
-
# webhook outage MUST NOT mark the release as failed.
|
|
149
|
-
sentinel-notify:
|
|
150
|
-
name: Notify Sentinel of release
|
|
151
|
-
runs-on: ubuntu-latest
|
|
152
|
-
continue-on-error: true
|
|
153
|
-
timeout-minutes: 5
|
|
154
|
-
env:
|
|
155
|
-
ENABLE_SENTINEL_NOTIFY: '{{ENABLE_SENTINEL_NOTIFY}}'
|
|
156
|
-
SENTINEL_WEBHOOK_URL: '{{SENTINEL_WEBHOOK_URL}}'
|
|
157
|
-
SENTINEL_TOKEN: ${{ secrets.SENTINEL_TOKEN }}
|
|
158
|
-
VERSION: ${{ github.event.release.tag_name }}
|
|
159
|
-
RELEASE_URL: ${{ github.event.release.html_url }}
|
|
160
|
-
if: ${{ env.ENABLE_SENTINEL_NOTIFY == 'true' }}
|
|
161
|
-
steps:
|
|
162
|
-
- name: POST release.published to Sentinel
|
|
163
|
-
run: |
|
|
164
|
-
set -euo pipefail
|
|
165
|
-
curl -fSL -X POST "$SENTINEL_WEBHOOK_URL" \
|
|
166
|
-
-H 'Content-Type: application/json' \
|
|
167
|
-
-H "Authorization: Bearer ${SENTINEL_TOKEN}" \
|
|
168
|
-
-d "{\"event\":\"release.published\",\"version\":\"${VERSION}\",\"url\":\"${RELEASE_URL}\"}"
|
|
169
|
-
timeout-minutes: 3
|
|
170
|
-
|
|
171
|
-
# R-242 (4/5): trigger a Studio rebuild via deploy hook. Studio runs
|
|
172
|
-
# outside this repo, so this job is a fire-and-forget POST.
|
|
173
|
-
studio-deploy:
|
|
174
|
-
name: Trigger Studio rebuild
|
|
175
|
-
runs-on: ubuntu-latest
|
|
176
|
-
continue-on-error: true
|
|
177
|
-
timeout-minutes: 20
|
|
178
|
-
env:
|
|
179
|
-
ENABLE_STUDIO_DEPLOY: '{{ENABLE_STUDIO_DEPLOY}}'
|
|
180
|
-
STUDIO_DEPLOY_HOOK: '{{STUDIO_DEPLOY_HOOK}}'
|
|
181
|
-
STUDIO_DEPLOY_TOKEN: ${{ secrets.STUDIO_DEPLOY_TOKEN }}
|
|
182
|
-
VERSION: ${{ github.event.release.tag_name }}
|
|
183
|
-
if: ${{ env.ENABLE_STUDIO_DEPLOY == 'true' }}
|
|
184
|
-
steps:
|
|
185
|
-
- name: POST to Studio deploy hook
|
|
186
|
-
run: |
|
|
187
|
-
set -euo pipefail
|
|
188
|
-
curl -fSL -X POST "$STUDIO_DEPLOY_HOOK" \
|
|
189
|
-
-H "Authorization: Bearer ${STUDIO_DEPLOY_TOKEN}" \
|
|
190
|
-
-H 'Content-Type: application/json' \
|
|
191
|
-
-d "{\"version\":\"${VERSION}\"}"
|
|
192
|
-
timeout-minutes: 10
|
|
193
|
-
|
|
194
|
-
# R-242 (5/5): dispatch the nightly e2e workflow against the freshly
|
|
195
|
-
# published release version. Decoupled from the release path — a failed
|
|
196
|
-
# dispatch is logged but does NOT fail the release.
|
|
197
|
-
nightly-trigger:
|
|
198
|
-
name: Dispatch nightly e2e for release
|
|
199
|
-
runs-on: ubuntu-latest
|
|
200
|
-
continue-on-error: true
|
|
201
|
-
timeout-minutes: 3
|
|
202
|
-
env:
|
|
203
|
-
ENABLE_NIGHTLY_TRIGGER: '{{ENABLE_NIGHTLY_TRIGGER}}'
|
|
204
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
205
|
-
VERSION: ${{ github.event.release.tag_name }}
|
|
206
|
-
if: ${{ env.ENABLE_NIGHTLY_TRIGGER == 'true' }}
|
|
207
|
-
steps:
|
|
208
|
-
- name: Dispatch nightly e2e
|
|
209
|
-
run: |
|
|
210
|
-
set -euo pipefail
|
|
211
|
-
gh workflow run nightly-e2e.yml \
|
|
212
|
-
--repo "${{ github.repository }}" \
|
|
213
|
-
-f release_version="$VERSION"
|
|
214
|
-
timeout-minutes: 2
|
|
@@ -1,296 +0,0 @@
|
|
|
1
|
-
# CLEO release pipeline — Prepare bump-PR workflow.
|
|
2
|
-
#
|
|
3
|
-
# Generated from packages/cleo/templates/workflows/release-prepare.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.1 (R-200 — R-210, R-260 —
|
|
8
|
-
# R-263). Triggers ONLY on workflow_dispatch; runs preflight (lint +
|
|
9
|
-
# typecheck + test + build) BEFORE creating any commit; prepares the
|
|
10
|
-
# release/<version> branch and opens the bump-PR via `gh pr create`.
|
|
11
|
-
#
|
|
12
|
-
# Placeholders (replaced at scaffold time — see workflows/README.md):
|
|
13
|
-
# <NODE_VERSION> Node.js version (e.g. "22.x")
|
|
14
|
-
# <INSTALL_CMD> Install command (e.g. "pnpm install --frozen-lockfile")
|
|
15
|
-
# <LINT_CMD> tool:lint (ADR-061 resolved)
|
|
16
|
-
# <TYPECHECK_CMD> tool:typecheck (ADR-061 resolved)
|
|
17
|
-
# <TEST_CMD> tool:test (ADR-061 resolved)
|
|
18
|
-
# <BUILD_CMD> tool:build (ADR-061 resolved)
|
|
19
|
-
# <BRANCH_PREFIX> Release branch prefix (default: "release")
|
|
20
|
-
# <PR_LABEL> GitHub label applied to bump-PR (default: "release")
|
|
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 Prepare
|
|
25
|
-
|
|
26
|
-
# R-200: workflow_dispatch only — MUST NOT trigger on push to any branch.
|
|
27
|
-
# R-201: required input `version`; optional `plan-blob-sha256`, `epic`, `channel`.
|
|
28
|
-
on:
|
|
29
|
-
workflow_dispatch:
|
|
30
|
-
inputs:
|
|
31
|
-
version:
|
|
32
|
-
description: 'Version to release (e.g. v2026.6.0)'
|
|
33
|
-
type: string
|
|
34
|
-
required: true
|
|
35
|
-
plan-blob-sha256:
|
|
36
|
-
description: 'SHA256 of pre-computed plan JSON (optional)'
|
|
37
|
-
type: string
|
|
38
|
-
required: false
|
|
39
|
-
epic:
|
|
40
|
-
description: 'Epic task ID (e.g. T9999) used by `cleo release plan`'
|
|
41
|
-
type: string
|
|
42
|
-
required: false
|
|
43
|
-
channel:
|
|
44
|
-
description: 'Release channel'
|
|
45
|
-
type: choice
|
|
46
|
-
required: false
|
|
47
|
-
default: latest
|
|
48
|
-
options:
|
|
49
|
-
- latest
|
|
50
|
-
- beta
|
|
51
|
-
- alpha
|
|
52
|
-
- rc
|
|
53
|
-
|
|
54
|
-
# R-202: contents:write (commit + branch), pull-requests:write (gh pr create),
|
|
55
|
-
# id-token:write (signed tags only). MUST NOT request packages:write — npm
|
|
56
|
-
# publish belongs to release-publish.yml.
|
|
57
|
-
permissions:
|
|
58
|
-
contents: write
|
|
59
|
-
pull-requests: write
|
|
60
|
-
id-token: write
|
|
61
|
-
|
|
62
|
-
# R-207: concurrent prepare runs against the same version queue rather than
|
|
63
|
-
# collide. cancel-in-progress=false preserves in-flight work.
|
|
64
|
-
concurrency:
|
|
65
|
-
group: release-prepare-${{ inputs.version }}
|
|
66
|
-
cancel-in-progress: false
|
|
67
|
-
|
|
68
|
-
# R-262: bash everywhere — no cross-platform shell drift.
|
|
69
|
-
defaults:
|
|
70
|
-
run:
|
|
71
|
-
shell: bash
|
|
72
|
-
|
|
73
|
-
jobs:
|
|
74
|
-
# R-204: lint + typecheck + test + build BEFORE any commit. Failure here
|
|
75
|
-
# MUST fail the workflow and prevent `prepare` from running.
|
|
76
|
-
preflight:
|
|
77
|
-
name: Preflight (lint + typecheck + test + build)
|
|
78
|
-
runs-on: ubuntu-latest
|
|
79
|
-
timeout-minutes: 20
|
|
80
|
-
steps:
|
|
81
|
-
# R-263: third-party Actions pinned to major+minor.
|
|
82
|
-
- name: Checkout
|
|
83
|
-
uses: actions/checkout@v4.1
|
|
84
|
-
with:
|
|
85
|
-
fetch-depth: 0
|
|
86
|
-
timeout-minutes: 5
|
|
87
|
-
|
|
88
|
-
- name: Set up Node.js
|
|
89
|
-
uses: actions/setup-node@v4.0
|
|
90
|
-
with:
|
|
91
|
-
node-version: '{{NODE_VERSION}}'
|
|
92
|
-
timeout-minutes: 3
|
|
93
|
-
|
|
94
|
-
- name: Install dependencies
|
|
95
|
-
run: {{INSTALL_CMD}}
|
|
96
|
-
timeout-minutes: 5
|
|
97
|
-
|
|
98
|
-
- name: Lint
|
|
99
|
-
run: {{LINT_CMD}}
|
|
100
|
-
timeout-minutes: 5
|
|
101
|
-
|
|
102
|
-
- name: Typecheck
|
|
103
|
-
run: {{TYPECHECK_CMD}}
|
|
104
|
-
timeout-minutes: 5
|
|
105
|
-
|
|
106
|
-
- name: Test
|
|
107
|
-
run: {{TEST_CMD}}
|
|
108
|
-
timeout-minutes: 10
|
|
109
|
-
|
|
110
|
-
- name: Build
|
|
111
|
-
run: {{BUILD_CMD}}
|
|
112
|
-
timeout-minutes: 10
|
|
113
|
-
|
|
114
|
-
# R-208: status check `release-prepare/preflight` is reported by GitHub
|
|
115
|
-
# automatically against the bump-PR head SHA once the PR is open. No
|
|
116
|
-
# extra step is required — branch protection MUST require this check.
|
|
117
|
-
|
|
118
|
-
# R-203: prepare needs preflight.
|
|
119
|
-
# R-205: plan resolution → version bump → CHANGELOG → commit → push → bump-PR.
|
|
120
|
-
prepare:
|
|
121
|
-
name: Prepare bump-PR
|
|
122
|
-
runs-on: ubuntu-latest
|
|
123
|
-
needs: preflight
|
|
124
|
-
timeout-minutes: 20
|
|
125
|
-
outputs:
|
|
126
|
-
branch: ${{ steps.branch.outputs.name }}
|
|
127
|
-
branch_created: ${{ steps.push.outputs.created }}
|
|
128
|
-
env:
|
|
129
|
-
# R-210: only GITHUB_TOKEN required. NPM_TOKEN is publish-only.
|
|
130
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
131
|
-
steps:
|
|
132
|
-
- name: Checkout
|
|
133
|
-
uses: actions/checkout@v4.1
|
|
134
|
-
with:
|
|
135
|
-
fetch-depth: 0
|
|
136
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
137
|
-
timeout-minutes: 5
|
|
138
|
-
|
|
139
|
-
- name: Configure git identity
|
|
140
|
-
run: |
|
|
141
|
-
git config user.email "actions@github.com"
|
|
142
|
-
git config user.name "GitHub Actions"
|
|
143
|
-
timeout-minutes: 1
|
|
144
|
-
|
|
145
|
-
- name: Cut release branch
|
|
146
|
-
id: branch
|
|
147
|
-
run: |
|
|
148
|
-
BRANCH="{{BRANCH_PREFIX}}/${{ inputs.version }}"
|
|
149
|
-
git checkout -b "$BRANCH"
|
|
150
|
-
echo "name=$BRANCH" >> "$GITHUB_OUTPUT"
|
|
151
|
-
timeout-minutes: 2
|
|
152
|
-
|
|
153
|
-
- name: Set up Node.js
|
|
154
|
-
uses: actions/setup-node@v4.0
|
|
155
|
-
with:
|
|
156
|
-
node-version: '{{NODE_VERSION}}'
|
|
157
|
-
timeout-minutes: 3
|
|
158
|
-
|
|
159
|
-
- name: Install dependencies
|
|
160
|
-
run: {{INSTALL_CMD}}
|
|
161
|
-
timeout-minutes: 5
|
|
162
|
-
|
|
163
|
-
# R-205(a)/(b): resolve plan — call `cleo release plan` if sha is empty,
|
|
164
|
-
# otherwise verify the existing plan blob via sha256.
|
|
165
|
-
- name: Resolve release plan
|
|
166
|
-
id: plan
|
|
167
|
-
run: |
|
|
168
|
-
mkdir -p .cleo/release
|
|
169
|
-
PLAN_FILE=".cleo/release/${{ inputs.version }}.plan.json"
|
|
170
|
-
if [ -z "${{ inputs.plan-blob-sha256 }}" ]; then
|
|
171
|
-
echo "No plan-blob-sha256 input — generating fresh plan."
|
|
172
|
-
EPIC_ARGS=()
|
|
173
|
-
if [ -n "${{ inputs.epic }}" ]; then
|
|
174
|
-
EPIC_ARGS=(--epic "${{ inputs.epic }}")
|
|
175
|
-
fi
|
|
176
|
-
cleo release plan "${{ inputs.version }}" "${EPIC_ARGS[@]}" --json > "$PLAN_FILE"
|
|
177
|
-
else
|
|
178
|
-
echo "Verifying pre-computed plan against sha256=${{ inputs.plan-blob-sha256 }}"
|
|
179
|
-
if [ ! -f "$PLAN_FILE" ]; then
|
|
180
|
-
echo "::error::Plan file $PLAN_FILE not found — cannot verify sha256"
|
|
181
|
-
exit 1
|
|
182
|
-
fi
|
|
183
|
-
ACTUAL=$(sha256sum "$PLAN_FILE" | awk '{print $1}')
|
|
184
|
-
if [ "$ACTUAL" != "${{ inputs.plan-blob-sha256 }}" ]; then
|
|
185
|
-
echo "::error::Plan sha256 mismatch — expected ${{ inputs.plan-blob-sha256 }}, got $ACTUAL"
|
|
186
|
-
exit 1
|
|
187
|
-
fi
|
|
188
|
-
fi
|
|
189
|
-
echo "plan_file=$PLAN_FILE" >> "$GITHUB_OUTPUT"
|
|
190
|
-
timeout-minutes: 5
|
|
191
|
-
|
|
192
|
-
# R-205(c): bump version files via `cleo version-bump`.
|
|
193
|
-
- name: Bump version
|
|
194
|
-
run: cleo version-bump --version "${{ inputs.version }}"
|
|
195
|
-
timeout-minutes: 3
|
|
196
|
-
|
|
197
|
-
# R-205(d): regenerate CHANGELOG via the preserved `cleo release
|
|
198
|
-
# changelog` primitive, scoped to commits since the previous tag.
|
|
199
|
-
- name: Regenerate CHANGELOG
|
|
200
|
-
run: |
|
|
201
|
-
PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
202
|
-
if [ -n "$PREV_TAG" ]; then
|
|
203
|
-
cleo release changelog --since "$PREV_TAG"
|
|
204
|
-
else
|
|
205
|
-
cleo release changelog
|
|
206
|
-
fi
|
|
207
|
-
timeout-minutes: 3
|
|
208
|
-
|
|
209
|
-
# R-205(e): commit with the canonical subject line.
|
|
210
|
-
- name: Commit prepare
|
|
211
|
-
run: |
|
|
212
|
-
git add -A
|
|
213
|
-
git commit -m "release: prepare ${{ inputs.version }}"
|
|
214
|
-
timeout-minutes: 2
|
|
215
|
-
|
|
216
|
-
- name: Push branch
|
|
217
|
-
id: push
|
|
218
|
-
run: |
|
|
219
|
-
git push origin "${{ steps.branch.outputs.name }}"
|
|
220
|
-
echo "created=true" >> "$GITHUB_OUTPUT"
|
|
221
|
-
timeout-minutes: 5
|
|
222
|
-
|
|
223
|
-
# R-205(f): open the bump-PR via `gh pr create`.
|
|
224
|
-
- name: Open bump-PR
|
|
225
|
-
run: |
|
|
226
|
-
gh pr create \
|
|
227
|
-
--base main \
|
|
228
|
-
--head "${{ steps.branch.outputs.name }}" \
|
|
229
|
-
--title "release: prepare ${{ inputs.version }}" \
|
|
230
|
-
--label "{{PR_LABEL}}" \
|
|
231
|
-
--body-file "${{ steps.plan.outputs.plan_file }}"
|
|
232
|
-
timeout-minutes: 5
|
|
233
|
-
|
|
234
|
-
# R-209(c): emit recovery hint into the job summary artifact.
|
|
235
|
-
- name: Emit recovery hint to job summary
|
|
236
|
-
if: always()
|
|
237
|
-
run: |
|
|
238
|
-
{
|
|
239
|
-
echo "## Release Prepare — ${{ inputs.version }}"
|
|
240
|
-
echo ""
|
|
241
|
-
echo "Branch: \`${{ steps.branch.outputs.name }}\`"
|
|
242
|
-
echo ""
|
|
243
|
-
echo "**Recovery command** (if anything went wrong):"
|
|
244
|
-
echo ""
|
|
245
|
-
echo "\`\`\`"
|
|
246
|
-
echo "cleo release plan ${{ inputs.version }} --epic ${{ inputs.epic }}; cleo release open ${{ inputs.version }}"
|
|
247
|
-
echo "\`\`\`"
|
|
248
|
-
} >> "$GITHUB_STEP_SUMMARY"
|
|
249
|
-
timeout-minutes: 1
|
|
250
|
-
|
|
251
|
-
# R-209(a)(b): on failure, delete the half-cut release branch and print
|
|
252
|
-
# the recovery command. continue-on-error so cleanup itself cannot fail
|
|
253
|
-
# the workflow further.
|
|
254
|
-
cleanup-on-failure:
|
|
255
|
-
name: Cleanup on failure
|
|
256
|
-
runs-on: ubuntu-latest
|
|
257
|
-
needs:
|
|
258
|
-
- preflight
|
|
259
|
-
- prepare
|
|
260
|
-
if: failure()
|
|
261
|
-
timeout-minutes: 10
|
|
262
|
-
env:
|
|
263
|
-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
264
|
-
steps:
|
|
265
|
-
- name: Checkout
|
|
266
|
-
uses: actions/checkout@v4.1
|
|
267
|
-
with:
|
|
268
|
-
fetch-depth: 0
|
|
269
|
-
token: ${{ secrets.GITHUB_TOKEN }}
|
|
270
|
-
timeout-minutes: 5
|
|
271
|
-
|
|
272
|
-
- name: Delete release branch (best-effort)
|
|
273
|
-
continue-on-error: true
|
|
274
|
-
run: |
|
|
275
|
-
BRANCH="{{BRANCH_PREFIX}}/${{ inputs.version }}"
|
|
276
|
-
if git ls-remote --exit-code --heads origin "$BRANCH" >/dev/null 2>&1; then
|
|
277
|
-
echo "Deleting remote branch $BRANCH"
|
|
278
|
-
git push origin --delete "$BRANCH" || true
|
|
279
|
-
else
|
|
280
|
-
echo "No remote branch $BRANCH to delete."
|
|
281
|
-
fi
|
|
282
|
-
timeout-minutes: 5
|
|
283
|
-
|
|
284
|
-
- name: Print recovery command
|
|
285
|
-
run: |
|
|
286
|
-
echo "::notice::Recovery: cleo release plan ${{ inputs.version }} --epic ${{ inputs.epic }}; cleo release open ${{ inputs.version }}"
|
|
287
|
-
{
|
|
288
|
-
echo "## Release Prepare FAILED — ${{ inputs.version }}"
|
|
289
|
-
echo ""
|
|
290
|
-
echo "Run this to recover:"
|
|
291
|
-
echo ""
|
|
292
|
-
echo "\`\`\`"
|
|
293
|
-
echo "cleo release plan ${{ inputs.version }} --epic ${{ inputs.epic }}; cleo release open ${{ inputs.version }}"
|
|
294
|
-
echo "\`\`\`"
|
|
295
|
-
} >> "$GITHUB_STEP_SUMMARY"
|
|
296
|
-
timeout-minutes: 1
|