Skip to content

Commit a8cf9bc

Browse files
author
David Elner
committed
Changed: Use shared release actions for JS package release
1 parent edfa259 commit a8cf9bc

4 files changed

Lines changed: 231 additions & 162 deletions

File tree

.github/workflows/publish-js.yaml

Lines changed: 165 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,40 @@
1+
#
2+
# Publishes the `autoevals` npm package via the shared, centrally-maintained
3+
# release actions in braintrustdata/sdk-actions (pinned by SHA below). Bumping
4+
# that SHA pulls in upstream release-tooling improvements.
5+
#
6+
# Adapted from braintrustdata/sdk-actions .github/workflows/release-js.yml
7+
# @ dc38f223e415652eb4a4f17567a360662ba729b1
8+
#
9+
# ─────────────────────────────────────────────────────────────────────────────
10+
# SETUP REQUIREMENTS
11+
# ─────────────────────────────────────────────────────────────────────────────
12+
#
13+
# GitHub Environments (repo Settings → Environments):
14+
# npm-publish
15+
# - Required reviewers: SDK maintainers
16+
# - Deployment branches: main only
17+
# npm-publish-dry-run
18+
# - Required reviewers: at least yourself (to exercise the gate)
19+
# - Allow administrators to bypass: enabled
20+
#
21+
# npm Trusted Publishing (OIDC — no token):
22+
# npmjs.com → autoevals → Settings → Trusted Publisher
23+
# Repository: braintrustdata/autoevals
24+
# Workflow: publish-js.yaml
25+
# Environment: npm-publish ← MUST match, or the gated publish is rejected
26+
# Provenance requires a PUBLIC package; the publish job needs id-token: write.
27+
#
28+
# Slack (optional — steps self-guard and no-op if unset):
29+
# Repository variable SLACK_SDK_RELEASE_CHANNEL (channel ID)
30+
# Repository/org secret SLACK_BOT_TOKEN (Brainbot token; invite it to the channel)
31+
#
32+
# ─────────────────────────────────────────────────────────────────────────────
33+
134
name: publish-js
235

336
concurrency:
4-
group: publish-js-${{ inputs.release_type }}-${{ inputs.branch }}
37+
group: publish-js-${{ inputs.release_type }}-${{ inputs.sha }}
538
cancel-in-progress: false
639

740
on:
@@ -15,192 +48,183 @@ on:
1548
options:
1649
- stable
1750
- prerelease
18-
branch:
19-
description: Branch to release from
51+
sha:
52+
description: Commit SHA to release (full 40-char SHA)
2053
required: true
21-
default: main
2254
type: string
2355
prerelease_suffix:
2456
description: Optional shared prerelease suffix
2557
required: false
2658
default: ""
2759
type: string
60+
dry_run:
61+
description: "Dry run: build and pack without publishing"
62+
required: false
63+
default: false
64+
type: boolean
2865

