Repository navigation
publish: npm (Lib publish) #43
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.task-name }})' | |
| # Cascade-owned — every npm-publishing repo carries the byte-identical copy | |
| # (adopt by copying the template once; the sync then keeps it in lock-step; | |
| # member edits are reverted on the next cascade). The thin dispatch shell: | |
| # checkout → setup-and-install → build → scripts/fleet/npm-publish.mts, which | |
| # owns what + how the repo publishes. | |
| # | |
| # Default flow: manual dispatch, DRY-RUN unless `publish: true`; 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 reserves the version, changelog, tag and configured release before npm | |
| # staging. An unaccepted stage consumes the version; approval only promotes npm. | |
| # | |
| # 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-infra/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 | |
| publish: | |
| description: 'Publish for real (false = dry-run, the default).' | |
| type: boolean | |
| default: false | |
| 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: '' | |
| 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-reserved: | |
| description: >- | |
| Resume an unpublished release from its existing vVERSION tag. | |
| Requires checkout-ref to name that exact tag. | |
| 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 }} | |
| 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_RESERVED: ${{ inputs.resume-reserved }} | |
| 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_RESERVED" = "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`); | |
| NODE | |
| coverage-shards: | |
| needs: [coverage-plan] | |
| if: ${{ inputs.scan-stage-id == '' && inputs.backfill-version == '' && needs.coverage-plan.result == 'success' && 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_RESERVED" = "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: Restore reserved coverage tooling | |
| if: ${{ inputs.resume-reserved == 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 | |
| run: pnpm run build | |
| - 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 }} | |
| 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-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_RESERVED: ${{ inputs.resume-reserved }} | |
| 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_RESERVED" = "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: 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-reserved == 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 | |
| run: pnpm run build | |
| - name: Aggregate coverage | |
| if: ${{ needs.coverage-plan.outputs.runner == 'vitest' }} | |
| env: | |
| COVERAGE_SHARD_COUNT: ${{ needs.coverage-shards.outputs.shard-count }} | |
| 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_RESERVED: ${{ inputs.resume-reserved }} | |
| 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_RESERVED" = "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_RESERVED: ${{ inputs.resume-reserved }} | |
| 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_RESERVED" != "true" ] || [ -n "$BACKFILL_VERSION" ] || [[ ! "$CHECKOUT_REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$ ]]; then | |
| echo "trusted=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| git fetch --no-tags origin "refs/heads/$DEFAULT_BRANCH:refs/remotes/origin/$DEFAULT_BRANCH" | |
| TAG_SHA="$(git rev-parse "refs/tags/$CHECKOUT_REF^{commit}")" | |
| HEAD_SHA="$(git rev-parse HEAD)" | |
| if [ "$TAG_SHA" != "$HEAD_SHA" ] || ! git merge-base --is-ancestor "$TAG_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.publish == true && 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-infra/npm/publish-dispatch-context.mts --name "$PUBLISH_TASK_NAME" | |
| - name: Validate reserved release resume | |
| if: ${{ inputs.resume-reserved == true }} | |
| env: | |
| BACKFILL_VERSION: ${{ inputs.backfill-version }} | |
| CHECKOUT_REF: ${{ inputs.checkout-ref }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="$(node -p 'require("./package.json").version')" | |
| if [ -n "$BACKFILL_VERSION" ] || [ "$CHECKOUT_REF" != "v$VERSION" ]; then | |
| echo "::error::Reserved release resume requires checkout-ref v$VERSION without backfill-version." >&2 | |
| exit 1 | |
| fi | |
| HEAD_SHA="$(git rev-parse HEAD)" | |
| TAG_SHA="$(git rev-parse "$CHECKOUT_REF^{commit}")" | |
| if [ "$HEAD_SHA" != "$TAG_SHA" ]; then | |
| echo "::error::Reserved release resume checked out $HEAD_SHA, but $CHECKOUT_REF resolves to $TAG_SHA." >&2 | |
| exit 1 | |
| fi | |
| echo "[reserved-source] $HEAD_SHA" | |
| - name: Build | |
| run: pnpm run build | |
| - 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-reserved != true }} | |
| uses: ./.github/actions/fleet/setup-odai | |
| with: | |
| allow-fill: 'true' | |
| require-ready: 'true' | |
| - 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-reserved != true && inputs.checkout-ref || '' }} | |
| DIST_TAG: ${{ inputs.dist-tag }} | |
| PUBLISH: ${{ inputs.publish }} | |
| RELEASE_AS: ${{ inputs.release-as }} | |
| RESUME_RESERVED: ${{ inputs.resume-reserved }} | |
| 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 [ "$RESUME_RESERVED" = true ]; then args+=(--resume-reserved); fi | |
| if [ "$PUBLISH" != true ]; then args+=(--dry-run); fi | |
| node scripts/fleet/npm-publish.mts "${args[@]}" | |
| scan-staged-package: | |
| if: ${{ inputs.scan-stage-id != '' }} | |
| name: Scan staged npm package | |
| runs-on: ubuntu-26.04 | |
| timeout-minutes: 60 | |
| permissions: | |
| actions: read | |
| contents: read | |
| 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-ci.mts "$RUNNER_TEMP/npm-scan-ci.mts" | |
| install -m 0600 scripts/fleet/npm/scan-receipt.mts "$RUNNER_TEMP/npm-scan-receipt.mts" | |
| install -m 0600 scripts/fleet/registry-infra/npm/scan-ndjson.mts "$RUNNER_TEMP/npm-registry-scan-ndjson.mts" | |
| install -m 0600 scripts/fleet/registry-infra/npm/scan.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: Set up and install | |
| uses: ./.github/actions/fleet/setup-and-install | |
| with: | |
| checkout-ref: ${{ inputs.scan-source-sha }} | |
| 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-ci.mts" scripts/fleet/npm/scan-ci.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-infra/npm/scan-ndjson.mts | |
| install -m 0600 "$RUNNER_TEMP/npm-registry-scan.mts" scripts/fleet/registry-infra/npm/scan.mts | |
| - name: Validate publish task name | |
| env: | |
| PUBLISH_TASK_NAME: ${{ inputs.task-name }} | |
| SCAN_PACKAGE: ${{ inputs.scan-package }} | |
| run: node scripts/fleet/registry-infra/npm/publish-dispatch-context.mts --name "$PUBLISH_TASK_NAME" --package "$SCAN_PACKAGE" | |
| - name: Build exact release source | |
| run: pnpm run build | |
| - name: Restore exact native artifacts | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| NATIVE_PUBLISH_RUN_ID: ${{ inputs.scan-publish-run-id }} | |
| run: node scripts/fleet/npm/native-artifacts.mts | |
| - 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-ci.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-ci/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 |