Skip to content

Commit ede4cd1

Browse files
authored
Merge branch 'main' into docs/aws-strands-typescript-tabs
2 parents 24b981d + ce87d91 commit ede4cd1

2 files changed

Lines changed: 83 additions & 204 deletions

File tree

.github/workflows/prerelease.yml

Lines changed: 0 additions & 136 deletions
This file was deleted.

.github/workflows/publish-release.yml

Lines changed: 83 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,53 @@
1+
# release / publish
2+
#
3+
# Single npm OIDC entry point for both stable releases and prerelease canaries.
4+
# npm trusted publisher records for the 15 @copilotkit/* monorepo packages plus
5+
# @copilotkitnext/angular are registered against THIS workflow file. Matching
6+
# happens on the OIDC token's `workflow_ref` claim, which is always
7+
# publish-release.yml when this workflow is the entry point.
8+
#
9+
# Triggers:
10+
# - pull_request: closed on a release/publish/<scope>/v<X.Y.Z> branch → stable
11+
# release of <scope> at version <X.Y.Z> (the normal flow).
12+
# - workflow_dispatch with mode=stable → manual retrigger of a failed stable
13+
# release. Republishes from the latest commit on main. Only use this when
14+
# the normal flow failed BEFORE npm publish succeeded.
15+
# - workflow_dispatch with mode=prerelease → canary publish. Bumps versions
16+
# in the build job to <X.Y.Z>-canary.<suffix>, publishes with --tag canary,
17+
# skips tag push + GH Release + Notion notification.
118
name: release / publish
219

320
on:
421
pull_request:
522
types: [closed]
623
branches: [main]
7-
# Escape hatch for failed releases. Covers failures BEFORE the "Publish to
8-
# npm" step succeeds (build errors, infra blips, git config bugs). Does
9-
# NOT cover post-npm-publish failures: if npm publish already succeeded
10-
# but a later step failed, the retry will fail at "Publish to npm" with
11-
# an "already published" error, and the "Check for pre-existing tags"
12-
# step will also block retries where the tag was pushed. For those cases,
13-
# remediate by hand using release-notes.md from the merged release PR.
1424
workflow_dispatch:
1525
inputs:
1626
scope:
17-
description: |
18-
⚠️ MANUAL RETRIGGER — republishes from the latest commit on main,
19-
bypassing the normal release-PR flow. Only use this when the
20-
normal flow failed BEFORE npm publish succeeded. If npm publish
21-
already happened, finish the release by hand instead — dispatching
22-
here will fail at "Publish to npm" with "already published".
27+
description: "What to release"
2328
required: true
2429
type: choice
2530
options:
2631
- monorepo
2732
- angular
28-
dry-run:
29-
description: "Dry run (skip publish step)"
30-
required: false
31-
default: false
32-
type: boolean
33-
workflow_call:
34-
inputs:
35-
scope:
36-
required: true
37-
type: string
3833
mode:
34+
description: "Release mode: stable (full release with tag + GH Release) or prerelease (canary, no tag/release)"
3935
required: false
40-
type: string
4136
default: stable
42-
dry-run:
43-
required: false
44-
type: boolean
45-
default: false
46-
publish-script:
37+
type: choice
38+
options:
39+
- stable
40+
- prerelease
41+
suffix:
42+
description: "Canary suffix (only used when mode=prerelease). Falls back to timestamp if empty. Allowed: [a-zA-Z0-9._-]+"
4743
required: false
4844
type: string
49-
default: publish-release.ts
50-
secrets:
51-
NOTION_API_KEY:
45+
default: ""
46+
dry-run:
47+
description: "Dry run (skip publish step)"
5248
required: false
49+
default: false
50+
type: boolean
5351

5452
permissions:
5553
contents: read
@@ -63,34 +61,36 @@ env:
6361