2966
jobs:
30-
prepare-release:
67+
# autoevals glue: fail-fast version sync + version/channel computation (all pre-gate).
68+
compute-metadata:
3169
runs-on: ubuntu-latest
3270
timeout-minutes: 10
3371
outputs:
34-
version: ${{ steps.release_metadata.outputs.version }}
35-
release_tag: ${{ steps.release_metadata.outputs.release_tag }}
36-
branch: ${{ steps.release_metadata.outputs.branch }}
37-
commit: ${{ steps.release_metadata.outputs.commit }}
38-
release_type: ${{ steps.release_metadata.outputs.release_type }}
72+
version: ${{ steps.meta.outputs.version }}
73+
channel: ${{ steps.meta.outputs.channel }}
74+
github_release: ${{ steps.meta.outputs.github_release }}
75+
release_type: ${{ steps.meta.outputs.release_type }}
3976
steps:
4077
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4178
with:
79+
ref: ${{ inputs.sha }}
4280
fetch-depth: 1
43-
ref: ${{ inputs.branch }}
81+
82+
# Only place version-sync runs — pre-gate, fail-fast.
4483
- name: Check version sync
4584
run: python3 .github/scripts/check_version_sync.py
46-
- name: Set up Node.js
47-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
48-
with:
49-
node-version-file: .tool-versions
50-
- name: Determine release metadata
51-
id: release_metadata
85+
86+
- name: Compute release metadata
87+
id: meta
5288
env:
5389
RELEASE_TYPE: ${{ inputs.release_type }}
54-
TARGET_BRANCH: ${{ inputs.branch }}
5590
PRERELEASE_SUFFIX: ${{ inputs.prerelease_suffix }}
5691
run: |
5792
set -euo pipefail
5893
59-
CURRENT_VERSION=$(node -p "require('./package.json').version")
60-
RELEASE_COMMIT=$(git rev-parse HEAD)
61-
62-
if [[ -z "${PRERELEASE_SUFFIX}" ]]; then
63-
PRERELEASE_SUFFIX="${GITHUB_RUN_NUMBER}"
64-
fi
65-
94+
CURRENT=$(node -p "require('./package.json').version")
6695
echo "release_type=${RELEASE_TYPE}" >> "$GITHUB_OUTPUT"
67-
echo "branch=${TARGET_BRANCH}" >> "$GITHUB_OUTPUT"
68-
echo "commit=${RELEASE_COMMIT}" >> "$GITHUB_OUTPUT"
6996
7097
if [[ "$RELEASE_TYPE" == "stable" ]]; then
71-
RELEASE_TAG="js-${CURRENT_VERSION}"
72-
73-
if git ls-remote --exit-code --tags origin "refs/tags/${RELEASE_TAG}" >/dev/null 2>&1; then
74-
echo "Tag ${RELEASE_TAG} already exists on origin" >&2
75-
exit 1
76-
fi
77-
78-
echo "version=${CURRENT_VERSION}" >> "$GITHUB_OUTPUT"
79-
echo "release_tag=${RELEASE_TAG}" >> "$GITHUB_OUTPUT"
98+
echo "version=${CURRENT}" >> "$GITHUB_OUTPUT"
99+
echo "channel=latest" >> "$GITHUB_OUTPUT"
100+
echo "github_release=true" >> "$GITHUB_OUTPUT"
80101
else
81-
VERSION="${CURRENT_VERSION}-rc.${PRERELEASE_SUFFIX}"
82-
83-
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
84-
echo "release_tag=" >> "$GITHUB_OUTPUT"
102+
SUFFIX="${PRERELEASE_SUFFIX:-$GITHUB_RUN_NUMBER}"
103+
echo "version=${CURRENT}-rc.${SUFFIX}" >> "$GITHUB_OUTPUT"
104+
echo "channel=rc" >> "$GITHUB_OUTPUT"
105+
echo "github_release=false" >> "$GITHUB_OUTPUT"
85106
fi
86107
108+
validate:
109+
needs: compute-metadata
110+
runs-on: ubuntu-latest
111+
timeout-minutes: 10
112+
permissions:
113+
contents: read
114+
outputs:
115+
release_tag: ${{ steps.validate.outputs.release_tag }}
116+
prev_release: ${{ steps.validate.outputs.prev_release }}
117+
branch: ${{ steps.validate.outputs.branch }}
118+
on_release_branch: ${{ steps.validate.outputs.on_release_branch }}
119+
commit_message: ${{ steps.validate.outputs.commit_message }}
120+
steps:
121+
- name: Validate release
122+
id: validate
123+
uses: braintrustdata/sdk-actions/actions/release/lang/js/validate@dc38f223e415652eb4a4f17567a360662ba729b1
124+
with:
125+
sha: ${{ inputs.sha }}
126+
version: ${{ needs.compute-metadata.outputs.version }} # computed override (esp. prerelease)
127+
working_directory: .
128+
node_version: .tool-versions
129+
build_command: build
130+
tag_format: 'js-{version}'
131+
package_name: autoevals # enables the npm-availability fail-fast (covers prereleases)
132+
channel: ${{ needs.compute-metadata.outputs.channel }}
133+
allowed_channels: 'latest,rc'
134+
dry_run: ${{ inputs.dry_run }}
135+
136+
prepare:
137+
needs: validate
138+
runs-on: ubuntu-latest
139+
timeout-minutes: 5
140+
permissions:
141+
contents: write # required for the releases/generate-notes API
142+
outputs:
143+
pr_list: ${{ steps.prepare.outputs.pr_list }}
144+
notes: ${{ steps.prepare.outputs.notes }}
145+
steps:
146+
- name: Prepare release
147+
id: prepare
148+
uses: braintrustdata/sdk-actions/actions/release/prepare@dc38f223e415652eb4a4f17567a360662ba729b1
149+
with:
150+
release_tag: ${{ needs.validate.outputs.release_tag }}
151+
sha: ${{ inputs.sha }}
152+
prev_release: ${{ needs.validate.outputs.prev_release }}
153+
154+
notify-pending:
155+
needs: [validate, prepare]
156+
runs-on: ubuntu-latest
157+
timeout-minutes: 5
158+
permissions: {}
159+
steps:
160+
- name: Notify release pending
161+
uses: braintrustdata/sdk-actions/actions/release/notify-pending@dc38f223e415652eb4a4f17567a360662ba729b1
162+
with:
163+
sha: ${{ inputs.sha }}
164+
release_tag: ${{ needs.validate.outputs.release_tag }}
165+
prev_release: ${{ needs.validate.outputs.prev_release }}
166+
branch: ${{ needs.validate.outputs.branch }}
167+
on_release_branch: ${{ needs.validate.outputs.on_release_branch }}
168+
commit_message: ${{ needs.validate.outputs.commit_message }}
169+
pr_list: ${{ needs.prepare.outputs.pr_list }}
170+
notes: ${{ needs.prepare.outputs.notes }}
171+
dry_run: ${{ inputs.dry_run }}
172+
slack_token: ${{ secrets.SLACK_BOT_TOKEN }}
173+
slack_channel: ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}
174+
label: 'autoevals (js)'
175+
emoji: ':javascript:'
176+
87177
publish:
88-
needs: prepare-release
178+
needs: [compute-metadata, validate, prepare, notify-pending]
89179
runs-on: ubuntu-latest
90180
timeout-minutes: 20
181+
environment: ${{ inputs.dry_run && 'npm-publish-dry-run' || 'npm-publish' }}
91182
permissions:
92183
contents: write
93-
id-token: write
94-
env:
95-
PACKAGE_NAME: autoevals
96-
VERSION: ${{ needs.prepare-release.outputs.version }}
97-
RELEASE_TAG: ${{ needs.prepare-release.outputs.release_tag }}
98-
RELEASE_TYPE: ${{ needs.prepare-release.outputs.release_type }}
99-
TARGET_BRANCH: ${{ needs.prepare-release.outputs.branch }}
100-
RELEASE_COMMIT: ${{ needs.prepare-release.outputs.commit }}
184+
id-token: write # OIDC trusted publishing + provenance
101185
steps:
102186
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
103187
with:
188+
ref: ${{ inputs.sha }}
104189
fetch-depth: 0
105-
ref: ${{ needs.prepare-release.outputs.branch }}
106190

