-
Notifications
You must be signed in to change notification settings - Fork 3
243 lines (209 loc) · 7.79 KB
/
Copy pathpublish.yml
File metadata and controls
243 lines (209 loc) · 7.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
name: Publish
on:
workflow_dispatch:
inputs:
channel:
description: "Release channel, or publish an existing tag/ref"
required: true
type: choice
options:
- next
- finalize
- stable
- existing
default: next
bump:
description: "Version bump (next and stable only)"
required: false
type: choice
options:
- patch
- minor
- major
default: patch
prerelease_tag:
description: "Prerelease tag to finalize (defaults to latest v*-next.* tag)"
required: false
type: string
ref:
description: "Existing release tag/ref to publish (required when channel=existing)"
required: false
type: string
concurrency:
group: ${{ github.workflow }}-${{ inputs.channel }}-${{ inputs.ref || inputs.prerelease_tag || inputs.bump || github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
prepare:
name: Prepare release
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
publish_action: ${{ steps.context.outputs.publish_action }}
release_tag: ${{ steps.context.outputs.release_tag }}
version: ${{ steps.context.outputs.version }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
ref: ${{ inputs.channel == 'existing' && inputs.ref || github.ref }}
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
if [ "${{ inputs.channel }}" != "existing" ]; then
git checkout main
fi
- name: Validate existing ref input
if: inputs.channel == 'existing' && inputs.ref == ''
run: |
echo "::error::ref is required when channel=existing"
exit 1
- name: Resolve finalize ref
if: inputs.channel == 'finalize'
id: finalize_ref
run: |
if [ -n "${{ inputs.prerelease_tag }}" ]; then
TAG="${{ inputs.prerelease_tag }}"
else
TAG=$(git tag --list 'v*-next.*' --sort=-version:refname | head -n 1)
fi
case "$TAG" in
v*) ;;
*) TAG="v$TAG" ;;
esac
if [ -z "$TAG" ]; then
echo "No prerelease tag found to finalize" >&2
exit 1
fi
git rev-parse --verify "$TAG^{commit}" >/dev/null
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Install dependencies
run: bun install --frozen-lockfile
# Finalize/stable releases promote to latest, so block before any version
# bump/tag if the live contract eval fails. The gate uses a Models-capable
# PAT secret and does not override the workflow GITHUB_TOKEN.
- name: Contract eval gate
if: inputs.channel == 'finalize' || inputs.channel == 'stable'
run: bun run contract-eval
env:
GH_MODELS_TOKEN: ${{ secrets.GH_MODELS_TOKEN }}
CONTRACT_EVAL_MODEL: ${{ vars.CONTRACT_EVAL_MODEL || 'openai/gpt-4.1-mini' }}
- name: Bump version, commit, and tag
if: inputs.channel != 'existing'
run: |
if [ "${{ inputs.channel }}" = "next" ]; then
bun run release next ${{ inputs.bump }}
elif [ "${{ inputs.channel }}" = "finalize" ]; then
bun run release finalize "${{ steps.finalize_ref.outputs.tag }}"
else
bun run release ${{ inputs.bump }}
fi
- name: Resolve publish context
id: context
run: |
set -euo pipefail
VERSION=$(node -p "require('./apps/cli/package.json').version")
RELEASE_TAG="v${VERSION}"
CHECKOUT_TAG=$(git tag --points-at HEAD | grep -Fx "$RELEASE_TAG" | head -n 1 || true)
if [ -z "$CHECKOUT_TAG" ]; then
echo "::error::Publish checkout must point at $RELEASE_TAG; tags at HEAD are:"
git tag --points-at HEAD || true
exit 1
fi
if [[ "$VERSION" == *"-next."* ]]; then
PUBLISH_ACTION="publish-next"
else
PUBLISH_ACTION="publish-latest"
fi
echo "Publishing $RELEASE_TAG with $PUBLISH_ACTION"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "release_tag=$RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "publish_action=$PUBLISH_ACTION" >> "$GITHUB_OUTPUT"
- name: Ensure GitHub Release
run: |
VERSION="${{ steps.context.outputs.version }}"
RELEASE_TAG="${{ steps.context.outputs.release_tag }}"
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
echo "GitHub Release $RELEASE_TAG already exists"
exit 0
fi
if [[ "$VERSION" == *"-next."* ]]; then
gh release create "$RELEASE_TAG" --generate-notes --prerelease
else
gh release create "$RELEASE_TAG" --generate-notes
fi
env:
GH_TOKEN: ${{ github.token }}
publish:
name: Publish npm packages
needs: prepare
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
fetch-tags: true
ref: ${{ needs.prepare.outputs.release_tag }}
- uses: actions/setup-node@v6
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Upgrade npm
run: npm install -g npm@latest
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Validate publish checkout
run: |
set -euo pipefail
VERSION=$(node -p "require('./apps/cli/package.json').version")
RELEASE_TAG="v${VERSION}"
CHECKOUT_TAG=$(git tag --points-at HEAD | grep -Fx "$RELEASE_TAG" | head -n 1 || true)
if [ -z "$CHECKOUT_TAG" ]; then
echo "::error::Publish checkout must point at $RELEASE_TAG; tags at HEAD are:"
git tag --points-at HEAD || true
exit 1
fi
if [[ "$VERSION" == *"-next."* ]]; then
if [ "${{ needs.prepare.outputs.publish_action }}" != "publish-next" ]; then
echo "::error::Cannot publish prerelease $VERSION with ${{ needs.prepare.outputs.publish_action }}"
exit 1
fi
else
if [ "${{ needs.prepare.outputs.publish_action }}" != "publish-latest" ]; then
echo "::error::Cannot publish stable version $VERSION with ${{ needs.prepare.outputs.publish_action }}"
exit 1
fi
fi
echo "Publishing $RELEASE_TAG with ${{ needs.prepare.outputs.publish_action }}"
# Existing latest publishes still run the live contract gate because they
# promote an already-created tag to the default npm install path.
- name: Run contract eval gate
if: needs.prepare.outputs.publish_action == 'publish-latest' && inputs.channel == 'existing'
run: bun run contract-eval
env:
GH_MODELS_TOKEN: ${{ secrets.GH_MODELS_TOKEN }}
CONTRACT_EVAL_MODEL: ${{ vars.CONTRACT_EVAL_MODEL || 'openai/gpt-4.1-mini' }}
- name: Publish to npm
run: |
if [ "${{ needs.prepare.outputs.publish_action }}" = "publish-next" ]; then
bun run publish:next
else
bun run publish
fi
env:
NPM_CONFIG_PROVENANCE: true
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}