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,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
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+ 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