Repository navigation
publish: npm (f846a674-556d-4d6c-8141-ca41b84fd405) #7
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
| name: 'publish: npm' | |
| run-name: 'publish: npm (${{ inputs.coordinator-id || inputs.task-name }})' | |
| # Fleet-canonical publish workflow with one repo-owned step cutout. | |
| # The shared publish script owns the packaging and upload checks. The shell: | |
| # checkout → setup-and-install → build → scripts/fleet/npm/publish/run-cli.mts, which | |
| # owns what + how the repo publishes. | |
| # | |
| # Default flow: manual dispatch; packages, validates, and publishes the | |
| # workspace's publishable packages via the fleet staged-publish script with | |
| # npm provenance (OIDC trusted publishing — id-token: write, no long-lived | |
| # npm token). | |
| # | |
| # CI writes the changelog and version, commits the source, and reserves the tag | |
| # before staging so packed README links and badges resolve. The coordinator | |
| # scans the exact staged bytes, approves only a zero-alert scan with npm's | |
| # attended proof-of-presence, then verifies the public tarball against the | |
| # recorded stage digests before creating the GitHub release. | |
| # | |
| # BACKFILL: to republish prior content as a skipped GAP version — 1.4.3 | |
| # between a live 1.4.2 and 1.4.4 — dispatch from MAIN, where this file always | |
| # exists, with `backfill-version` + `checkout-ref`. The checkout-ref supplies | |
| # the CONTENT while the workflow definition stays main's. The bump/changelog | |
| # gate is bypassed; hard gap-fill-only guards replace it (never-published | |
| # version, lower than latest, non-latest dist-tag, content declares its own | |
| # version) — see scripts/fleet/registry/npm/backfill.mts. | |
| # | |
| # NAPI ADDON PATH: not here. A member that declares a `napi` block in | |
| # .config/repo/socket-wheelhouse.json receives a SEPARATE, conditionally | |
| # cascaded `.github/workflows/publish-npm-addons.yml` carrying the per-platform | |
| # `.node` build + platform-package publish. GitHub parses a workflow against | |
| # the repo's Actions allowlist BEFORE evaluating any job-level `if:`, so addon | |
| # jobs living in this fleet-wide file would force the Rust toolchain actions | |
| # onto every member's allowlist — and a strict-allowlist member that lacks them | |
| # fails the whole file at startup with zero jobs and no logs. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| task-name: | |
| description: 'Package publish task name generated by the dispatch script.' | |
| required: true | |
| type: string | |
| coordinator-id: | |
| description: 'Local coordinator identity for exact run continuation.' | |
| required: false | |
| type: string | |
| default: '' | |
| dist-tag: | |
| description: 'npm dist-tag to publish under.' | |
| type: string | |
| default: 'latest' | |
| release-as: | |
| description: 'Release target: patch, minor, or an exact non-major X.Y.Z version.' | |
| type: string | |
| default: '' | |
| nightly-version: | |
| description: 'Planned X.Y.Z-nightly.YYYYMMDD.N target; requires dist-tag nightly.' | |
| type: string | |
| default: '' | |
| backfill-version: | |
| description: >- | |
| Backfill a never-published GAP version below registry latest with | |
| the content at checkout-ref. Bypasses the bump/changelog gate | |
| behind hard gap-fill-only guards; requires checkout-ref and a | |
| non-latest dist-tag. | |
| type: string | |
| default: '' | |
| checkout-ref: | |
| description: >- | |
| Backfill or reserved-release resume — the tag, branch, or SHA whose | |
| content is published while the workflow definition stays on main. | |
| type: string | |
| default: '' | |
| resume-publish: | |
| description: >- | |
| Resume publishing after squash-merging the release PR. Keep its title | |
| unchanged and dispatch from the default branch with checkout-ref empty. | |
| type: boolean | |
| default: false | |
| coverage-tooling-ref: | |
| description: 'Immutable fleet-pack SHA reference for reserved release coverage tooling.' | |
| type: string | |
| default: '' | |
| scan-package: | |
| description: 'Package name for a CI-only staged-byte Socket scan.' | |
| type: string | |
| default: '' | |
| scan-version: | |
| description: 'Package version for the CI-only scan.' | |
| type: string | |
| default: '' | |
| scan-stage-id: | |
| description: 'npm stage ID. A non-empty value selects scan-only mode.' | |
| type: string | |
| default: '' | |
| scan-stage-sha1: | |
| description: 'Exact npm-recorded sha1 for the staged tarball.' | |
| type: string | |
| default: '' | |
| scan-source-sha: | |
| description: 'Full signed release-App bump SHA that produced the stage.' | |
| type: string | |
| default: '' | |
| scan-publish-run-id: | |
| description: 'Successful original publish workflow run ID.' | |
| type: string | |
| default: '' | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: npm-publish-${{ github.repository }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| coverage-plan: | |
| if: ${{ inputs.scan-stage-id == '' && inputs.backfill-version == '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} | |
| outputs: | |
| runner: ${{ steps.coverage_plan.outputs.runner }} | |
| shared-build: ${{ steps.coverage_plan.outputs.shared-build }} | |
| name: Coverage plan | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Bootstrap checkout | |
| shell: bash | |
| env: &coverage-bootstrap-env | |
| CHECKOUT_REF: ${{ inputs.checkout-ref || github.sha }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| RESUME_PUBLISH: ${{ inputs.resume-publish }} | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SERVER_URL: ${{ github.server_url }} | |
| REPOSITORY: ${{ github.repository }} | |
| TRIGGER_REF: ${{ github.ref }} | |
| run: | | |
| set -euo pipefail | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote remove origin 2>/dev/null || true | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| FETCH_REF="${CHECKOUT_REF:-${TRIGGER_REF}}" | |
| FETCH_ARGS=(--no-tags --prune --depth 1 origin "${FETCH_REF}") | |
| if [ -n "${GITHUB_TOKEN}" ]; then | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch "${FETCH_ARGS[@]}" | |
| else | |
| git fetch "${FETCH_ARGS[@]}" | |
| fi | |
| git checkout -q --detach FETCH_HEAD | |
| if [ "$RESUME_PUBLISH" = "true" ] && [ "$TRIGGER_REF" = "refs/heads/$DEFAULT_BRANCH" ]; then | |
| if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then | |
| git fetch --no-tags --depth=32 origin "$FETCH_REF" | |
| fi | |
| git fetch --no-tags origin "$WORKFLOW_SHA" | |
| BOOTSTRAP_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT | |
| BOOTSTRAP="$BOOTSTRAP_DIR/fleet.mjs" | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$BOOTSTRAP" | |
| node --input-type=module -e ' | |
| const { pathToFileURL } = await import("node:url"); | |
| const { migrateRuleFile } = await import(pathToFileURL(process.argv[2]).href); | |
| migrateRuleFile(process.argv[3]); | |
| ' release-rule-recovery "$BOOTSTRAP" "$GITHUB_WORKSPACE" | |
| rm -rf "$BOOTSTRAP_DIR" | |
| trap - EXIT | |
| fi | |
| - name: 'Set up and install' | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| socket-api-token: ${{ inputs.checkout-ref == '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| payload-token-client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| payload-token-private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| checkout-ref: ${{ inputs.checkout-ref || github.sha }} | |
| - name: Select coverage runner | |
| id: coverage_plan | |
| shell: bash | |
| run: | | |
| node --input-type=module <<'NODE' | |
| import { appendFileSync } from 'node:fs'; | |
| import { resolveRunPlan } from './scripts/fleet/cover-run.mts'; | |
| const plan = resolveRunPlan(process.cwd()); | |
| if (plan.configErrors.length) throw new Error(plan.configErrors.join('\n')); | |
| if (!['bun', 'vitest'].includes(plan.runner)) throw new Error('Unsupported coverage runner. Fix the repository coverage settings.'); | |
| appendFileSync(process.env.GITHUB_OUTPUT, `runner=${plan.runner}\n`); | |
| const config = JSON.parse(await (await import('node:fs/promises')).readFile('.config/repo/socket-wheelhouse.json', 'utf8')); | |
| appendFileSync(process.env.GITHUB_OUTPUT, `shared-build=${plan.runner === 'vitest' && Array.isArray(config.build?.coverageArtifacts) && config.build.coverageArtifacts.length > 0}\n`); | |
| NODE | |
| coverage-build: | |
| needs: [coverage-plan] | |
| if: ${{ inputs.scan-stage-id == '' && inputs.backfill-version == '' && needs.coverage-plan.result == 'success' && needs.coverage-plan.outputs.shared-build == 'true' }} | |
| name: Coverage source-map build | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 120 | |
| steps: | |
| - name: Bootstrap checkout | |
| shell: bash | |
| env: *coverage-bootstrap-env | |
| run: | | |
| set -euo pipefail | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch --no-tags --depth 1 origin "${CHECKOUT_REF:-${TRIGGER_REF}}" | |
| git checkout -q --detach FETCH_HEAD | |
| - name: Set up and install | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| socket-api-token: ${{ inputs.checkout-ref == '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| payload-token-client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| payload-token-private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| checkout-ref: ${{ inputs.checkout-ref || github.sha }} | |
| - name: Build coverage prerequisites | |
| env: | |
| FLEET_COVERAGE_PREREQUISITE_BUILD: 'true' | |
| run: pnpm run build | |
| - name: Build source maps | |
| env: | |
| COVERAGE: 'true' | |
| run: pnpm run build | |
| - name: Package verified coverage build | |
| run: node scripts/fleet/cover-build-artifact.mts create .cache/fleet/coverage-build | |
| - name: Upload coverage build | |
| uses: ./.github/actions/fleet/upload-artifact | |
| with: | |
| name: npm-publish-coverage-build | |
| path: .cache/fleet/coverage-build | |
| coverage-shards: | |
| needs: [coverage-plan, coverage-build] | |
| if: ${{ !cancelled() && inputs.scan-stage-id == '' && inputs.backfill-version == '' && needs.coverage-plan.result == 'success' && (needs.coverage-build.result == 'success' || needs.coverage-build.result == 'skipped') && needs.coverage-plan.outputs.runner == 'vitest' }} | |
| outputs: | |
| shard-count: ${{ strategy.job-total }} | |
| name: Coverage collection | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 60 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] | |
| steps: | |
| - name: Bootstrap checkout | |
| shell: bash | |
| env: *coverage-bootstrap-env | |
| run: | | |
| set -euo pipefail | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote remove origin 2>/dev/null || true | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| FETCH_REF="${CHECKOUT_REF:-${TRIGGER_REF}}" | |
| FETCH_ARGS=(--no-tags --prune --depth 1 origin "${FETCH_REF}") | |
| if [ -n "${GITHUB_TOKEN}" ]; then | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch "${FETCH_ARGS[@]}" | |
| else | |
| git fetch "${FETCH_ARGS[@]}" | |
| fi | |
| git checkout -q --detach FETCH_HEAD | |
| if [ "$RESUME_PUBLISH" = "true" ] && [ "$TRIGGER_REF" = "refs/heads/$DEFAULT_BRANCH" ]; then | |
| if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then | |
| git fetch --no-tags --depth=32 origin "$FETCH_REF" | |
| fi | |
| git fetch --no-tags origin "$WORKFLOW_SHA" | |
| BOOTSTRAP_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT | |
| BOOTSTRAP="$BOOTSTRAP_DIR/fleet.mjs" | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$BOOTSTRAP" | |
| node --input-type=module -e ' | |
| const { pathToFileURL } = await import("node:url"); | |
| const { migrateRuleFile } = await import(pathToFileURL(process.argv[2]).href); | |
| migrateRuleFile(process.argv[3]); | |
| ' release-rule-recovery "$BOOTSTRAP" "$GITHUB_WORKSPACE" | |
| rm -rf "$BOOTSTRAP_DIR" | |
| trap - EXIT | |
| fi | |
| - name: Download shared coverage build | |
| if: ${{ needs.coverage-plan.outputs.shared-build == 'true' }} | |
| uses: ./.github/actions/fleet/download-artifact | |
| with: | |
| name: npm-publish-coverage-build | |
| path: .cache/fleet/coverage-build | |
| - name: 'Set up and install' | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| socket-api-token: ${{ inputs.checkout-ref == '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| payload-token-client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| payload-token-private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| checkout-ref: ${{ inputs.checkout-ref || github.sha }} | |
| - name: Restore reserved coverage tooling | |
| if: ${{ inputs.resume-publish == true }} | |
| shell: bash | |
| env: | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| COVERAGE_PACK_REF: ${{ inputs.coverage-tooling-ref }} | |
| run: &restore-reserved-coverage-tooling | | |
| set -euo pipefail | |
| TOOLING_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$TOOLING_DIR"' EXIT | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$TOOLING_DIR/fleet.mjs" | |
| node --input-type=module - "$TOOLING_DIR" "$COVERAGE_PACK_REF" <<'NODE' | |
| import { execFileSync } from 'node:child_process'; | |
| import { lstatSync, mkdirSync, realpathSync, renameSync } from 'node:fs'; | |
| import path from 'node:path'; | |
| import { pathToFileURL } from 'node:url'; | |
| const [temporary, ref] = process.argv.slice(2); | |
| if (!/^fleet-pack-[a-f\d]{40}$/.test(ref)) throw new Error('Invalid immutable coverage tooling reference.'); | |
| const root = process.cwd(); | |
| const files = ['scripts/fleet/cover/shard-run.mts', 'scripts/fleet/cover/shard-revision.mts']; | |
| const tracked = execFileSync('git', ['ls-files', '--', ...files], { encoding: 'utf8' }); | |
| if (tracked.trim()) throw new Error('Coverage tooling must be an untracked fleet payload.'); | |
| const targets = files.map((relative, index) => { | |
| const target = path.join(root, relative); | |
| const entry = lstatSync(target, { throwIfNoEntry: false }); | |
| if ((entry ? !entry.isFile() : index === 0) || realpathSync(path.dirname(target)) !== path.dirname(target)) throw new Error('Coverage tooling must be a regular local file.'); | |
| return target; | |
| }); | |
| const { installFleet } = await import(pathToFileURL(path.join(temporary, 'fleet.mjs')).href); | |
| const extracted = path.join(realpathSync(temporary), 'verified'); | |
| mkdirSync(extracted); | |
| execFileSync('git', ['init', '-q', extracted]); | |
| if (await installFleet({ ref, dest: extracted, preserveTracked: true }) !== 0) throw new Error('Coverage tooling pack verification failed.'); | |
| const sources = files.map(relative => path.join(extracted, relative)); | |
| for (const source of sources) { | |
| if (!lstatSync(source).isFile() || realpathSync(source) !== source) throw new Error('Verified coverage tooling is incomplete.'); | |
| } | |
| for (let index = 0; index < sources.length; index++) renameSync(sources[index], targets[index]); | |
| NODE | |
| - name: Build | |
| if: ${{ needs.coverage-plan.outputs.shared-build != 'true' }} | |
| run: pnpm run build | |
| # repo-cutout: coverage-shards-build | |
| # <repo> | |
| # </repo> | |
| - name: 'Run coverage' | |
| id: coverage_collection | |
| uses: ./.github/actions/fleet/run-script | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| COVERAGE_SHARD: ${{ matrix.shard }} | |
| COVERAGE_SHARD_COUNT: ${{ strategy.job-total }} | |
| FLEET_COVERAGE_BUILD_ARTIFACT: ${{ needs.coverage-plan.outputs.shared-build == 'true' && format('{0}/.cache/fleet/coverage-build', github.workspace) || '' }} | |
| with: | |
| main-script: pnpm run ci:gates --stage=cover-shard --shard="$COVERAGE_SHARD/$COVERAGE_SHARD_COUNT" | |
| - name: Upload coverage collection | |
| uses: ./.github/actions/fleet/upload-artifact | |
| with: | |
| name: npm-publish-coverage-shard-${{ matrix.shard }} | |
| path: .cache/fleet/coverage-shards/${{ matrix.shard }} | |
| - name: Preserve coverage failure output | |
| if: ${{ failure() && steps.coverage_collection.outcome == 'failure' }} | |
| uses: ./.github/actions/fleet/upload-artifact | |
| with: | |
| name: npm-publish-coverage-shard-${{ matrix.shard }}-failure | |
| path: | | |
| .cache/fleet/fleet-cover | |
| .cache/fleet/coverage-shards/${{ matrix.shard }} | |
| if-no-files-found: ignore | |
| coverage: | |
| needs: [coverage-plan, coverage-build, coverage-shards] | |
| if: ${{ !cancelled() && inputs.scan-stage-id == '' && inputs.backfill-version == '' }} | |
| name: Coverage gate | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 60 | |
| steps: | |
| - name: Require successful coverage prerequisites | |
| shell: bash | |
| env: | |
| PLAN_RESULT: ${{ needs.coverage-plan.result }} | |
| COVERAGE_RUNNER: ${{ needs.coverage-plan.outputs.runner }} | |
| COLLECTION_RESULT: ${{ needs.coverage-shards.result }} | |
| run: | | |
| if [ "$PLAN_RESULT" != success ]; then | |
| echo "Coverage planning failed. Fix the coverage plan before publishing." | |
| exit 1 | |
| fi | |
| case "$COVERAGE_RUNNER:$COLLECTION_RESULT" in | |
| vitest:success|bun:skipped) ;; | |
| *) | |
| echo "Coverage prerequisites failed. Fix the coverage runner or collection jobs before publishing." | |
| exit 1 | |
| ;; | |
| esac | |
| - name: Bootstrap checkout | |
| shell: bash | |
| env: | |
| CHECKOUT_REF: ${{ inputs.checkout-ref || github.sha }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| RESUME_PUBLISH: ${{ inputs.resume-publish }} | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SERVER_URL: ${{ github.server_url }} | |
| REPOSITORY: ${{ github.repository }} | |
| TRIGGER_REF: ${{ github.ref }} | |
| run: | | |
| set -euo pipefail | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote remove origin 2>/dev/null || true | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| FETCH_REF="${CHECKOUT_REF:-${TRIGGER_REF}}" | |
| FETCH_ARGS=(--no-tags --prune --depth 1 origin "${FETCH_REF}") | |
| if [ -n "${GITHUB_TOKEN}" ]; then | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch "${FETCH_ARGS[@]}" | |
| else | |
| git fetch "${FETCH_ARGS[@]}" | |
| fi | |
| git checkout -q --detach FETCH_HEAD | |
| if [ "$RESUME_PUBLISH" = "true" ] && [ "$TRIGGER_REF" = "refs/heads/$DEFAULT_BRANCH" ]; then | |
| if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then | |
| git fetch --no-tags --depth=32 origin "$FETCH_REF" | |
| fi | |
| git fetch --no-tags origin "$WORKFLOW_SHA" | |
| BOOTSTRAP_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT | |
| BOOTSTRAP="$BOOTSTRAP_DIR/fleet.mjs" | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$BOOTSTRAP" | |
| node --input-type=module -e ' | |
| const { pathToFileURL } = await import("node:url"); | |
| const { migrateRuleFile } = await import(pathToFileURL(process.argv[2]).href); | |
| migrateRuleFile(process.argv[3]); | |
| ' release-rule-recovery "$BOOTSTRAP" "$GITHUB_WORKSPACE" | |
| rm -rf "$BOOTSTRAP_DIR" | |
| trap - EXIT | |
| fi | |
| - name: 'Set up and install' | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| socket-api-token: ${{ inputs.checkout-ref == '' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| payload-token-client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| payload-token-private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| checkout-ref: ${{ inputs.checkout-ref || github.sha }} | |
| - name: Download shared coverage build | |
| if: ${{ needs.coverage-plan.outputs.shared-build == 'true' }} | |
| uses: ./.github/actions/fleet/download-artifact | |
| with: | |
| name: npm-publish-coverage-build | |
| path: .cache/fleet/coverage-build | |
| - name: Setup Bun coverage runtime | |
| if: ${{ needs.coverage-plan.outputs.runner == 'bun' }} | |
| shell: bash | |
| run: | | |
| node --input-type=module <<'NODE' | |
| import fs from 'node:fs' | |
| import path from 'node:path' | |
| import { ensureTool } from './scripts/fleet/external-tools/manager.mts' | |
| const result = await ensureTool('bun') | |
| if (!result.available || !result.binPath) { | |
| throw new Error(`Bun coverage runtime installation failed. Where: npm coverage setup. Saw: ${result.error ?? 'no executable returned'}. Wanted: the configured Bun executable. Fix: check the member tool configuration and retry.`) | |
| } | |
| fs.appendFileSync(process.env.GITHUB_PATH, `${path.dirname(result.binPath)}\n`) | |
| NODE | |
| - name: 'Download coverage artifacts' | |
| if: ${{ needs.coverage-plan.outputs.runner == 'vitest' }} | |
| uses: ./.github/actions/fleet/download-artifact | |
| with: | |
| path: .cache/fleet/npm-publish-coverage | |
| - name: Arrange coverage artifacts | |
| if: ${{ needs.coverage-plan.outputs.runner == 'vitest' }} | |
| shell: bash | |
| env: | |
| COVERAGE_SHARD_COUNT: ${{ needs.coverage-shards.outputs.shard-count }} | |
| run: | | |
| set -euo pipefail | |
| for index in $(seq 1 "$COVERAGE_SHARD_COUNT"); do | |
| source=".cache/fleet/npm-publish-coverage/npm-publish-coverage-shard-$index" | |
| target=".cache/fleet/coverage-shards/$index" | |
| if [ ! -d "$source" ]; then | |
| echo "Missing coverage artifact for shard $index." >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "$target" | |
| cp -R "$source/." "$target/" | |
| done | |
| - name: Restore reserved coverage tooling | |
| if: ${{ inputs.resume-publish == true }} | |
| shell: bash | |
| env: | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| COVERAGE_PACK_REF: ${{ inputs.coverage-tooling-ref }} | |
| run: *restore-reserved-coverage-tooling | |
| - name: Install cargo-llvm-cov | |
| if: ${{ hashFiles('**/Cargo.toml') != '' }} | |
| run: pnpm run setup:rust-coverage | |
| - name: Build | |
| if: ${{ needs.coverage-plan.outputs.shared-build != 'true' }} | |
| run: pnpm run build | |
| # repo-cutout: coverage-final-build | |
| # <repo> | |
| # </repo> | |
| - name: Aggregate coverage | |
| if: ${{ needs.coverage-plan.outputs.runner == 'vitest' }} | |
| env: | |
| COVERAGE_SHARD_COUNT: ${{ needs.coverage-shards.outputs.shard-count }} | |
| FLEET_COVERAGE_BUILD_ARTIFACT: ${{ needs.coverage-plan.outputs.shared-build == 'true' && format('{0}/.cache/fleet/coverage-build', github.workspace) || '' }} | |
| run: pnpm run cover:aggregate --shards="$COVERAGE_SHARD_COUNT" | |
| - name: Run Bun coverage | |
| if: ${{ needs.coverage-plan.outputs.runner == 'bun' }} | |
| run: pnpm run cover | |
| npm-publish: | |
| needs: [coverage] | |
| if: ${{ always() && inputs.scan-stage-id == '' && (inputs.backfill-version != '' || needs.coverage.result == 'success') }} | |
| cache-mode: read | |
| name: Publish npm | |
| runs-on: ${{ vars.NPM_PUBLISH_RUNNER || vars.ODAI_RUNNER || 'ubuntu-26.04' }} | |
| # npm's trusted-publisher config pins this GitHub environment name; the | |
| # OIDC token exchange 404s if the job runs outside it. | |
| environment: publish-npm | |
| permissions: | |
| actions: read | |
| contents: read | |
| # npm provenance / trusted publishing mints its OIDC token here. | |
| id-token: write | |
| steps: | |
| # First step can't call the local ./.github/actions/fleet/checkout | |
| # composite (nothing checked out yet); bootstrap the workspace with the | |
| # inline git-fetch shape so setup-and-install can re-check-out at its own | |
| # deeper default. Two npm-publish specifics: a backfill fetches the | |
| # checkout-ref content ref (empty = the dispatched ref), and the fetch | |
| # carries --tags — the bump derivation anchors on registry-latest + the | |
| # last v-tag, and on a first-publish repo the registry has nothing, so | |
| # the tags are the only anchor; a tagless shallow fetch makes the engine | |
| # derive from zero (0.1.0) and trip the half-applied-bump gate on | |
| # historical CHANGELOG sections that describe shipped versions. | |
| - name: Bootstrap checkout | |
| shell: bash | |
| env: | |
| # Route context through env (no ${{ }} in the shell body — | |
| # zizmor expression-injection). Token authorizes the fetch inline and | |
| # is never persisted to .git/config. | |
| CHECKOUT_REF: ${{ inputs.checkout-ref || github.sha }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| RESUME_PUBLISH: ${{ inputs.resume-publish }} | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SERVER_URL: ${{ github.server_url }} | |
| REPOSITORY: ${{ github.repository }} | |
| TRIGGER_REF: ${{ github.ref }} | |
| run: | | |
| set -euo pipefail | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote remove origin 2>/dev/null || true | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| # Backfill's content ref wins; otherwise the dispatched ref. | |
| FETCH_REF="${CHECKOUT_REF:-${TRIGGER_REF}}" | |
| FETCH_ARGS=(--prune origin "${FETCH_REF}") | |
| # --tags stays on the fetch line itself so the | |
| # version-derivation-jobs-have-tags gate can see it. | |
| if [ -n "${GITHUB_TOKEN}" ]; then | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch --tags "${FETCH_ARGS[@]}" | |
| else | |
| git fetch --tags "${FETCH_ARGS[@]}" | |
| fi | |
| git checkout -q --detach FETCH_HEAD | |
| if [ "$RESUME_PUBLISH" = "true" ] && [ "$TRIGGER_REF" = "refs/heads/$DEFAULT_BRANCH" ]; then | |
| if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then | |
| git fetch --no-tags --depth=32 origin "$FETCH_REF" | |
| fi | |
| git fetch --no-tags origin "$WORKFLOW_SHA" | |
| BOOTSTRAP_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT | |
| BOOTSTRAP="$BOOTSTRAP_DIR/fleet.mjs" | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$BOOTSTRAP" | |
| node --input-type=module -e ' | |
| const { pathToFileURL } = await import("node:url"); | |
| const { migrateRuleFile } = await import(pathToFileURL(process.argv[2]).href); | |
| migrateRuleFile(process.argv[3]); | |
| ' release-rule-recovery "$BOOTSTRAP" "$GITHUB_WORKSPACE" | |
| rm -rf "$BOOTSTRAP_DIR" | |
| trap - EXIT | |
| fi | |
| - name: Establish trusted release content | |
| id: content-trust | |
| shell: bash | |
| env: | |
| BACKFILL_VERSION: ${{ inputs.backfill-version }} | |
| CHECKOUT_REF: ${{ inputs.checkout-ref }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| DISPATCH_REF: ${{ github.ref }} | |
| RESUME_PUBLISH: ${{ inputs.resume-publish }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SERVER_URL: ${{ github.server_url }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$DISPATCH_REF" != "refs/heads/$DEFAULT_BRANCH" ]; then | |
| echo "trusted=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ -z "$CHECKOUT_REF" ]; then | |
| echo "trusted=true" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if [ "$RESUME_PUBLISH" != "true" ] || [ -n "$BACKFILL_VERSION" ] || [[ ! "$CHECKOUT_REF" =~ ^(v[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?|[a-f0-9]{40})$ ]]; then | |
| echo "trusted=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| AUTH_B64="$(printf 'x-access-token:%s' "$GITHUB_TOKEN" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch --no-tags origin "refs/heads/$DEFAULT_BRANCH:refs/remotes/origin/$DEFAULT_BRANCH" | |
| if [[ "$CHECKOUT_REF" =~ ^[a-f0-9]{40}$ ]]; then | |
| SOURCE_SHA="$CHECKOUT_REF" | |
| else | |
| SOURCE_SHA="$(git rev-parse "refs/tags/$CHECKOUT_REF^{commit}")" | |
| fi | |
| HEAD_SHA="$(git rev-parse HEAD)" | |
| if [ "$SOURCE_SHA" != "$HEAD_SHA" ] || ! git merge-base --is-ancestor "$SOURCE_SHA" "refs/remotes/origin/$DEFAULT_BRANCH"; then | |
| echo "trusted=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| echo "trusted=true" >> "$GITHUB_OUTPUT" | |
| # `latest` is what an untagged install of the package resolves to, so it belongs | |
| # to whichever branch carries the line customers actually consume. For | |
| # almost every member that IS the default branch, which is the default | |
| # here — those repos see no behavior change. | |
| # | |
| # A member whose consumable line is NOT the default branch declares it as | |
| # `release.latestDistTagBranch` in .config/repo/socket-wheelhouse.json — | |
| # the shape being a maintenance branch shipping to users while the | |
| # default branch carries a prerelease major. | |
| # | |
| # Read from the manifest rather than hard-coded so one file states the | |
| # law for the whole fleet and each member parameterizes it. | |
| - name: Guard the latest dist-tag to the consumable release line | |
| if: ${{ inputs.dist-tag == 'latest' }} | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| REF: ${{ github.ref }} | |
| run: | | |
| LATEST_BRANCH="$(node -e ' | |
| const fs = require("node:fs") | |
| const p = ".config/repo/socket-wheelhouse.json" | |
| let branch = "" | |
| try { | |
| branch = JSON.parse(fs.readFileSync(p, "utf8"))?.release?.latestDistTagBranch ?? "" | |
| } catch {} | |
| process.stdout.write(String(branch)) | |
| ')" | |
| if [ -z "$LATEST_BRANCH" ]; then | |
| LATEST_BRANCH="$DEFAULT_BRANCH" | |
| fi | |
| if [ "$REF" != "refs/heads/$LATEST_BRANCH" ]; then | |
| echo "::error::Refusing to publish dist-tag 'latest' from $REF." >&2 | |
| echo "::error::Where: this dispatch, against the '$LATEST_BRANCH' consumable release line." >&2 | |
| echo "::error::Saw vs wanted: 'latest' requested off refs/heads/$LATEST_BRANCH; 'latest' is what an untagged install resolves to, so only the consumable line may move it." >&2 | |
| echo "::error::Fix: re-dispatch from $LATEST_BRANCH, or pick a prerelease dist-tag (next, beta, canary, rc). To change which branch owns 'latest', set release.latestDistTagBranch in .config/repo/socket-wheelhouse.json." >&2 | |
| exit 1 | |
| fi | |
| echo "dist-tag 'latest' is allowed from $REF (consumable line: $LATEST_BRANCH)." | |
| - name: Set up and install | |
| uses: ./.github/actions/fleet/setup-and-install | |
| env: | |
| SOCKET_API_KEY: ${{ steps.content-trust.outputs.trusted == 'true' && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| with: | |
| cache: 'false' | |
| checkout-fetch-depth: '0' | |
| socket-api-token: ${{ steps.content-trust.outputs.trusted == 'true' && secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW || '' }} | |
| # Forward the backfill content ref — setup-and-install re-checks-out | |
| # internally (fleet checkout falls back to the TRIGGERING ref when | |
| # unset), which would silently swap the backfill content back to | |
| # main's tree; the backfill gate then refuses against main's | |
| # version. Empty forwards as unset, so normal dispatches keep the | |
| # dispatched-ref re-checkout. | |
| checkout-ref: ${{ inputs.checkout-ref || github.sha }} | |
| # Reuse the Release App for a contents:read token scoped to wheelhouse. | |
| # Both credentials enable the private release fallback. Without the | |
| # key, hydration still pulls public GHCR anonymously. | |
| payload-token-client-id: ${{ steps.content-trust.outputs.trusted == 'true' && (secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID) || '' }} | |
| payload-token-private-key: ${{ steps.content-trust.outputs.trusted == 'true' && secrets.SOCKET_RELEASE_APP_PRIVATE_KEY || '' }} | |
| - name: Validate publish task name | |
| env: | |
| PUBLISH_TASK_NAME: ${{ inputs.task-name }} | |
| run: node scripts/fleet/registry/npm/publish-dispatch-context.mts --name "$PUBLISH_TASK_NAME" | |
| - name: Validate reserved release resume | |
| if: ${{ inputs.resume-publish == true }} | |
| env: | |
| BACKFILL_VERSION: ${{ inputs.backfill-version }} | |
| CHECKOUT_REF: ${{ inputs.checkout-ref }} | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| DISPATCH_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="$(node --input-type=module -e 'import { readPackageJson } from "./scripts/fleet/registry/npm/shared.mts"; process.stdout.write(readPackageJson().version)')" | |
| if [ -n "$BACKFILL_VERSION" ]; then | |
| echo "::error::Reserved release resume cannot use backfill-version." >&2 | |
| exit 1 | |
| fi | |
| if [ -n "$CHECKOUT_REF" ]; then | |
| echo "::error::Reserved release resume must use the merged default-branch source; checkout-ref must be empty." >&2 | |
| exit 1 | |
| fi | |
| if [ "$GITHUB_REF" != "refs/heads/$DEFAULT_BRANCH" ]; then | |
| echo "::error::Reserved release resume must run from the default branch." >&2 | |
| exit 1 | |
| fi | |
| HEAD_SHA="$(git rev-parse HEAD)" | |
| if [ "$HEAD_SHA" != "$DISPATCH_SHA" ]; then | |
| echo "::error::Reserved release resume did not check out the dispatched default-branch source." >&2 | |
| exit 1 | |
| fi | |
| BUMP_SUBJECT="$(git log -1 --format=%s --fixed-strings --grep="chore: bump version to $VERSION" HEAD)" | |
| if [ "$BUMP_SUBJECT" != "chore: bump version to $VERSION" ]; then | |
| echo "::error::Default-branch history does not contain the signed bump commit for $VERSION. Merge its release PR first." >&2 | |
| exit 1 | |
| fi | |
| BUMP_SHA="$(git log -1 --format=%H --fixed-strings --grep="chore: bump version to $VERSION" HEAD)" | |
| if [ -n "$(git status --porcelain)" ]; then | |
| echo "::error::Reserved release resume requires a clean checkout." >&2 | |
| exit 1 | |
| fi | |
| echo "[reserved-bump] $BUMP_SHA" | |
| echo "[reserved-source] $HEAD_SHA" | |
| - name: Build | |
| run: node scripts/fleet/build/production.mts | |
| # repo-cutout: publish-build | |
| # <repo> | |
| # </repo> | |
| - name: Stage verified native artifacts | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: node scripts/fleet/npm/native-artifacts.mts | |
| - name: Retain native artifact receipt | |
| uses: ./.github/actions/fleet/upload-artifact | |
| with: | |
| name: npm-native-artifacts | |
| path: .cache/fleet/native-addons/receipt.json | |
| if-no-files-found: ignore | |
| - name: Validate repository publish contract | |
| run: pnpm run --if-present prepublish:check | |
| - name: Set up odai | |
| if: ${{ inputs.backfill-version == '' && inputs.resume-publish != true }} | |
| uses: ./.github/actions/fleet/setup-odai | |
| with: | |
| allow-fill: 'true' | |
| require-ready: 'true' | |
| - name: 'Mint PR branch token' | |
| if: ${{ inputs.backfill-version == '' && steps.content-trust.outputs.trusted == 'true' }} | |
| id: pr-branch-app | |
| uses: ./.github/actions/fleet/github-pr-branch-app-token | |
| with: | |
| client-id: ${{ secrets.SOCKET_PR_CLIENT_ID || vars.SOCKET_PR_CLIENT_ID }} | |
| private-key: ${{ secrets.SOCKET_PR_APP_PRIVATE_KEY }} | |
| repositories: ${{ github.event.repository.name }} | |
| - name: 'Mint release token' | |
| if: ${{ inputs.backfill-version == '' && steps.content-trust.outputs.trusted == 'true' }} | |
| id: release-app | |
| uses: ./.github/actions/fleet/github-release-app-token | |
| with: | |
| client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| repositories: ${{ github.event.repository.name }} | |
| # The version resolver consumes a prerelease hint, uses configured odai | |
| # for patch/minor, or defaults to minor. Backfills keep their version. | |
| - name: Publish | |
| env: | |
| BACKFILL_VERSION: ${{ inputs.backfill-version }} | |
| CHECKOUT_REF: ${{ inputs.resume-publish != true && inputs.checkout-ref || '' }} | |
| DIST_TAG: ${{ inputs.dist-tag }} | |
| NIGHTLY_VERSION: ${{ inputs.nightly-version }} | |
| PR_APP_TOKEN: ${{ steps.pr-branch-app.outputs.token }} | |
| RELEASE_AS: ${{ inputs.release-as }} | |
| RESUME_PUBLISH: ${{ inputs.resume-publish }} | |
| RELEASE_APP_TOKEN: ${{ steps.release-app.outputs.token }} | |
| GH_TOKEN: ${{ steps.release-app.outputs.token }} | |
| # CHECKOUT_REF forwards on its own so a checkout-ref dispatch WITHOUT | |
| # backfill-version is refused by the script instead of silently | |
| # bump-publishing historical content. | |
| run: | | |
| args=(--tag "$DIST_TAG") | |
| if [ -n "$BACKFILL_VERSION" ]; then args+=(--backfill "$BACKFILL_VERSION"); fi | |
| if [ -n "$CHECKOUT_REF" ]; then args+=(--checkout-ref "$CHECKOUT_REF"); fi | |
| if [ -n "$RELEASE_AS" ]; then args+=(--release-as "$RELEASE_AS"); fi | |
| if [ -n "$NIGHTLY_VERSION" ]; then args+=(--nightly-version "$NIGHTLY_VERSION"); fi | |
| if [ "$RESUME_PUBLISH" = true ]; then args+=(--resume-publish); fi | |
| node scripts/fleet/npm/publish/run-cli.mts "${args[@]}" | |
| scan-staged-package: | |
| if: ${{ inputs.scan-stage-id != '' }} | |
| name: Scan staged npm package | |
| runs-on: ubuntu-26.04 | |
| environment: publish-npm | |
| timeout-minutes: 60 | |
| permissions: | |
| actions: read | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Bootstrap trusted scan controller | |
| shell: bash | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| GITHUB_TOKEN: ${{ github.token }} | |
| REPOSITORY: ${{ github.repository }} | |
| SERVER_URL: ${{ github.server_url }} | |
| run: | | |
| set -euo pipefail | |
| if [ "$GITHUB_REF" != "refs/heads/$DEFAULT_BRANCH" ]; then | |
| echo "::error::Scan mode must be dispatched from the default branch." >&2 | |
| exit 1 | |
| fi | |
| git init -q | |
| git config --local advice.detachedHead false | |
| git remote remove origin 2>/dev/null || true | |
| git remote add origin "${SERVER_URL}/${REPOSITORY}" | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch --no-tags --prune --depth 1 origin "+refs/heads/${DEFAULT_BRANCH}:refs/remotes/origin/${DEFAULT_BRANCH}" | |
| git checkout -q --detach "refs/remotes/origin/${DEFAULT_BRANCH}" | |
| install -m 0600 scripts/fleet/npm/scan/staged.mts "$RUNNER_TEMP/npm-scan-staged.mts" | |
| install -m 0600 scripts/fleet/npm/scan/receipt.mts "$RUNNER_TEMP/npm-scan-receipt.mts" | |
| install -m 0600 scripts/fleet/registry/npm/scan/ndjson.mts "$RUNNER_TEMP/npm-registry-scan-ndjson.mts" | |
| install -m 0600 scripts/fleet/registry/npm/scan/run.mts "$RUNNER_TEMP/npm-registry-scan.mts" | |
| - name: Verify release source before setup | |
| shell: bash | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| GH_TOKEN: ${{ github.token }} | |
| PUBLISH_RUN_ID: ${{ inputs.scan-publish-run-id }} | |
| REPOSITORY: ${{ github.repository }} | |
| SOURCE_SHA: ${{ inputs.scan-source-sha }} | |
| run: | | |
| set -euo pipefail | |
| [[ "$PUBLISH_RUN_ID" =~ ^[1-9][0-9]*$ ]] | |
| [[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]] | |
| RUN_JSON="$(gh api "repos/$REPOSITORY/actions/runs/$PUBLISH_RUN_ID")" | |
| test "$(jq -r '.repository.full_name' <<<"$RUN_JSON")" = "$REPOSITORY" | |
| test "$(jq -r '.head_repository.full_name' <<<"$RUN_JSON")" = "$REPOSITORY" | |
| test "$(jq -r '.path' <<<"$RUN_JSON")" = ".github/workflows/publish-npm.yml" | |
| test "$(jq -r '.event' <<<"$RUN_JSON")" = "workflow_dispatch" | |
| test "$(jq -r '.status' <<<"$RUN_JSON")" = "completed" | |
| test "$(jq -r '.conclusion' <<<"$RUN_JSON")" = "success" | |
| test "$(jq -r '.head_branch' <<<"$RUN_JSON")" = "$DEFAULT_BRANCH" | |
| RUN_HEAD="$(jq -r '.head_sha' <<<"$RUN_JSON")" | |
| COMMIT_JSON="$(gh api "repos/$REPOSITORY/commits/$SOURCE_SHA")" | |
| test "$(jq -r '.sha' <<<"$COMMIT_JSON")" = "$SOURCE_SHA" | |
| test "$(jq -r '.commit.verification.verified' <<<"$COMMIT_JSON")" = "true" | |
| gh api "repos/$REPOSITORY/actions/runs/$PUBLISH_RUN_ID/logs" > "$RUNNER_TEMP/publish-logs.zip" | |
| unzip -p "$RUNNER_TEMP/publish-logs.zip" > "$RUNNER_TEMP/publish-logs.txt" | |
| perl -pe 's/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z +//; s/\e\[[0-?]*[ -\/]*[@-~]//g' "$RUNNER_TEMP/publish-logs.txt" > "$RUNNER_TEMP/publish-logs.normalized.txt" | |
| export RUN_HEAD COMMIT_JSON | |
| node --input-type=module -e ' | |
| import { readFileSync } from "node:fs"; | |
| import { pathToFileURL } from "node:url"; | |
| const { verifyNpmScanSourceBinding } = await import(pathToFileURL(`${process.env.RUNNER_TEMP}/npm-scan-receipt.mts`).href); | |
| verifyNpmScanSourceBinding({ sourceSha: process.env.SOURCE_SHA, runHead: process.env.RUN_HEAD, parents: JSON.parse(process.env.COMMIT_JSON).parents.map(parent => parent.sha), logs: readFileSync(`${process.env.RUNNER_TEMP}/publish-logs.normalized.txt`, "utf8") }); | |
| ' | |
| rm -f "$RUNNER_TEMP/publish-logs.zip" "$RUNNER_TEMP/publish-logs.txt" "$RUNNER_TEMP/publish-logs.normalized.txt" | |
| - name: Require enterprise scan credential | |
| shell: bash | |
| env: | |
| SOCKET_API_TOKEN_FOR_CLI_AND_SFW: ${{ secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW }} | |
| run: | | |
| if [ -z "$SOCKET_API_TOKEN_FOR_CLI_AND_SFW" ]; then | |
| echo "::error::Required secret SOCKET_API_TOKEN_FOR_CLI_AND_SFW is unavailable." >&2 | |
| exit 1 | |
| fi | |
| - name: Recover verified scan source rules | |
| shell: bash | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SERVER_URL: ${{ github.server_url }} | |
| SOURCE_SHA: ${{ inputs.scan-source-sha }} | |
| WORKFLOW_SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| AUTH_B64="$(printf 'x-access-token:%s' "${GITHUB_TOKEN}" | base64 | tr -d '\n')" | |
| export GIT_CONFIG_COUNT=1 | |
| export GIT_CONFIG_KEY_0="http.${SERVER_URL}/.extraheader" | |
| export GIT_CONFIG_VALUE_0="AUTHORIZATION: basic ${AUTH_B64}" | |
| git fetch --no-tags --depth=32 origin "$SOURCE_SHA" | |
| git checkout -q --detach "$SOURCE_SHA" | |
| git fetch --no-tags origin "$WORKFLOW_SHA" | |
| BOOTSTRAP_DIR="$(mktemp -d "$GITHUB_WORKSPACE/.release-bootstrap.XXXXXX")" | |
| trap 'rm -rf "$BOOTSTRAP_DIR"' EXIT | |
| BOOTSTRAP="$BOOTSTRAP_DIR/fleet.mjs" | |
| git show "$WORKFLOW_SHA:scripts/repo/bootstrap/fleet.mjs" > "$BOOTSTRAP" | |
| node --input-type=module -e ' | |
| const { pathToFileURL } = await import("node:url"); | |
| const { migrateRuleFile } = await import(pathToFileURL(process.argv[2]).href); | |
| migrateRuleFile(process.argv[3]); | |
| ' scan-rule-recovery "$BOOTSTRAP" "$GITHUB_WORKSPACE" | |
| - name: Select source-pinned node | |
| id: scan-node | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PIN="$(tr -d '[:space:]' < .node-version)" | |
| [[ "$PIN" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] | |
| echo "version=$PIN" >> "$GITHUB_OUTPUT" | |
| - name: Set up and install | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| checkout-ref: ${{ inputs.scan-source-sha }} | |
| node-version: ${{ steps.scan-node.outputs.version }} | |
| payload-token-client-id: ${{ secrets.SOCKET_RELEASE_CLIENT_ID || vars.SOCKET_RELEASE_CLIENT_ID }} | |
| payload-token-private-key: ${{ secrets.SOCKET_RELEASE_APP_PRIVATE_KEY }} | |
| - name: Restore trusted scan controller | |
| shell: bash | |
| run: | | |
| install -m 0600 "$RUNNER_TEMP/npm-scan-staged.mts" scripts/fleet/npm/scan/staged.mts | |
| install -m 0600 "$RUNNER_TEMP/npm-scan-receipt.mts" scripts/fleet/npm/scan/receipt.mts | |
| install -m 0600 "$RUNNER_TEMP/npm-registry-scan-ndjson.mts" scripts/fleet/registry/npm/scan/ndjson.mts | |
| install -m 0600 "$RUNNER_TEMP/npm-registry-scan.mts" scripts/fleet/registry/npm/scan/run.mts | |
| - name: Validate publish task name | |
| env: | |
| PUBLISH_TASK_NAME: ${{ inputs.task-name }} | |
| SCAN_PACKAGE: ${{ inputs.scan-package }} | |
| run: node scripts/fleet/registry/npm/publish-dispatch-context.mts --name "$PUBLISH_TASK_NAME" --package "$SCAN_PACKAGE" | |
| # repo-cutout: scan-build | |
| # <repo> | |
| # </repo> | |
| - name: Scan exact staged bytes | |
| id: scan | |
| env: | |
| GITHUB_TOKEN: ${{ github.token }} | |
| SCAN_PACKAGE: ${{ inputs.scan-package }} | |
| SCAN_PUBLISH_RUN_ID: ${{ inputs.scan-publish-run-id }} | |
| SCAN_SOURCE_SHA: ${{ inputs.scan-source-sha }} | |
| SCAN_STAGE_ID: ${{ inputs.scan-stage-id }} | |
| SCAN_STAGE_SHA1: ${{ inputs.scan-stage-sha1 }} | |
| SCAN_VERSION: ${{ inputs.scan-version }} | |
| SOCKET_API_TOKEN: ${{ secrets.SOCKET_API_TOKEN_FOR_CLI_AND_SFW }} | |
| run: node scripts/fleet/npm/scan/staged.mts --json | |
| - name: Upload scan receipt | |
| if: ${{ always() }} | |
| uses: ./.github/actions/fleet/upload-artifact | |
| with: | |
| name: npm-stage-scan-receipt-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: .cache/fleet/npm-scan-staged/npm-stage-scan-receipt.json | |
| if-no-files-found: error | |
| - name: Require successful scan | |
| if: ${{ always() }} | |
| env: | |
| SCAN_OUTCOME: ${{ steps.scan.outcome }} | |
| run: test "$SCAN_OUTCOME" = success |