6462
jobs:
6563
build:
66-
# Run on a merged release PR (normal flow) or a manual workflow_dispatch (escape hatch).
67-
# Skipped on workflow_call: the caller's build job uploads the "workspace" artifact, so
68-
# the callee's publish job downloads it directly without re-building.
64+
# Run on a merged release PR (normal flow) or a manual workflow_dispatch
65+
# from main (escape hatch). The main-branch guard on workflow_dispatch
66+
# prevents republishing from arbitrary branches.
6967
if: >
70-
github.event_name != 'workflow_call' && (
71-
github.event_name == 'workflow_dispatch' ||
72-
(github.event.pull_request.merged == true &&
73-
startsWith(github.event.pull_request.head.ref, 'release/publish/'))
74-
)
68+
(github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') ||
69+
(github.event.pull_request.merged == true &&
70+
startsWith(github.event.pull_request.head.ref, 'release/publish/'))
7571
runs-on: ubuntu-latest
7672
timeout-minutes: 20
7773
permissions:
7874
contents: read
7975
steps:
80-
- name: Determine scope
76+
- name: Determine scope and mode
8177
id: meta
8278
env:
8379
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
8480
INPUT_SCOPE: ${{ inputs.scope }}
81+
INPUT_MODE: ${{ inputs.mode }}
8582
run: |
83+
set -euo pipefail
8684
if [ -n "$INPUT_SCOPE" ]; then
8785
SCOPE="$INPUT_SCOPE"
8886
else
8987
# Branch format: release/publish/<scope>/v<version>
9088
SCOPE=$(echo "$PR_HEAD_REF" | sed 's|release/publish/\([^/]*\)/v.*|\1|')
9189
fi
92-
echo "scope=$SCOPE" >> $GITHUB_OUTPUT
93-
echo "Detected scope: $SCOPE"
90+
MODE="${INPUT_MODE:-stable}"
91+
echo "scope=$SCOPE" >> "$GITHUB_OUTPUT"
92+
echo "mode=$MODE" >> "$GITHUB_OUTPUT"
93+
echo "Detected scope: $SCOPE, mode: $MODE"
9494
9595
# No token/credential persistence: the publish job sets up its own
9696
# `git config insteadOf` with secrets.GITHUB_TOKEN before pushing tags,
@@ -117,6 +117,27 @@ jobs:
117117
- name: Install Dependencies
118118
run: pnpm install --frozen-lockfile
119119

120+
# Validate user-supplied suffix against npm-safe charset before passing
121+
# to bump-prerelease.ts. Empty suffix → omit the --suffix flag entirely
122+
# so the script applies its timestamp fallback (passing an empty string
123+
# would produce a version like "X.Y.Z-canary." with a trailing dot).
124+
- name: Bump prerelease versions
125+
if: ${{ steps.meta.outputs.mode == 'prerelease' }}
126+
env:
127+
INPUT_SCOPE: ${{ inputs.scope }}
128+
INPUT_SUFFIX: ${{ inputs.suffix }}
129+
run: |
130+
set -euo pipefail
131+
if [ -n "$INPUT_SUFFIX" ]; then
132+
if ! [[ "$INPUT_SUFFIX" =~ ^[a-zA-Z0-9._-]+$ ]]; then
133+
echo "::error::Invalid suffix '$INPUT_SUFFIX'. Allowed: [a-zA-Z0-9._-]+"
134+
exit 1
135+
fi
136+
pnpm tsx scripts/release/bump-prerelease.ts --scope "$INPUT_SCOPE" --suffix "$INPUT_SUFFIX"
137+
else
138+
pnpm tsx scripts/release/bump-prerelease.ts --scope "$INPUT_SCOPE"
139+
fi
140+
120141
- name: Build packages
121142
run: pnpm run build
122143

@@ -140,17 +161,11 @@ jobs:
140161

141162
outputs:
142163
scope: ${{ steps.meta.outputs.scope }}
164+
mode: ${{ steps.meta.outputs.mode }}
143165

144166
publish:
145167
needs: build
146-
# Run when build succeeded (normal flow) OR was skipped specifically because this is a
147-
# workflow_call invocation (the caller already built and uploaded the artifact). The
148-
# workflow_call branch is the ONE intentional skip — other skipped paths (notably a
149-
# `pull_request: closed` event where the PR was closed WITHOUT merging, which makes the
150-
# build job's own merged-true guard fail and skip) must NOT trigger publish. The default
151-
# `if: success()` would also skip publish on legitimate workflow_call invocations, hence
152-
# the explicit allowlist below.
153-
if: ${{ !cancelled() && (needs.build.result == 'success' || (needs.build.result == 'skipped' && github.event_name == 'workflow_call')) }}
168+
if: ${{ !cancelled() && needs.build.result == 'success' }}
154169
runs-on: ubuntu-latest
155170
timeout-minutes: 20
156171
environment: npm
@@ -164,39 +179,33 @@ jobs:
164179
PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
165180
INPUT_SCOPE: ${{ inputs.scope }}
166181
INPUT_MODE: ${{ inputs.mode }}
167-
EVENT_NAME: ${{ github.event_name }}
168182
run: |
169183
set -euo pipefail
170-
# Scope: workflow_call and workflow_dispatch both populate inputs.scope;
171-
# pull_request parses scope from the release branch name.
172184
if [ -n "$INPUT_SCOPE" ]; then
173185
SCOPE="$INPUT_SCOPE"
174186
else
175187
# Branch format: release/publish/<scope>/v<version>
176188
SCOPE=$(echo "$PR_HEAD_REF" | sed 's|release/publish/\([^/]*\)/v.*|\1|')
177189
fi
178190
if [ -z "$SCOPE" ]; then
179-
echo "::error::Failed to resolve scope (event=$EVENT_NAME, input=$INPUT_SCOPE, ref=$PR_HEAD_REF)"
191+
echo "::error::Failed to resolve scope (input=$INPUT_SCOPE, ref=$PR_HEAD_REF)"
180192
exit 1
181193
fi
182-
# Mode: only workflow_call carries an explicit mode; everything else is stable.
183-
if [ "$EVENT_NAME" = "workflow_call" ]; then
184-
MODE="${INPUT_MODE:-stable}"
185-
else
186-
MODE="stable"
187-
fi
194+
MODE="${INPUT_MODE:-stable}"
188195
echo "scope=$SCOPE" >> "$GITHUB_OUTPUT"
189-
echo "mode=$MODE" >> "$GITHUB_OUTPUT"
190-
echo "Resolved scope=$SCOPE mode=$MODE (event=$EVENT_NAME)"
196+
echo "mode=$MODE" >> "$GITHUB_OUTPUT"
191197
192198
- name: Download workspace
193199
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
194200
with:
195201
name: workspace
196202

197203
- name: Configure git credentials
204+
env:
205+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
198206
run: |
199-
git config --local url."https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/".insteadOf "https://github.com/"
207+
set -euo pipefail
208+
git config --local url."https://x-access-token:${GH_TOKEN}@github.com/".insteadOf "https://github.com/"
200209
201210
- name: Setup pnpm
202211
# Omit `version:` so pnpm/action-setup inherits from the repo's
@@ -229,20 +238,26 @@ jobs:
229238
env:
230239
NODE_AUTH_TOKEN: ""
231240
NOTION_API_KEY: ${{ steps.meta.outputs.mode == 'stable' && secrets.NOTION_API_KEY || '' }}
232-
PUBLISH_SCRIPT: ${{ inputs.publish-script || 'publish-release.ts' }}
241+
PUBLISH_SCRIPT: ${{ steps.meta.outputs.mode == 'prerelease' && 'prerelease.ts' || 'publish-release.ts' }}
233242
SCOPE: ${{ steps.meta.outputs.scope }}
234243
run: pnpm tsx "scripts/release/$PUBLISH_SCRIPT" --scope "$SCOPE"
235244

236245
- name: Verify publish step emitted version
237-
if: ${{ success() && inputs.dry-run != true && steps.meta.outputs.mode != 'prerelease' }}
246+
if: ${{ success() && inputs.dry-run != true }}
238247
env:
248+
MODE: ${{ steps.meta.outputs.mode }}
239249
VERSION: ${{ steps.publish.outputs.version }}
240250
run: |
251+
set -euo pipefail
241252
if [ -z "$VERSION" ]; then
242-
echo "::error::publish step did not emit 'version' output. This indicates the publish-release.ts script (or the configured publish-script input) did not write to GITHUB_OUTPUT. Tag/release creation would produce malformed artifacts; aborting."
253+
if [ "$MODE" = "prerelease" ]; then
254+
echo "::error::prerelease.ts did not emit 'version' output to GITHUB_OUTPUT. The Prerelease summary would render a blank Version field; aborting."
255+
else
256+
echo "::error::publish-release.ts did not emit 'version' output to GITHUB_OUTPUT. Tag/release creation would produce malformed artifacts; aborting."
257+
fi
243258
exit 1
244259
fi
245-
echo "VERSION=$VERSION confirmed"
260+
echo "VERSION=$VERSION confirmed (mode=$MODE)"
246261
247262
- name: Configure git user
248263
if: ${{ success() && inputs.dry-run != true && steps.meta.outputs.mode != 'prerelease' }}

0 commit comments

Comments
 (0)