Release JavaScript custom-build (@braintrust/bt-publishing-test) #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # | |
| # Reference implementation of the v2 JavaScript CUSTOM-BUILD ("build-ownership") shape, | |
| # using @braintrust/bt-publishing-test (pnpm). This does NOT release a real SDK. | |
| # | |
| # The custom shape is the same pipeline as turnkey, cut at the build⟷publish seam: | |
| # configure (facts + manifest) → validate (gate) → pack (unprivileged build) → | |
| # request-approval → [gate] → ship-package | |
| # | |
| # • configure fact-finds (version/tag/notes/registry) and emits the package manifest. | |
| # • pack builds + packs + attests in a job with NO publish credentials, uploads it. | |
| # • ship-package (gated) downloads it, verifies attestations, publishes THAT exact artifact. | |
| # Use this shape when the consumer owns a non-standard build; turnkey (release-js.yml) otherwise. | |
| # | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # SETUP: npm Trusted Publishing must list THIS workflow filename (release-js-custom.yml) | |
| # + the `publish` environment. Only the ship-package job publishes. | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| name: Release JavaScript custom-build (@braintrust/bt-publishing-test) | |
| on: | |
| pull_request: | |
| paths: | |
| - 'actions/**' | |
| - '.github/workflows/**' | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: "Release type — maps to channel + github_release defaults" | |
| type: choice | |
| default: stable | |
| options: [stable, prerelease] | |
| channel: | |
| description: "Explicit npm channel / dist-tag override (empty → derived from release_type)" | |
| type: choice | |
| default: latest | |
| options: [latest, rc, next, beta] | |
| sha: | |
| description: "Commit SHA (of the version bump) to release" | |
| required: true | |
| type: string | |
| prev_release: | |
| description: "Anchor for release notes: a tag name or commit SHA." | |
| type: string | |
| required: false | |
| default: '58a397193105516446c3042361913655bcece509' | |
| dry_run: | |
| description: "Dry run: build + pack (no attest, no publish)" | |
| type: boolean | |
| default: false | |
| jobs: | |
| bump: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: {} | |
| outputs: | |
| version: ${{ steps.bump.outputs.version }} | |
| steps: | |
| # bt-publishing-test: derive next version from npm. Real SDKs drop this job. | |
| - name: Bump version | |
| id: bump | |
| run: | | |
| CURRENT=$(npm view @braintrust/bt-publishing-test version 2>/dev/null || echo "0.0.0") | |
| CURRENT=${CURRENT:-0.0.0} | |
| # TEMP (idempotency test): reuse the already-published version to exercise present→skip. | |
| # Revert to the increment below after confirming. | |
| echo "version=$CURRENT" >> $GITHUB_OUTPUT | |
| # IFS='.' read -r major minor patch <<< "$CURRENT" | |
| # echo "version=$major.$minor.$((patch + 1))" >> $GITHUB_OUTPUT | |
| # FACT-FIND (version/tag/notes/registry) + emit the package manifest. contents: write for notes. | |
| configure: | |
| needs: bump | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: write | |
| outputs: | |
| version: ${{ steps.configure.outputs.version }} | |
| release_tag: ${{ steps.configure.outputs.release_tag }} | |
| prev_release: ${{ steps.configure.outputs.prev_release }} | |
| branch: ${{ steps.configure.outputs.branch }} | |
| on_release_branch: ${{ steps.configure.outputs.on_release_branch }} | |
| channel: ${{ steps.configure.outputs.channel }} | |
| already_published: ${{ steps.configure.outputs.already_published }} | |
| notes: ${{ steps.configure.outputs.notes }} | |
| package: ${{ steps.configure.outputs.package }} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Configure release | |
| id: configure | |
| uses: ./actions/release/lang/js/configure | |
| with: | |
| version: ${{ needs.bump.outputs.version }} | |
| sha: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| working_directory: test/release/js | |
| release_type: ${{ inputs.release_type || 'stable' }} | |
| npm_channel: ${{ inputs.channel }} | |
| npm_package_name: '@braintrust/bt-publishing-test' | |
| package_label: '@braintrust/bt-publishing-test' | |
| emoji: ':javascript:' | |
| prev_release: ${{ inputs.prev_release || github.event.pull_request.head.sha }} | |
| # JUDGE (checks only — pack does the build, so build/sbom are off here). | |
| validate: | |
| needs: [bump, configure] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 10 | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Validate release | |
| uses: ./actions/release/lang/js/validate | |
| with: | |
| sha: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} | |
| working_directory: test/release/js | |
| node_version: test/release/js/.tool-versions | |
| release_tag: ${{ needs.configure.outputs.release_tag }} | |
| channel: ${{ needs.configure.outputs.channel }} | |
| allowed_channels: 'latest,rc,next,beta' | |
| on_release_branch: ${{ needs.configure.outputs.on_release_branch }} | |
| notes: ${{ needs.configure.outputs.notes }} | |
| already_published: ${{ needs.configure.outputs.already_published }} | |
| build: 'false' # the pack job owns the build in the custom shape | |
| sbom: 'false' # pack generates + attests the SBOM | |
| # TEMP (idempotency test): relax so a re-run of an already-published version reaches | |
| # ship (mirrors the multi-package config). Revert after confirming. | |
| check_version_unpublished: 'false' | |
| check_tag_unused: 'false' | |
| # BUILD side — unprivileged: NO publish credentials. Builds + packs + attests, then uploads. | |
| pack: | |
| needs: [bump, configure, validate] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| permissions: | |
| contents: read | |
| id-token: write # sign attestations | |
| attestations: write # write attestations | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| ref: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| fetch-depth: 0 | |
| # bt-publishing-test: write the resolved version into package.json before packing. | |
| # Real SDK workflows omit this — the version is committed in the bump PR. | |
| - name: Apply version to package.json | |
| env: | |
| VERSION: ${{ needs.configure.outputs.version }} | |
| run: | | |
| node -e 'const fs=require("fs");const f="test/release/js/package.json";const p=JSON.parse(fs.readFileSync(f,"utf8"));p.version=process.env.VERSION;fs.writeFileSync(f,JSON.stringify(p,null,2)+"\n")' | |
| - name: Pack + attest | |
| id: pack | |
| uses: ./actions/release/lang/js/pack-pnpm | |
| with: | |
| sha: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| checkout: 'false' # version patch above must survive | |
| working_directory: test/release/js | |
| node_version: test/release/js/.tool-versions | |
| dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} | |
| # Stage the exact packed tarball + its SBOM for the gated ship job. | |
| - name: Stage artifact | |
| run: | | |
| mkdir -p artifact | |
| cp "${{ steps.pack.outputs.tarball }}" artifact/ | |
| cp test/release/js/sbom.json artifact/ 2>/dev/null || true | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: release-package | |
| path: artifact/ | |
| retention-days: 1 | |
| if-no-files-found: error | |
| # NOTIFY — approval request (gated behind validate; the real publish gate is on ship-package). | |
| request-approval: | |
| needs: [configure, validate, pack] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: {} | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Request release approval | |
| uses: ./actions/release/request-approval | |
| with: | |
| packages: '[${{ needs.configure.outputs.package }}]' | |
| sha: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| branch: ${{ needs.configure.outputs.branch }} | |
| on_release_branch: ${{ needs.configure.outputs.on_release_branch }} | |
| commit_message: ${{ needs.configure.outputs.commit_message }} | |
| dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} | |
| slack_token: ${{ github.event_name != 'pull_request' && secrets.SLACK_BOT_TOKEN || '' }} | |
| slack_channel: ${{ github.event_name != 'pull_request' && vars.SLACK_SDK_RELEASE_CHANNEL || '' }} | |
| # PUBLISH side — gated. Downloads the prebuilt artifact, verifies, publishes THAT file. | |
| ship-package: | |
| needs: [configure, validate, pack, request-approval] | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| environment: >- | |
| ${{ github.event_name != 'pull_request' | |
| && (inputs.dry_run && 'publish-dry-run' || 'publish') | |
| || '' }} | |
| permissions: | |
| contents: write | |
| id-token: write # OIDC trusted publishing + provenance | |
| attestations: read # gh attestation verify | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - name: Download artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: release-package | |
| path: dl | |
| - name: Resolve tarball | |
| id: art | |
| run: echo "tarball=$(ls dl/*.tgz)" >> "$GITHUB_OUTPUT" | |
| - name: Ship package | |
| uses: ./actions/release/lang/js/ship-package | |
| with: | |
| tarball: ${{ steps.art.outputs.tarball }} | |
| working_directory: dl # holds the tarball + sbom.json (attached to the release) | |
| sha: ${{ inputs.sha || github.event.pull_request.head.sha }} | |
| dry_run: ${{ inputs.dry_run || github.event_name == 'pull_request' }} | |
| release_tag: ${{ needs.configure.outputs.release_tag }} | |
| already_published: ${{ needs.configure.outputs.already_published }} | |
| # ⚠️ TEST-BED EXCEPTION — do NOT copy. Real consumers wire the derived value: | |
| # github_release: ${{ needs.configure.outputs.github_release }} | |
| github_release: 'false' | |
| version: ${{ needs.configure.outputs.version }} | |
| package_name: '@braintrust/bt-publishing-test' | |
| label: '@braintrust/bt-publishing-test' | |
| access: 'public' | |
| provenance: 'true' | |
| channel: ${{ needs.configure.outputs.channel }} | |
| notes: ${{ needs.configure.outputs.notes }} | |
| prev_release: ${{ needs.configure.outputs.prev_release }} | |
| branch: ${{ needs.configure.outputs.branch }} | |
| on_release_branch: ${{ needs.configure.outputs.on_release_branch }} | |
| slack_token: ${{ github.event_name != 'pull_request' && secrets.SLACK_BOT_TOKEN || '' }} | |
| slack_channel: ${{ github.event_name != 'pull_request' && vars.SLACK_SDK_RELEASE_CHANNEL || '' }} | |
| emoji: ':javascript:' |