107-
- name: Check version sync
108-
run: python3 .github/scripts/check_version_sync.py
109-
110-
- name: Set up Node.js
111-
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
112-
with:
113-
node-version-file: .tool-versions
114-
registry-url: https://registry.npmjs.org
115-
116-
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0
117-
with:
118-
version: 10.33.0
119-
120-
- name: Check npm version availability
121-
run: |
122-
set -euo pipefail
123-
124-
if npm view "${PACKAGE_NAME}@${VERSION}" version --registry=https://registry.npmjs.org >/dev/null 2>&1; then
125-
echo "${PACKAGE_NAME}@${VERSION} already exists on npm" >&2
126-
exit 1
127-
fi
128-
129-
- name: Install dependencies
130-
run: pnpm install --frozen-lockfile
131-
132-
- name: Prepare prerelease package metadata
133-
if: ${{ env.RELEASE_TYPE == 'prerelease' }}
191+
# Write the computed version into package.json before publish. For a
192+
# prerelease this applies the -rc.N suffix (and deliberately diverges from
193+
# py/version.py); for stable it confirms the already-bumped version.
194+
# publish runs with checkout:false so this edit survives into the build.
195+
- name: Patch package.json version
196+
env:
197+
VERSION: ${{ needs.compute-metadata.outputs.version }}
134198
run: |
135-
set -euo pipefail
136-
137199
node -e '
138200
const fs = require("fs");
139201
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
140202
pkg.version = process.env.VERSION;
141203
fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
142204
'
143205
144-
- name: Build package
145-
run: pnpm run build
146-
147-
- name: Publish stable release to npm
148-
if: ${{ env.RELEASE_TYPE == 'stable' }}
149-
env:
150-
NODE_AUTH_TOKEN: ""
151-
NPM_TOKEN: ""
152-
run: npm publish --provenance --access public
153-
154-
- name: Publish prerelease to npm
155-
if: ${{ env.RELEASE_TYPE == 'prerelease' }}
156-
env:
157-
NODE_AUTH_TOKEN: ""
158-
NPM_TOKEN: ""
159-
run: npm publish --tag rc --provenance --access public
160-
161-
- name: Create and push stable release tag
162-
if: ${{ env.RELEASE_TYPE == 'stable' }}
163-
run: |
164-
set -euo pipefail
165-
166-
git config user.name "github-actions[bot]"
167-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
168-
git tag "${RELEASE_TAG}" "${RELEASE_COMMIT}"
169-
git push origin "${RELEASE_TAG}"
170-
171-
- name: Create GitHub release
172-
if: ${{ env.RELEASE_TYPE == 'stable' }}
173-
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
174-
env:
175-
RELEASE_TAG: ${{ env.RELEASE_TAG }}
176-
VERSION: ${{ env.VERSION }}
206+
- name: Publish
207+
uses: braintrustdata/sdk-actions/actions/release/lang/js/publish@dc38f223e415652eb4a4f17567a360662ba729b1
177208
with:
178-
script: |
179-
await github.rest.repos.createRelease({
180-
owner: context.repo.owner,
181-
repo: context.repo.repo,
182-
tag_name: process.env.RELEASE_TAG,
183-
name: `autoevals JavaScript v${process.env.VERSION}`,
184-
draft: false,
185-
prerelease: false,
186-
generate_release_notes: true,
187-
});
188-
189-
- name: Summarize release
190-
run: |
191-
set -euo pipefail
192-
193-
{
194-
echo "## npm publish complete"
195-
echo
196-
echo "- Package: \`${PACKAGE_NAME}\`"
197-
echo "- Version: \`${VERSION}\`"
198-
echo "- Release type: \`${RELEASE_TYPE}\`"
199-
if [ "${RELEASE_TYPE}" = "prerelease" ]; then
200-
echo "- npm tag: \`rc\`"
201-
echo "- Install: \`npm install ${PACKAGE_NAME}@rc\`"
202-
else
203-
echo "- Git tag: \`${RELEASE_TAG}\`"
204-
echo "- Install: \`npm install ${PACKAGE_NAME}\`"
205-
fi
206-
} >> "$GITHUB_STEP_SUMMARY"
209+
sha: ${{ inputs.sha }}
210+
checkout: 'false' # keep the patched package.json (checked out + patched above)
211+
working_directory: .
212+
node_version: .tool-versions
213+
version: ${{ needs.compute-metadata.outputs.version }}
214+
release_tag: ${{ needs.validate.outputs.release_tag }}
215+
channel: ${{ needs.compute-metadata.outputs.channel }}
216+
access: 'public'
217+
provenance: 'true'
218+
github_release: ${{ needs.compute-metadata.outputs.github_release }} # stable only
219+
release_title: autoevals JavaScript v${{ needs.compute-metadata.outputs.version }}
220+
package_name: autoevals
221+
label: 'autoevals (js)'
222+
dry_run: ${{ inputs.dry_run }}
223+
notes: ${{ needs.prepare.outputs.notes }}
224+
prev_release: ${{ needs.validate.outputs.prev_release }}
225+
branch: ${{ needs.validate.outputs.branch }}
226+
on_release_branch: ${{ needs.validate.outputs.on_release_branch }}
227+
pr_list: ${{ needs.prepare.outputs.pr_list }}
228+
slack_token: ${{ secrets.SLACK_BOT_TOKEN }}
229+
slack_channel: ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}
230+
emoji: ':javascript:'

0 commit comments

Comments
 (0)