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+
134name : publish-js
235
336concurrency :
4- group : publish-js-${{ inputs.release_type }}-${{ inputs.branch }}
37+ group : publish-js-${{ inputs.release_type }}-${{ inputs.sha }}
538 cancel-in-progress : false
639
740on :
@@ -15,192 +48,178 @@ 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
2966jobs :
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+ node_version : .tool-versions
128+ tag_format : ' js-{version}'
129+ package_name : autoevals # enables the npm-availability fail-fast (covers prereleases)
130+ channel : ${{ needs.compute-metadata.outputs.channel }}
131+ allowed_channels : ' latest,rc'
132+ dry_run : ${{ inputs.dry_run }}
133+
134+ prepare :
135+ needs : validate
136+ runs-on : ubuntu-latest
137+ timeout-minutes : 5
138+ permissions :
139+ contents : write # required for the releases/generate-notes API
140+ outputs :
141+ pr_list : ${{ steps.prepare.outputs.pr_list }}
142+ notes : ${{ steps.prepare.outputs.notes }}
143+ steps :
144+ - name : Prepare release
145+ id : prepare
146+ uses : braintrustdata/sdk-actions/actions/release/prepare@dc38f223e415652eb4a4f17567a360662ba729b1
147+ with :
148+ release_tag : ${{ needs.validate.outputs.release_tag }}
149+ sha : ${{ inputs.sha }}
150+ prev_release : ${{ needs.validate.outputs.prev_release }}
151+
152+ notify-pending :
153+ needs : [validate, prepare]
154+ runs-on : ubuntu-latest
155+ timeout-minutes : 5
156+ permissions : {}
157+ steps :
158+ - name : Notify release pending
159+ uses : braintrustdata/sdk-actions/actions/release/notify-pending@dc38f223e415652eb4a4f17567a360662ba729b1
160+ with :
161+ sha : ${{ inputs.sha }}
162+ release_tag : ${{ needs.validate.outputs.release_tag }}
163+ prev_release : ${{ needs.validate.outputs.prev_release }}
164+ branch : ${{ needs.validate.outputs.branch }}
165+ on_release_branch : ${{ needs.validate.outputs.on_release_branch }}
166+ commit_message : ${{ needs.validate.outputs.commit_message }}
167+ pr_list : ${{ needs.prepare.outputs.pr_list }}
168+ notes : ${{ needs.prepare.outputs.notes }}
169+ dry_run : ${{ inputs.dry_run }}
170+ slack_token : ${{ secrets.SLACK_BOT_TOKEN }}
171+ slack_channel : ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}
172+ label : ' autoevals (js)'
173+ emoji : ' :javascript:'
174+
87175 publish :
88- needs : prepare-release
176+ needs : [compute-metadata, validate, prepare, notify-pending]
89177 runs-on : ubuntu-latest
90178 timeout-minutes : 20
179+ environment : ${{ inputs.dry_run && 'npm-publish-dry-run' || 'npm-publish' }}
91180 permissions :
92181 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 }}
182+ id-token : write # OIDC trusted publishing + provenance
101183 steps :
102184 - uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
103185 with :
186+ ref : ${{ inputs.sha }}
104187 fetch-depth : 0
105- ref : ${{ needs.prepare-release.outputs.branch }}
106188
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' }}
189+ # Write the computed version into package.json before publish. For a
190+ # prerelease this applies the -rc.N suffix (and deliberately diverges from
191+ # py/version.py); for stable it confirms the already-bumped version.
192+ # publish runs with checkout:false so this edit survives into the build.
193+ - name : Patch package.json version
194+ env :
195+ VERSION : ${{ needs.compute-metadata.outputs.version }}
134196 run : |
135- set -euo pipefail
136-
137197 node -e '
138198 const fs = require("fs");
139199 const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
140200 pkg.version = process.env.VERSION;
141201 fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
142202 '
143203
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 }}
204+ - name : Publish
205+ uses : braintrustdata/sdk-actions/actions/release/lang/js/publish@dc38f223e415652eb4a4f17567a360662ba729b1
177206 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"
207+ sha : ${{ inputs.sha }}
208+ checkout : ' false' # keep the patched package.json (checked out + patched above)
209+ node_version : .tool-versions
210+ version : ${{ needs.compute-metadata.outputs.version }}
211+ release_tag : ${{ needs.validate.outputs.release_tag }}
212+ channel : ${{ needs.compute-metadata.outputs.channel }}
213+ github_release : ${{ needs.compute-metadata.outputs.github_release }} # stable only
214+ release_title : autoevals JavaScript v${{ needs.compute-metadata.outputs.version }}
215+ package_name : autoevals
216+ label : ' autoevals (js)'
217+ dry_run : ${{ inputs.dry_run }}
218+ notes : ${{ needs.prepare.outputs.notes }}
219+ prev_release : ${{ needs.validate.outputs.prev_release }}
220+ branch : ${{ needs.validate.outputs.branch }}
221+ on_release_branch : ${{ needs.validate.outputs.on_release_branch }}
222+ pr_list : ${{ needs.prepare.outputs.pr_list }}
223+ slack_token : ${{ secrets.SLACK_BOT_TOKEN }}
224+ slack_channel : ${{ vars.SLACK_SDK_RELEASE_CHANNEL }}
225+ emoji : ' :javascript:'
0 commit comments