Release / publish-dry-run / f0rr0/wasix-ts-napi-release-probe #135
Workflow file for this run
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: Release | |
| run-name: Release / ${{ inputs.operation }} / ${{ github.ref_name }} | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| operation: | |
| description: Prepare, dry-run, publish with trusted publishers, or bootstrap first registry versions | |
| required: true | |
| type: choice | |
| default: prepare-release-pr | |
| options: | |
| - prepare-release-pr | |
| - publish-dry-run | |
| - publish | |
| - publish-bootstrap | |
| release_commit: | |
| description: Optional full commit SHA assertion; when set it must equal the workflow commit | |
| required: false | |
| type: string | |
| default: "" | |
| continuation_pointer: | |
| description: Internal exact-parent continuation pointer; leave empty for a maintainer dispatch | |
| required: false | |
| type: string | |
| default: "" | |
| permissions: | |
| contents: read | |
| env: | |
| CARGO_TERM_COLOR: always | |
| RUST_BACKTRACE: 1 | |
| CANONICAL_RELEASE_REPOSITORY: f0rr0/oliphaunt | |
| NODE_VERSION: 22.22.3 | |
| NPM_VERSION: 11.18.0 | |
| PNPM_VERSION: 11.5.0 | |
| BUN_VERSION: 1.3.14 | |
| PUBLICATION_LOCK_PATH: target/release/publication-lock.json | |
| BOOTSTRAP_LEDGER_PATH: target/release/bootstrap-ledger | |
| # Normal publication and bootstrap each run within one hosted-runner window. | |
| # Normal publication keeps enough time after registry writes for public | |
| # consumer verification and GitHub draft promotion. | |
| RELEASE_JOB_HARD_WINDOW_SECONDS: 21180 | |
| BOOTSTRAP_REGISTRY_MUTATION_WINDOW_SECONDS: 19800 | |
| POST_REGISTRY_RESERVE_SECONDS: 3240 | |
| # Public consumer lanes run concurrently under one 13-minute internal | |
| # deadline and stop with ten minutes preserved for evidence, lock proof, and | |
| # draft promotion. The enclosing workflow step has a 15-minute hard bound. | |
| PUBLIC_CONSUMER_SMOKE_TIMEOUT_SECONDS: 780 | |
| PUBLIC_CONSUMER_FINALIZATION_RESERVE_SECONDS: 600 | |
| MAVEN_CENTRAL_NAMESPACE: dev.oliphaunt | |
| concurrency: | |
| # Every registry-writing or release-branch-writing operation shares one | |
| # non-cancelling lock. GitHub permits one pending run while the active run | |
| # completes, so maintainers must not stack mutation dispatches. Dry-runs are | |
| # read-only and may run once per exact SHA. | |
| group: release-${{ inputs.operation == 'publish-dry-run' && github.sha || 'mutation' }} | |
| cancel-in-progress: false | |
| defaults: | |
| run: | |
| shell: bash | |
| jobs: | |
| validate-inputs: | |
| name: Validate release inputs | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Require canonical release repository | |
| run: | | |
| if [[ "${GITHUB_REPOSITORY}" != "${CANONICAL_RELEASE_REPOSITORY}" ]]; then | |
| echo "Release workflow is pinned to ${CANONICAL_RELEASE_REPOSITORY}; got ${GITHUB_REPOSITORY}" >&2 | |
| exit 1 | |
| fi | |
| - name: Checkout exact workflow commit | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| ref: ${{ github.sha }} | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Validate release workflow inputs | |
| id: validate_release_inputs | |
| env: | |
| RELEASE_OPERATION: ${{ inputs.operation }} | |
| RELEASE_COMMIT: ${{ inputs.release_commit }} | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| run: bash .github/scripts/validate-release-workflow-inputs.sh | |
| - name: Set up pinned Node.js | |
| id: setup_release_validation_node | |
| if: ${{ inputs.operation == 'prepare-release-pr' }} | |
| timeout-minutes: 3 | |
| uses: ./.github/actions/setup-node-runtime | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Require clear Release Please lifecycle | |
| id: require_release_please_lifecycle | |
| if: ${{ inputs.operation == 'prepare-release-pr' }} | |
| timeout-minutes: 1 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: node tools/release/release-please-pr-lifecycle.mjs assert-clean --base main | |
| prepare-release-pr: | |
| name: Prepare release PR | |
| needs: validate-inputs | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 20 | |
| if: ${{ inputs.operation == 'prepare-release-pr' }} | |
| environment: release-pr | |
| permissions: | |
| contents: write | |
| issues: write | |
| pull-requests: write | |
| steps: | |
| - name: Require main | |
| run: | | |
| if [[ "${GITHUB_REF}" != "refs/heads/main" ]]; then | |
| echo "Releases must be run from main; got ${GITHUB_REF}" >&2 | |
| exit 1 | |
| fi | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Require current main | |
| id: require_current_main | |
| timeout-minutes: 1 | |
| run: bash .github/scripts/require-current-main.sh "$GITHUB_SHA" | |
| - name: Require release PR token | |
| env: | |
| RELEASE_PR_TOKEN: ${{ secrets.RELEASE_PR_TOKEN }} | |
| run: | | |
| if [[ -z "${RELEASE_PR_TOKEN}" ]]; then | |
| echo "RELEASE_PR_TOKEN is required so generated release PRs trigger normal PR CI." >&2 | |
| echo "Configure a GitHub App or maintainer bot token in the release-pr environment." >&2 | |
| exit 1 | |
| fi | |
| - name: Set up Moon | |
| uses: ./.github/actions/setup-moon | |
| with: | |
| install-workspace: "true" | |
| - name: Set up Rust | |
| uses: ./.github/actions/setup-rust | |
| - name: Validate release metadata | |
| run: | | |
| tools/dev/bun.sh tools/release/release-check.mjs | |
| - name: Create or update release-please PR | |
| id: release_please | |
| uses: googleapis/release-please-action@5c625bfb5d1ff62eadeeb3772007f7f66fdcf071 | |
| with: | |
| token: ${{ secrets.RELEASE_PR_TOKEN }} | |
| target-branch: main | |
| config-file: release-please-config.json | |
| manifest-file: .release-please-manifest.json | |
| skip-github-release: true | |
| - name: Sync derived release PR files | |
| id: sync_release_pr | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PR_TOKEN }} | |
| RELEASE_PLEASE_PR: ${{ steps.release_please.outputs.pr }} | |
| RELEASE_PLEASE_PRS: ${{ steps.release_please.outputs.prs }} | |
| RELEASE_PLEASE_PRS_CREATED: ${{ steps.release_please.outputs.prs_created }} | |
| run: | | |
| set -euo pipefail | |
| release_pr_number="$(bun .github/scripts/resolve-release-please-pr.mjs)" | |
| if [[ -z "${release_pr_number}" ]]; then | |
| release_pr_number="$( | |
| gh pr list \ | |
| --base main \ | |
| --head release-please--branches--main \ | |
| --state open \ | |
| --json number \ | |
| --jq '.[0].number // empty' | |
| )" | |
| fi | |
| if [[ -z "${release_pr_number}" ]]; then | |
| if [[ "${RELEASE_PLEASE_PRS_CREATED}" == "true" ]]; then | |
| echo "release-please reported PR changes, but no release PR number could be resolved." >&2 | |
| exit 1 | |
| fi | |
| shared_contrib_status="$( | |
| tools/dev/bun.sh tools/release/sync-release-pr.mjs --shared-contrib-status | |
| )" | |
| if [[ "${shared_contrib_status}" != "required=true" ]]; then | |
| echo "No open release-please PR or unreleased shared contrib source change found." | |
| exit 0 | |
| fi | |
| release_pr_branch="release-please--branches--main" | |
| release_pr_title="chore(release): prepare main releases" | |
| gh auth setup-git | |
| bash .github/scripts/require-current-main.sh "$GITHUB_SHA" | |
| git switch -c "${release_pr_branch}" | |
| tools/dev/bun.sh tools/release/sync-release-pr.mjs --bootstrap-shared-contrib | |
| if [[ -n "$(git ls-files --others --exclude-standard)" ]]; then | |
| echo "Shared contrib release bootstrap created untracked files." >&2 | |
| git status --short >&2 | |
| exit 1 | |
| fi | |
| git config user.name "oliphaunt-release-bot" | |
| git config user.email "oliphaunt-release-bot@users.noreply.github.com" | |
| git add -u | |
| if git diff --cached --quiet --exit-code; then | |
| echo "Shared contrib release bootstrap produced no release candidate." >&2 | |
| exit 1 | |
| fi | |
| git commit -m "${release_pr_title}" | |
| tools/dev/bun.sh tools/release/sync-release-pr.mjs --check | |
| git push --set-upstream origin "${release_pr_branch}" | |
| gh pr create \ | |
| --base main \ | |
| --head "${release_pr_branch}" \ | |
| --title "${release_pr_title}" \ | |
| --label "autorelease: pending" \ | |
| --body "Runtime release candidates derived from shared PostgreSQL contrib carrier source changes." | |
| release_pr_number="$( | |
| gh pr view "${release_pr_branch}" --json number --jq '.number' | |
| )" | |
| echo "shared_contrib_pr_created=true" >> "$GITHUB_OUTPUT" | |
| fi | |
| IFS=$'\t' read -r \ | |
| release_pr_observed_number \ | |
| release_pr_base \ | |
| release_pr_head \ | |
| release_pr_old_sha \ | |
| release_pr_head_repository \ | |
| release_pr_is_cross_repository \ | |
| release_pr_state \ | |
| release_pr_title \ | |
| <<<"$( | |
| gh pr view "${release_pr_number}" \ | |
| --json number,baseRefName,headRefName,headRefOid,headRepository,isCrossRepository,state,title \ | |
| --jq '[.number, .baseRefName, .headRefName, .headRefOid, .headRepository.nameWithOwner, (.isCrossRepository | tostring), .state, .title] | @tsv' | |
| )" | |
| gh auth setup-git | |
| bash .github/scripts/require-current-main.sh "$GITHUB_SHA" | |
| release_pr_identity_args=( | |
| --pr-number "${release_pr_number}" | |
| --observed-pr-number "${release_pr_observed_number}" | |
| --base "${release_pr_base}" | |
| --head "${release_pr_head}" | |
| --head-sha "${release_pr_old_sha}" | |
| --head-repository "${release_pr_head_repository}" | |
| --cross-repository "${release_pr_is_cross_repository}" | |
| --state "${release_pr_state}" | |
| --title "${release_pr_title}" | |
| --main-sha "$GITHUB_SHA" | |
| ) | |
| bun .github/scripts/normalize-release-please-pr.mjs \ | |
| normalize \ | |
| "${release_pr_identity_args[@]}" | |
| tools/dev/bun.sh tools/release/sync-release-pr.mjs | |
| tools/dev/bun.sh tools/release/sync-release-pr.mjs --check | |
| if [[ -n "$(git ls-files --others --exclude-standard)" ]]; then | |
| echo "Derived release synchronization created untracked files; refusing an incomplete amend." >&2 | |
| git status --short >&2 | |
| exit 1 | |
| fi | |
| git config user.name "oliphaunt-release-bot" | |
| git config user.email "oliphaunt-release-bot@users.noreply.github.com" | |
| if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then | |
| git add -u | |
| git commit --amend --no-edit | |
| else | |
| echo "Derived release files already match the normalized Release Please tree." | |
| fi | |
| release_products_json="$( | |
| tools/dev/bun.sh tools/release/verify-release-commit.mjs \ | |
| --derive-products \ | |
| --head-ref HEAD | |
| )" | |
| tools/dev/bun.sh tools/release/verify-release-commit.mjs \ | |
| --products-json "${release_products_json}" \ | |
| --head-ref HEAD | |
| tools/dev/bun.sh tools/release/release-metadata-check.mjs | |
| bun .github/scripts/normalize-release-please-pr.mjs \ | |
| push \ | |
| "${release_pr_identity_args[@]}" | |
| - name: Report release-please PR result | |
| env: | |
| RELEASE_PLEASE_PRS_CREATED: ${{ steps.release_please.outputs.prs_created }} | |
| SHARED_CONTRIB_PR_CREATED: ${{ steps.sync_release_pr.outputs.shared_contrib_pr_created }} | |
| run: | | |
| if [[ "${RELEASE_PLEASE_PRS_CREATED}" == "true" || "${SHARED_CONTRIB_PR_CREATED}" == "true" ]]; then | |
| echo "A release PR was created or updated." | |
| else | |
| echo "release-please found no releasable changes." | |
| fi | |
| publish-dry-run: | |
| name: Prepare release dry run | |
| needs: | |
| - validate-inputs | |
| runs-on: macos-26 | |
| timeout-minutes: 360 | |
| if: ${{ inputs.operation == 'publish-dry-run' }} | |
| environment: release-dry-run | |
| permissions: | |
| actions: read | |
| contents: read | |
| steps: &release_candidate_steps | |
| - name: Record release deadline | |
| id: release_job_deadline | |
| if: ${{ inputs.operation == 'publish' }} | |
| run: | | |
| if [[ ! "$RELEASE_JOB_HARD_WINDOW_SECONDS" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'RELEASE_JOB_HARD_WINDOW_SECONDS must be a positive integer' >&2 | |
| exit 1 | |
| fi | |
| hard_deadline=$(( $(date +%s) + RELEASE_JOB_HARD_WINDOW_SECONDS )) | |
| { | |
| echo "RELEASE_JOB_HARD_DEADLINE_EPOCH=$hard_deadline" | |
| echo "OLIPHAUNT_GITHUB_CONTENT_WRITE_COLD_START_EPOCH=$(date +%s)" | |
| echo "OLIPHAUNT_GITHUB_CONTENT_WRITE_PACER_PATH=$RUNNER_TEMP/oliphaunt-github-content-write-pacer.json" | |
| echo "OLIPHAUNT_GITHUB_CORE_REQUEST_JOURNAL_PATH=$RUNNER_TEMP/oliphaunt-github-core-request-journal.json" | |
| echo "OLIPHAUNT_REQUIRE_GITHUB_CORE_REQUEST_JOURNAL=true" | |
| echo "OLIPHAUNT_GITHUB_RUN_SNAPSHOT_DIR=$RUNNER_TEMP/oliphaunt-github-run-snapshots" | |
| } >> "$GITHUB_ENV" | |
| echo "The release must finish before Unix time $hard_deadline." | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Resolve release commit | |
| id: release_head | |
| timeout-minutes: 1 | |
| env: | |
| INPUT_RELEASE_COMMIT: ${{ inputs.release_commit }} | |
| run: .github/scripts/resolve-release-head.sh | |
| - name: Set up pinned Node.js | |
| id: setup_github_stage_node | |
| timeout-minutes: 3 | |
| uses: ./.github/actions/setup-node-runtime | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Set up Moon | |
| uses: ./.github/actions/setup-moon | |
| with: | |
| install-workspace: "true" | |
| - name: Set up Rust | |
| uses: ./.github/actions/setup-rust | |
| - name: Configure macOS release toolchains | |
| id: configure_release_candidate_toolchains | |
| run: bash .github/scripts/configure-macos-release-toolchains.sh --android | |
| - name: Validate release metadata | |
| run: | | |
| tools/dev/bun.sh tools/release/release-check.mjs | |
| - name: Plan product releases | |
| id: release_plan | |
| run: | | |
| release_plan_args=( | |
| --from-product-tags | |
| --include-current-tags | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| --format github-output | |
| ) | |
| tools/dev/bun.sh tools/release/release_plan.mjs "${release_plan_args[@]}" >> "$GITHUB_OUTPUT" | |
| - name: No package release planned | |
| if: ${{ steps.release_plan.outputs.has_release_changes != 'true' }} | |
| run: echo "No release-affecting product changes were found since the last product tag." | |
| - name: Resolve selected registry authentication needs | |
| id: registry_needs | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: bun .github/scripts/selected-registry-needs.mjs | |
| - name: Verify direct-workflow OIDC identity | |
| id: verify_oidc_identity | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| RELEASE_OPERATION: ${{ inputs.operation }} | |
| run: bun .github/scripts/verify-github-oidc-identity.mjs | |
| - name: Prove workflow HEAD is the release commit | |
| id: verify_publication_candidate | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 2 | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/verify-publication-candidate.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --github-output "$GITHUB_OUTPUT" | |
| - name: Prove Release Please PR can complete after publication | |
| id: assert_release_please_markable | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 1 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| tools/dev/bun.sh tools/release/release-please-pr-lifecycle.mjs \ | |
| assert-markable \ | |
| --release-sha "${{ steps.verify_publication_candidate.outputs.release_sha }}" \ | |
| --base main | |
| - name: Preflight selected product tag and release collisions | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| gh auth setup-git | |
| tools/dev/bun.sh tools/release/verify_product_tags.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --target "$RELEASE_HEAD_SHA" \ | |
| --allow-missing | |
| bun .github/scripts/manage-release-drafts.mjs preflight \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Check publish environment | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} | |
| ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_GPG_KEY_ID }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| run: tools/release/check_publish_environment.mjs --products-json "${PRODUCTS_JSON}" | |
| - name: Verify external registry ownership and trust links | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} | |
| ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} | |
| run: bun .github/scripts/verify-external-publish-readiness.mjs | |
| - name: Import, sign, and verify Maven credentials before mutation | |
| id: verify_maven_signing | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.registry_needs.outputs.needs_maven == 'true' }} | |
| timeout-minutes: 2 | |
| env: | |
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_GPG_KEY_ID }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| run: tools/dev/bun.sh tools/release/verify-maven-signing-readiness.mjs | |
| - name: Require qualified release-commit CI run | |
| id: ci_qualification | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_OPERATION: ${{ inputs.operation }} | |
| REQUIRES_WASIX_EVIDENCE: ${{ steps.release_plan.outputs.requires_wasix_release_regression_evidence }} | |
| run: | | |
| qualification_timeout=7200 | |
| if [[ "$RELEASE_OPERATION" != publish-dry-run ]]; then | |
| qualification_timeout=0 | |
| fi | |
| qualification_args=( | |
| CI | |
| "$RELEASE_HEAD_SHA" | |
| "$qualification_timeout" | |
| --event push | |
| --event workflow_dispatch | |
| --job Builds | |
| --job Required | |
| --job Qualified | |
| --artifact artifact-build-plan | |
| --artifact oliphaunt-release-candidate | |
| ) | |
| if [[ "$REQUIRES_WASIX_EVIDENCE" == true ]]; then | |
| qualification_args+=(--artifact wasix-release-regression-evidence) | |
| fi | |
| if [[ "${{ steps.release_plan.outputs.has_extension_artifacts }}" == true ]]; then | |
| qualification_args+=(--artifact oliphaunt-extension-package-artifacts) | |
| fi | |
| bash .github/scripts/require-workflow-success.sh "${qualification_args[@]}" | |
| - name: Download exact-SHA qualification record | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| target/release-candidate \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Qualified \ | |
| --artifact oliphaunt-release-candidate | |
| - name: Download exact-SHA affected plan | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| target/release-candidate/affected-plan \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Plan \ | |
| --artifact artifact-build-plan | |
| - name: Download required exact-SHA WASIX evidence | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && steps.release_plan.outputs.requires_wasix_release_regression_evidence == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| target/release-candidate/wasix-evidence \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job "E2E / WASIX release regression" \ | |
| --artifact wasix-release-regression-evidence | |
| - name: Verify exact-SHA qualification record | |
| id: verify_qualification | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| WASIX_EVIDENCE_REQUIRED: ${{ steps.release_plan.outputs.requires_wasix_release_regression_evidence }} | |
| run: | | |
| node .github/scripts/verify-release-candidate.mjs \ | |
| target/release-candidate/oliphaunt-release-candidate.json \ | |
| --plan target/release-candidate/affected-plan/ci-plan.json \ | |
| --qualification-mode full-payload \ | |
| --wasix-evidence-required "$WASIX_EVIDENCE_REQUIRED" \ | |
| --wasix-evidence-root target/release-candidate/wasix-evidence | |
| - name: Require one approved dry-run lock and capsule | |
| id: approved_publication_lock | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| approved_artifacts=( | |
| --artifact oliphaunt-publication-lock | |
| --artifact oliphaunt-bootstrap-capsule | |
| ) | |
| gate_output="$RUNNER_TEMP/approved-publication-inputs-gate.out" | |
| GITHUB_OUTPUT="$gate_output" \ | |
| bash .github/scripts/require-workflow-success.sh \ | |
| Release \ | |
| "$RELEASE_HEAD_SHA" \ | |
| 300 \ | |
| --event workflow_dispatch \ | |
| "${approved_artifacts[@]}" | |
| cat "$gate_output" >> "$GITHUB_OUTPUT" | |
| - name: Download prior approved publication lock | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_LOCK_RUN_ID: ${{ steps.approved_publication_lock.outputs.run_id }} | |
| APPROVED_ARTIFACT_METADATA_JSON: ${{ steps.approved_publication_lock.outputs.artifact_metadata_json }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| Release \ | |
| "$RELEASE_HEAD_SHA" \ | |
| target/approved-publication-lock \ | |
| --run-id "$RELEASE_LOCK_RUN_ID" \ | |
| --artifact-metadata-json "$APPROVED_ARTIFACT_METADATA_JSON" \ | |
| --artifact oliphaunt-publication-lock | |
| - name: Validate product versions and registry state | |
| id: validate_release_registry_state | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/release-check-registries.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Download WASIX runtime build artifacts | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: bun .github/scripts/download-wasix-runtime-build-artifacts.mjs | |
| - name: Download WASIX release assets | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/oliphaunt-wasix/release-assets \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| --artifact liboliphaunt-wasix-release-assets | |
| - name: Download WASIX postmaster release assets | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix-postmaster') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/oliphaunt-wasix-postmaster/release-assets \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| --artifact liboliphaunt-wasix-postmaster-release-assets | |
| - name: Download exact-extension package artifacts | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && steps.release_plan.outputs.has_extension_artifacts == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/extension-artifacts \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| --artifact oliphaunt-extension-package-artifacts | |
| - name: Download SDK package artifacts | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| download_sdk_artifact() { | |
| local product="$1" | |
| local artifact_args=() | |
| while IFS= read -r artifact; do | |
| artifact_args+=(--artifact "$artifact") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs ci-artifact-names --product "$product" --family sdk-package --format lines) | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| "target/sdk-artifacts/$product" \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| "${artifact_args[@]}" | |
| } | |
| while IFS= read -r product; do | |
| download_sdk_artifact "$product" | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs ci-products --family sdk-package --products-json "$PRODUCTS_JSON" --format lines) | |
| - name: Download liboliphaunt release assets | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-native') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/liboliphaunt/release-assets \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| --artifact liboliphaunt-native-release-assets | |
| - name: Prove native and WASIX ICU data identity | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-native') && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/check-cross-family-icu-data.mjs \ | |
| target/liboliphaunt/release-assets \ | |
| target/oliphaunt-wasix/release-assets | |
| - name: Set up Bun for TypeScript npm consumer checks | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-js') }} | |
| uses: ./.github/actions/setup-bun | |
| with: | |
| bun-version: ${{ env.BUN_VERSION }} | |
| - name: Install TypeScript release tooling | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-js') }} | |
| run: pnpm install --frozen-lockfile | |
| - name: Download native helper release assets | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && (contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-broker') || contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-node-direct') || contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-wasix-napi')) }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| PRODUCT_OLIPHAUNT_BROKER: ${{ contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-broker') }} | |
| PRODUCT_OLIPHAUNT_NODE_DIRECT: ${{ contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-node-direct') }} | |
| PRODUCT_OLIPHAUNT_WASIX_NAPI: ${{ contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-wasix-napi') }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| download_helper_artifacts() { | |
| local product="$1" | |
| local kind="$2" | |
| local destination="$3" | |
| local artifact_args=() | |
| while IFS= read -r artifact; do | |
| artifact_args+=(--artifact "$artifact") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs ci-artifact-names --product "$product" --kind "$kind" --family release-assets --format lines) | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| "$destination" \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| "${artifact_args[@]}" | |
| } | |
| if [ "$PRODUCT_OLIPHAUNT_BROKER" = "true" ]; then | |
| download_helper_artifacts \ | |
| oliphaunt-broker \ | |
| broker-helper \ | |
| target/oliphaunt-broker/release-assets | |
| fi | |
| if [ "$PRODUCT_OLIPHAUNT_NODE_DIRECT" = "true" ]; then | |
| download_helper_artifacts \ | |
| oliphaunt-node-direct \ | |
| node-direct-addon \ | |
| target/oliphaunt-node-direct/release-assets | |
| fi | |
| if [ "$PRODUCT_OLIPHAUNT_WASIX_NAPI" = "true" ]; then | |
| download_helper_artifacts \ | |
| oliphaunt-wasix-napi \ | |
| wasix-napi-addon \ | |
| target/oliphaunt-wasix-napi/release-assets | |
| fi | |
| - name: Download Node direct optional npm packages | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-node-direct') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| artifact_args=() | |
| while IFS= read -r artifact; do | |
| artifact_args+=(--artifact "$artifact") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs ci-artifact-names --product oliphaunt-node-direct --kind node-direct-addon --family npm-package --format lines) | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/oliphaunt-node-direct/npm-packages \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| "${artifact_args[@]}" | |
| - name: Download WASIX Node-API optional npm packages | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-wasix-napi') }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| RELEASE_ARTIFACT_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: | | |
| artifact_args=() | |
| while IFS= read -r artifact; do | |
| artifact_args+=(--artifact "$artifact") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs ci-artifact-names --product oliphaunt-wasix-napi --kind wasix-napi-addon --family npm-package --format lines) | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_ARTIFACT_SHA" \ | |
| target/oliphaunt-wasix-napi/npm-packages \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Builds \ | |
| "${artifact_args[@]}" | |
| - name: Freeze canonical Apple extension carrier input | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && (contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-swift') || contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-react-native')) }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| includes_swift="$(bun -e 'console.log(JSON.parse(process.argv[1]).includes("oliphaunt-swift"))' "$PRODUCTS_JSON")" | |
| includes_react_native="$(bun -e 'console.log(JSON.parse(process.argv[1]).includes("oliphaunt-react-native"))' "$PRODUCTS_JSON")" | |
| swift_source_carrier=target/sdk-artifacts/oliphaunt-swift/release-tree/src/sdks/swift/Carriers/oliphaunt-react-native-ios-carriers.json | |
| react_native_source_carrier=target/sdk-artifacts/oliphaunt-react-native/ios-carriers/oliphaunt-react-native-ios-carriers.json | |
| extension_manifest_args=() | |
| extension_carrier_args=() | |
| while IFS= read -r product_root; do | |
| product="$(basename "$product_root")" | |
| manifest="$product_root/extension-artifacts.json" | |
| if [[ ! -f "$manifest" ]]; then | |
| echo "Selected extension product $product is missing $manifest" >&2 | |
| exit 1 | |
| fi | |
| extension_manifest_args+=(--extension-manifest "$manifest") | |
| product_carriers=() | |
| while IFS= read -r carrier; do | |
| product_carriers+=("$carrier") | |
| done < <(find "$product_root/release-assets" -maxdepth 1 -type f -name '*-swift-extension-carrier.json' | LC_ALL=C sort) | |
| if (( ${#product_carriers[@]} != 1 )); then | |
| echo "Selected extension product $product must provide exactly one independent Swift carrier; found ${#product_carriers[@]}" >&2 | |
| exit 1 | |
| fi | |
| extension_carrier_args+=(--extension-carrier "${product_carriers[0]}") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs \ | |
| ci-products \ | |
| --family extension-artifacts \ | |
| --carrier-family native \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --field artifact-root \ | |
| --format lines) | |
| rm -rf target/release/ios-carriers target/release-work/ios-carriers | |
| if [[ "$includes_swift" == true && "$includes_react_native" == true ]] && ! cmp -s "$swift_source_carrier" "$react_native_source_carrier"; then | |
| echo 'Swift and React Native source carriers disagree for the same release plan.' >&2 | |
| diff --unified "$swift_source_carrier" "$react_native_source_carrier" || true | |
| exit 1 | |
| fi | |
| if [[ "$includes_react_native" == true ]]; then | |
| mkdir -p target/release/ios-carriers | |
| public_args=( | |
| --base-carrier "$react_native_source_carrier" | |
| "${extension_manifest_args[@]}" | |
| --output target/release/ios-carriers/oliphaunt-react-native-ios-carriers.json | |
| ) | |
| tools/dev/bun.sh tools/release/ios-carrier-manifest.mjs "${public_args[@]}" | |
| fi | |
| if [[ "$includes_swift" == true && ${#extension_manifest_args[@]} -gt 0 ]]; then | |
| mkdir -p target/release-work/ios-carriers | |
| local_aggregate_carrier=target/release-work/ios-carriers/oliphaunt-react-native-ios-carriers.json | |
| local_args=( | |
| --base-carrier "$swift_source_carrier" | |
| "${extension_manifest_args[@]}" | |
| --output "$local_aggregate_carrier" | |
| --local-urls | |
| ) | |
| tools/dev/bun.sh tools/release/ios-carrier-manifest.mjs "${local_args[@]}" | |
| extensions_csv="$(bun -e ' | |
| const manifest = JSON.parse(await Bun.file(process.argv[1]).text()); | |
| console.log(manifest.extensions.map((row) => row.sqlName).sort().join(",")); | |
| ' "$local_aggregate_carrier")" | |
| if [[ -z "$extensions_csv" || ${#extension_carrier_args[@]} == 0 ]]; then | |
| echo 'Swift extension validation requires selected extension carrier assets.' >&2 | |
| exit 1 | |
| fi | |
| swift_version="$(tools/dev/bun.sh tools/release/product-version.mjs version oliphaunt-swift)" | |
| cache=target/release-work/swiftpm-extension-cache | |
| node src/sdks/swift/tools/render-extension-products.mjs \ | |
| --carrier "$local_aggregate_carrier" \ | |
| --extensions "$extensions_csv" \ | |
| --cache-dir "$cache" \ | |
| --allow-file-urls \ | |
| --base-package-version "$swift_version" \ | |
| --output-dir target/release-work/swiftpm-extension-cache-warm | |
| node src/sdks/swift/tools/render-extension-products.mjs \ | |
| --carrier "$swift_source_carrier" \ | |
| "${extension_carrier_args[@]}" \ | |
| --extensions "$extensions_csv" \ | |
| --cache-dir "$cache" \ | |
| --offline \ | |
| --base-package-version "$swift_version" \ | |
| --output-dir target/release/swiftpm-extension-consumer-fixture | |
| fi | |
| - name: Set up pinned npm publisher | |
| id: setup_github_stage_npm | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && steps.registry_needs.outputs.needs_npm == 'true' }} | |
| timeout-minutes: 3 | |
| uses: ./.github/actions/setup-npm-publisher | |
| with: | |
| npm-version: ${{ env.NPM_VERSION }} | |
| - name: Validate selected release | |
| id: validate_release | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| WASIX_EVIDENCE_REQUIRED: ${{ steps.release_plan.outputs.requires_wasix_release_regression_evidence }} | |
| run: tools/dev/bun.sh tools/release/release-publish.mjs publish-dry-run --qualified-ci --products-json "${PRODUCTS_JSON}" --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Package public release carriers | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| OLIPHAUNT_BROKER_RELEASE_ASSET_INPUT_DIRS: ${{ github.workspace }}/target/oliphaunt-broker/release-assets | |
| OLIPHAUNT_NODE_ADDON_ASSET_INPUT_DIRS: ${{ github.workspace }}/target/oliphaunt-node-direct/release-assets | |
| OLIPHAUNT_WASIX_NAPI_ASSET_INPUT_DIRS: ${{ github.workspace }}/target/oliphaunt-wasix-napi/release-assets | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: tools/dev/bun.sh tools/release/package-release-carriers.mjs --products-json "$PRODUCTS_JSON" | |
| - name: Freeze exhaustive publication lock | |
| id: freeze_publication_lock | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| lock_args=( | |
| create | |
| --products-json "$PRODUCTS_JSON" | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| --output target/release/publication-lock.json | |
| ) | |
| for artifact_root in \ | |
| target/release \ | |
| target/sdk-artifacts \ | |
| target/liboliphaunt/release-assets \ | |
| target/liboliphaunt/cargo-artifacts \ | |
| target/oliphaunt-wasix/release-assets \ | |
| target/oliphaunt-wasix-postmaster/release-assets \ | |
| target/oliphaunt-broker/release-assets \ | |
| target/oliphaunt-broker/cargo-artifacts \ | |
| target/oliphaunt-wasix/cargo-artifacts \ | |
| target/oliphaunt-node-direct/release-assets \ | |
| target/oliphaunt-node-direct/npm-packages \ | |
| target/oliphaunt-wasix-napi/release-assets \ | |
| target/oliphaunt-wasix-napi/npm-packages; do | |
| if [[ -e "$artifact_root" ]]; then | |
| lock_args+=(--artifact-root "$artifact_root") | |
| fi | |
| done | |
| while IFS= read -r artifact_root; do | |
| if [[ ! -d "$artifact_root" ]]; then | |
| echo "Selected extension artifacts are missing $artifact_root" >&2 | |
| exit 1 | |
| fi | |
| lock_args+=(--artifact-root "$artifact_root") | |
| done < <(tools/dev/bun.sh tools/release/release_graph_query.mjs \ | |
| ci-products \ | |
| --family extension-artifacts \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --field artifact-root \ | |
| --format lines) | |
| tools/dev/bun.sh tools/release/publication-lock.mjs "${lock_args[@]}" | |
| tools/dev/bun.sh tools/release/publication-lock.mjs \ | |
| verify \ | |
| --lock target/release/publication-lock.json \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Match prior approved publication lock | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| run: | | |
| approved_lock=target/approved-publication-lock/publication-lock.json | |
| if ! cmp -s "$approved_lock" "$PUBLICATION_LOCK_PATH"; then | |
| echo 'publication lock differs from the prior approved dry-run lock' >&2 | |
| diff --unified "$approved_lock" "$PUBLICATION_LOCK_PATH" || true | |
| exit 1 | |
| fi | |
| echo 'publication lock exactly matches the prior approved dry-run lock' | |
| - name: Assemble and sign the exact Maven Central bundle before release mutation | |
| id: preflight_maven_bundle | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.registry_needs.outputs.needs_maven == 'true' }} | |
| timeout-minutes: 15 | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_GPG_KEY_ID }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| run: | | |
| tools/dev/bun.sh tools/release/preflight-maven-central-bundle.mjs \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --release-commit "$RELEASE_HEAD_SHA" | |
| - name: Prove the exact SwiftPM source tag is remotely collision-free | |
| id: preflight_swift_source_tag | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-swift') }} | |
| timeout-minutes: 2 | |
| run: | | |
| tools/dev/bun.sh tools/release/preflight-swiftpm-source-tag.mjs \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --release-commit "$RELEASE_HEAD_SHA" | |
| - name: Classify pre-tag registry publication state | |
| id: bootstrap_ledger_state | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && (steps.registry_needs.outputs.needs_cargo == 'true' || steps.registry_needs.outputs.needs_npm == 'true') }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| RELEASE_HEAD_SHA: ${{ steps.release_head.outputs.sha }} | |
| run: bun .github/scripts/registry-bootstrap-ledger-state.mjs | |
| - name: Download immutable registry bootstrap ledger | |
| if: ${{ steps.bootstrap_ledger_state.outputs.needs_ledger == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| Release \ | |
| "$RELEASE_HEAD_SHA" \ | |
| "$BOOTSTRAP_LEDGER_PATH" \ | |
| --artifact oliphaunt-bootstrap-ledger | |
| - name: Verify immutable bootstrap ledger and registry existence | |
| if: ${{ steps.bootstrap_ledger_state.outputs.needs_ledger == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/bootstrap-ledger.mjs verify \ | |
| --lock "$PUBLICATION_LOCK_PATH" \ | |
| --ledger "$BOOTSTRAP_LEDGER_PATH" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --verify-registries | |
| - name: Freeze bootstrap publication capsule | |
| id: freeze_bootstrap_capsule | |
| if: ${{ inputs.operation == 'publish-dry-run' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/bootstrap-publication-capsule.mjs pack \ | |
| --lock "$PUBLICATION_LOCK_PATH" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --output target/release/oliphaunt-bootstrap-capsule.tar | |
| - name: Upload frozen publication lock | |
| id: preserve_publication_lock | |
| if: ${{ inputs.operation == 'publish-dry-run' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: oliphaunt-publication-lock | |
| path: target/release/publication-lock.json | |
| if-no-files-found: error | |
| overwrite: true | |
| retention-days: 90 | |
| - name: Upload frozen bootstrap publication capsule | |
| id: preserve_bootstrap_capsule | |
| if: ${{ inputs.operation == 'publish-dry-run' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: oliphaunt-bootstrap-capsule | |
| path: target/release/oliphaunt-bootstrap-capsule.tar | |
| if-no-files-found: error | |
| overwrite: true | |
| retention-days: 90 | |
| - name: Upload publication lock audit evidence | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: oliphaunt-publication-lock-${{ inputs.operation }} | |
| path: target/release/publication-lock.json | |
| if-no-files-found: error | |
| overwrite: true | |
| retention-days: 90 | |
| - name: Cool down and open the shared GitHub content-write journal | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 65 | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve \ | |
| --label "release mutation cold-start reservation" | |
| - name: Ensure exact immutable release transport ref | |
| id: ensure_release_transport_ref | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 3 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_OPERATION: publish | |
| RELEASE_TRANSPORT_CONTENT_WRITE_ADMISSION: pre-reserved | |
| run: node .github/scripts/release-transport-ref.mjs ensure "$RELEASE_HEAD_SHA" | |
| - name: Stage exact-SHA product tags and draft releases | |
| id: stage_github_releases | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 31 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| bun .github/scripts/manage-release-drafts.mjs stage \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --state staged | |
| - name: Verify exact product tags | |
| id: verify_product_tags | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 5 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: tools/dev/bun.sh tools/release/verify_product_tags.mjs --products-json "${PRODUCTS_JSON}" --target "$RELEASE_HEAD_SHA" | |
| - name: Verify exact-SHA GitHub release staging | |
| id: verify_github_staging | |
| if: ${{ inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| timeout-minutes: 5 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: bun .github/scripts/manage-release-drafts.mjs verify --products-json "$PRODUCTS_JSON" --head-ref "$RELEASE_HEAD_SHA" --state staged | |
| - name: Publish all selected GitHub release asset sets concurrently | |
| id: publish_github_assets | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 95 | |
| env: | |
| GITHUB_RELEASE_ASSET_UPLOAD_REPORT_PATH: ${{ runner.temp }}/oliphaunt-github-release-asset-upload-report.json | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: tools/dev/bun.sh tools/release/release-publish.mjs publish --step github-release-assets --products-json "${PRODUCTS_JSON}" --head-ref "$RELEASE_HEAD_SHA" --publication-lock "$PUBLICATION_LOCK_PATH" | |
| - name: Resolve exact selected extension attestation subjects | |
| id: extension_attestation_subjects | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.release_plan.outputs.has_extension_products == 'true' }} | |
| env: | |
| EXTENSION_PRODUCTS_JSON: ${{ steps.release_plan.outputs.extension_products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/locked-attestation-subjects.mjs \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --products-json "$EXTENSION_PRODUCTS_JSON" \ | |
| --github-output "$GITHUB_OUTPUT" | |
| - name: Reserve extension attestation content write (shard 1) | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.release_plan.outputs.has_extension_products == 'true' && steps.extension_attestation_subjects.outputs.nonempty_1 == 'true' }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "extension attestation shard 1" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "extension attestation shard 1 API attempt" | |
| - name: Attest selected extension release assets (shard 1) | |
| id: attest_extensions_1 | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.release_plan.outputs.has_extension_products == 'true' && steps.extension_attestation_subjects.outputs.nonempty_1 == 'true' }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: ${{ steps.extension_attestation_subjects.outputs.paths_1 }} | |
| - name: Reserve extension attestation content write (shard 2) | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.release_plan.outputs.has_extension_products == 'true' && steps.extension_attestation_subjects.outputs.nonempty_2 == 'true' }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "extension attestation shard 2" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "extension attestation shard 2 API attempt" | |
| - name: Attest selected extension release assets (shard 2) | |
| id: attest_extensions_2 | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && steps.release_plan.outputs.has_extension_products == 'true' && steps.extension_attestation_subjects.outputs.nonempty_2 == 'true' }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: ${{ steps.extension_attestation_subjects.outputs.paths_2 }} | |
| - name: Reserve liboliphaunt attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-native') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "liboliphaunt native attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "liboliphaunt native attestation API attempt" | |
| - name: Attest liboliphaunt release assets | |
| id: attest_liboliphaunt_native | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-native') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/liboliphaunt/release-assets/*.tar.gz | |
| target/liboliphaunt/release-assets/*.tar.zst | |
| target/liboliphaunt/release-assets/*.zip | |
| target/liboliphaunt/release-assets/*.tsv | |
| target/liboliphaunt/release-assets/*.sha256 | |
| target/extension-artifacts/liboliphaunt-native/oliphaunt-extension-contrib-pg18/release-assets/* | |
| - name: Publish Swift SDK GitHub release and SwiftPM tags | |
| id: publish_swift_source_tag | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-swift') }} | |
| timeout-minutes: 6 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: tools/dev/bun.sh tools/release/release-publish.mjs publish --product oliphaunt-swift --step github-release --head-ref "$RELEASE_HEAD_SHA" --publication-lock "$PUBLICATION_LOCK_PATH" | |
| - name: Reserve broker attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-broker') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "broker attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "broker attestation API attempt" | |
| - name: Attest broker release assets | |
| id: attest_broker | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-broker') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/oliphaunt-broker/release-assets/*.tar.gz | |
| target/oliphaunt-broker/release-assets/*.zip | |
| target/oliphaunt-broker/release-assets/*.sha256 | |
| - name: Reserve Node direct attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-node-direct') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "Node direct attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "Node direct attestation API attempt" | |
| - name: Attest Node direct release assets | |
| id: attest_node_direct | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-node-direct') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/oliphaunt-node-direct/release-assets/*.tar.gz | |
| target/oliphaunt-node-direct/release-assets/*.zip | |
| target/oliphaunt-node-direct/release-assets/*.sha256 | |
| - name: Reserve WASIX Node-API attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-wasix-napi') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "WASIX Node-API attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "WASIX Node-API attestation API attempt" | |
| - name: Attest WASIX Node-API release assets | |
| id: attest_wasix_napi | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'oliphaunt-wasix-napi') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/oliphaunt-wasix-napi/release-assets/*.tar.gz | |
| target/oliphaunt-wasix-napi/release-assets/*.zip | |
| target/oliphaunt-wasix-napi/release-assets/*.sha256 | |
| - name: Reserve WASIX attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "WASIX attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "WASIX attestation API attempt" | |
| - name: Attest WASIX release assets | |
| id: attest_wasix | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/oliphaunt-wasix/release-assets/*.tar.zst | |
| target/oliphaunt-wasix/release-assets/*.sha256 | |
| target/extension-artifacts/liboliphaunt-wasix/oliphaunt-extension-contrib-pg18/release-assets/* | |
| - name: Reserve WASIX postmaster attestation content write | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix-postmaster') }} | |
| run: | | |
| tools/dev/bun.sh tools/release/github-content-write-pacer.mjs reserve --label "WASIX postmaster attestation" | |
| tools/dev/bun.sh tools/release/github-core-request-journal.mjs reserve --label "WASIX postmaster attestation API attempt" | |
| - name: Attest WASIX postmaster release assets | |
| id: attest_wasix_postmaster | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' && contains(fromJson(steps.release_plan.outputs.products_json), 'liboliphaunt-wasix-postmaster') }} | |
| timeout-minutes: 5 | |
| uses: actions/attest-build-provenance@43d14bc2b83dec42d39ecae14e916627a18bb661 | |
| with: | |
| subject-path: | | |
| target/oliphaunt-wasix-postmaster/release-assets/*.tar.zst | |
| target/oliphaunt-wasix-postmaster/release-assets/*.sha256 | |
| - name: Freeze exact GitHub release asset and attestation evidence | |
| id: freeze_github_evidence | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 10 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| EXTENSIONS_ATTESTATION_BUNDLE_1: ${{ steps.attest_extensions_1.outputs.bundle-path }} | |
| EXTENSIONS_ATTESTATION_BUNDLE_2: ${{ steps.attest_extensions_2.outputs.bundle-path }} | |
| LIBOLIPHAUNT_NATIVE_ATTESTATION_BUNDLE: ${{ steps.attest_liboliphaunt_native.outputs.bundle-path }} | |
| BROKER_ATTESTATION_BUNDLE: ${{ steps.attest_broker.outputs.bundle-path }} | |
| NODE_DIRECT_ATTESTATION_BUNDLE: ${{ steps.attest_node_direct.outputs.bundle-path }} | |
| WASIX_NAPI_ATTESTATION_BUNDLE: ${{ steps.attest_wasix_napi.outputs.bundle-path }} | |
| WASIX_ATTESTATION_BUNDLE: ${{ steps.attest_wasix.outputs.bundle-path }} | |
| WASIX_POSTMASTER_ATTESTATION_BUNDLE: ${{ steps.attest_wasix_postmaster.outputs.bundle-path }} | |
| run: | | |
| bundle_args=() | |
| for bundle in \ | |
| "$EXTENSIONS_ATTESTATION_BUNDLE_1" \ | |
| "$EXTENSIONS_ATTESTATION_BUNDLE_2" \ | |
| "$LIBOLIPHAUNT_NATIVE_ATTESTATION_BUNDLE" \ | |
| "$BROKER_ATTESTATION_BUNDLE" \ | |
| "$NODE_DIRECT_ATTESTATION_BUNDLE" \ | |
| "$WASIX_NAPI_ATTESTATION_BUNDLE" \ | |
| "$WASIX_ATTESTATION_BUNDLE" \ | |
| "$WASIX_POSTMASTER_ATTESTATION_BUNDLE" | |
| do | |
| if [[ -n "$bundle" ]]; then | |
| bundle_args+=(--attestation-bundle "$bundle") | |
| fi | |
| done | |
| tools/dev/bun.sh tools/release/verify_github_release_attestations.mjs pre-mutation \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --output target/release/github-release-attestation-receipt.json \ | |
| "${bundle_args[@]}" | |
| - name: Open registry publication window | |
| id: registry_publication_window | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| run: | | |
| for name in RELEASE_JOB_HARD_DEADLINE_EPOCH POST_REGISTRY_RESERVE_SECONDS; do | |
| if [[ ! "${!name:-}" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "$name must be a positive integer" >&2 | |
| exit 1 | |
| fi | |
| done | |
| mutation_deadline=$(( RELEASE_JOB_HARD_DEADLINE_EPOCH - POST_REGISTRY_RESERVE_SECONDS )) | |
| if (( $(date +%s) >= mutation_deadline )); then | |
| echo 'Not enough time remains for registry publication and final verification; rerun this idempotent release.' >&2 | |
| exit 1 | |
| fi | |
| echo "REGISTRY_MUTATION_DEADLINE_EPOCH=$mutation_deadline" >> "$GITHUB_ENV" | |
| - name: Publish and reconcile every exact-lock registry carrier | |
| id: publish_registries | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 240 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} | |
| ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyId: ${{ secrets.MAVEN_GPG_KEY_ID }} | |
| ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.MAVEN_GPG_PASSPHRASE }} | |
| run: | | |
| tools/dev/bun.sh tools/release/release-publish.mjs publish \ | |
| --registry-plan \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" | |
| - name: Verify published release | |
| id: verify_published_release | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 8 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| gh auth setup-git | |
| git fetch --force --tags origin | |
| tools/dev/bun.sh tools/release/release-verify.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --registry-receipts target/release/registry-integrity-receipts.json \ | |
| --github-release-receipt target/release/github-release-attestation-receipt.json | |
| - name: Resolve and install exact public consumer surfaces | |
| id: public_consumer_smoke | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 15 | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/public-consumer-smoke.mjs \ | |
| --publication-lock "$PUBLICATION_LOCK_PATH" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --registry-receipts target/release/registry-integrity-receipts.json \ | |
| --github-release-receipt target/release/github-release-attestation-receipt.json \ | |
| --output target/release/public-consumer-smoke.json | |
| - name: Preserve public consumer evidence | |
| id: preserve_consumer_evidence | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 2 | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: public-consumer-evidence-${{ github.sha }} | |
| path: target/release/public-consumer-smoke.json | |
| if-no-files-found: error | |
| overwrite: true | |
| retention-days: 90 | |
| - name: Reverify exact publication lock before promotion | |
| id: reverify_publication_lock | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 2 | |
| run: | | |
| tools/dev/bun.sh tools/release/publication-lock.mjs \ | |
| verify \ | |
| --lock "$PUBLICATION_LOCK_PATH" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Preserve release evidence | |
| id: preserve_release_evidence | |
| if: ${{ always() && inputs.operation == 'publish' && steps.release_plan.outputs.has_release_changes == 'true' }} | |
| continue-on-error: true | |
| timeout-minutes: 3 | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: release-evidence-${{ github.sha }} | |
| path: | | |
| target/release/publication-lock.json | |
| target/release/registry-integrity-receipts.json | |
| target/release/github-release-attestation-receipt.json | |
| target/release/public-consumer-smoke.json | |
| ${{ runner.temp }}/oliphaunt-github-release-asset-upload-report.json | |
| if-no-files-found: warn | |
| include-hidden-files: true | |
| overwrite: true | |
| retention-days: 90 | |
| - name: Promote verified GitHub release drafts | |
| id: promote_github_releases | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && inputs.operation == 'publish' }} | |
| timeout-minutes: 16 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/release-please-pr-lifecycle.mjs \ | |
| assert-markable \ | |
| --release-sha "${{ steps.verify_publication_candidate.outputs.release_sha }}" \ | |
| --base main | |
| bun .github/scripts/manage-release-drafts.mjs promote \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| tools/dev/bun.sh tools/release/release-please-pr-lifecycle.mjs \ | |
| mark-tagged \ | |
| --release-sha "${{ steps.verify_publication_candidate.outputs.release_sha }}" \ | |
| --base main | |
| publish: | |
| name: Publish release | |
| needs: | |
| - validate-inputs | |
| runs-on: macos-26 | |
| timeout-minutes: 360 | |
| if: ${{ inputs.operation == 'publish' }} | |
| environment: release-publish | |
| permissions: | |
| actions: read | |
| artifact-metadata: write | |
| attestations: write | |
| contents: write | |
| id-token: write | |
| pull-requests: write | |
| steps: *release_candidate_steps | |
| publish-bootstrap: | |
| name: Bootstrap registry identities | |
| needs: | |
| - validate-inputs | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 360 | |
| if: ${{ inputs.operation == 'publish-bootstrap' }} | |
| environment: release-bootstrap | |
| permissions: | |
| actions: read | |
| contents: write | |
| id-token: write | |
| pull-requests: read | |
| outputs: | |
| continuation_required: ${{ steps.require_bootstrap_execution_decision.outputs.deferred }} | |
| continuation_artifact_id: ${{ steps.preserve_deferred_bootstrap_ledger.outputs.artifact-id }} | |
| continuation_artifact_digest: ${{ steps.preserve_deferred_bootstrap_ledger.outputs.artifact-digest }} | |
| continuation_contract_digest: ${{ steps.prepare_bootstrap_continuation.outputs.contract_digest }} | |
| steps: | |
| - name: Record bounded bootstrap job deadline | |
| id: bootstrap_job_deadline | |
| run: | | |
| if [[ ! "$RELEASE_JOB_HARD_WINDOW_SECONDS" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'RELEASE_JOB_HARD_WINDOW_SECONDS must be a positive integer' >&2 | |
| exit 1 | |
| fi | |
| hard_deadline=$(( $(date +%s) + RELEASE_JOB_HARD_WINDOW_SECONDS )) | |
| { | |
| echo "REGISTRY_JOB_HARD_DEADLINE_EPOCH=$hard_deadline" | |
| echo "OLIPHAUNT_GITHUB_RUN_SNAPSHOT_DIR=$RUNNER_TEMP/oliphaunt-github-run-snapshots" | |
| } >> "$GITHUB_ENV" | |
| echo "The bootstrap job must stop registry work before Unix time $hard_deadline." | |
| - name: Checkout repository | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Resolve exact release commit | |
| id: release_head | |
| timeout-minutes: 1 | |
| env: | |
| INPUT_RELEASE_COMMIT: ${{ inputs.release_commit }} | |
| run: .github/scripts/resolve-release-head.sh | |
| - name: Set up Moon | |
| uses: ./.github/actions/setup-moon | |
| with: | |
| install-workspace: "false" | |
| - name: Plan bootstrap releases | |
| id: release_plan | |
| run: | | |
| tools/dev/bun.sh tools/release/release_plan.mjs \ | |
| --from-product-tags \ | |
| --include-current-tags \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --format github-output \ | |
| >> "$GITHUB_OUTPUT" | |
| - name: No package release planned | |
| if: ${{ steps.release_plan.outputs.has_release_changes != 'true' }} | |
| run: echo "No release-affecting product changes were found since the last product tag." | |
| - name: Resolve selected bootstrap authentication needs | |
| id: registry_needs | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: bun .github/scripts/selected-registry-needs.mjs | |
| - name: Resolve registry identity bootstrap scope | |
| id: bootstrap_scope | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' }} | |
| env: | |
| NEEDS_CARGO: ${{ steps.registry_needs.outputs.needs_cargo }} | |
| NEEDS_NPM: ${{ steps.registry_needs.outputs.needs_npm }} | |
| run: | | |
| required=false | |
| if [[ "$NEEDS_CARGO" == true || "$NEEDS_NPM" == true ]]; then | |
| required=true | |
| fi | |
| echo "required=$required" >> "$GITHUB_OUTPUT" | |
| - name: No registry identities require bootstrap | |
| if: ${{ steps.release_plan.outputs.has_release_changes == 'true' && steps.bootstrap_scope.outputs.required != 'true' }} | |
| run: echo 'The selected release has no Cargo or npm identities; bootstrap is a no-op.' | |
| - name: Set up pinned npm publisher | |
| id: setup_bootstrap_npm | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && steps.registry_needs.outputs.needs_npm == 'true' }} | |
| timeout-minutes: 3 | |
| uses: ./.github/actions/setup-npm-publisher | |
| with: | |
| npm-version: ${{ env.NPM_VERSION }} | |
| - name: Verify direct-workflow OIDC identity | |
| id: verify_bootstrap_oidc_identity | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| RELEASE_OPERATION: publish-bootstrap | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| run: bun .github/scripts/verify-github-oidc-identity.mjs | |
| - name: Prove workflow HEAD is the release commit | |
| id: verify_bootstrap_publication_candidate | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| timeout-minutes: 2 | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/verify-publication-candidate.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --github-output "$GITHUB_OUTPUT" | |
| - name: Prove Release Please PR can complete after bootstrap publication | |
| id: assert_bootstrap_release_please_markable | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| timeout-minutes: 1 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| tools/dev/bun.sh tools/release/release-please-pr-lifecycle.mjs \ | |
| assert-markable \ | |
| --release-sha "${{ steps.verify_bootstrap_publication_candidate.outputs.release_sha }}" \ | |
| --base main | |
| - name: Preflight selected product tag and release collisions | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| tools/dev/bun.sh tools/release/verify_product_tags.mjs \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --target "$RELEASE_HEAD_SHA" \ | |
| --allow-missing | |
| bun .github/scripts/manage-release-drafts.mjs preflight \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Require existing qualified release-commit CI run | |
| id: ci_qualification | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| REQUIRES_WASIX_EVIDENCE: ${{ steps.release_plan.outputs.requires_wasix_release_regression_evidence }} | |
| run: | | |
| qualification_args=( | |
| CI | |
| "$RELEASE_HEAD_SHA" | |
| 0 | |
| --event push | |
| --event workflow_dispatch | |
| --job Builds | |
| --job Required | |
| --job Qualified | |
| --artifact artifact-build-plan | |
| --artifact oliphaunt-release-candidate | |
| ) | |
| if [[ "$REQUIRES_WASIX_EVIDENCE" == true ]]; then | |
| qualification_args+=(--artifact wasix-release-regression-evidence) | |
| fi | |
| bash .github/scripts/require-workflow-success.sh "${qualification_args[@]}" | |
| - name: Download exact-SHA qualification record | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| "$RUNNER_TEMP/release-candidate" \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Qualified \ | |
| --artifact oliphaunt-release-candidate | |
| - name: Download exact-SHA affected plan | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| "$RUNNER_TEMP/release-candidate/affected-plan" \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job Plan \ | |
| --artifact artifact-build-plan | |
| - name: Download required exact-SHA WASIX evidence | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && steps.release_plan.outputs.requires_wasix_release_regression_evidence == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| CI \ | |
| "$RELEASE_HEAD_SHA" \ | |
| "$RUNNER_TEMP/release-candidate/wasix-evidence" \ | |
| --run-id "$CI_RUN_ID" \ | |
| --job "E2E / WASIX release regression" \ | |
| --artifact wasix-release-regression-evidence | |
| - name: Verify exact-SHA qualification record | |
| id: verify_bootstrap_qualification | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| CI_RUN_ID: ${{ steps.ci_qualification.outputs.run_id }} | |
| WASIX_EVIDENCE_REQUIRED: ${{ steps.release_plan.outputs.requires_wasix_release_regression_evidence }} | |
| run: | | |
| node .github/scripts/verify-release-candidate.mjs \ | |
| "$RUNNER_TEMP/release-candidate/oliphaunt-release-candidate.json" \ | |
| --plan "$RUNNER_TEMP/release-candidate/affected-plan/ci-plan.json" \ | |
| --wasix-evidence-required "$WASIX_EVIDENCE_REQUIRED" \ | |
| --wasix-evidence-root "$RUNNER_TEMP/release-candidate/wasix-evidence" | |
| - name: Inspect exact parent bootstrap continuation | |
| id: inspect_bootstrap_continuation | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && inputs.continuation_pointer != '' }} | |
| # Covers the 53-minute sequential GitHub-read envelope plus local verification margin. | |
| timeout-minutes: 55 | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| RELEASE_CONTINUATION_ARCHIVE: ${{ runner.temp }}/release-continuation.zip | |
| RELEASE_OPERATION: publish-bootstrap | |
| run: node .github/scripts/inspect-release-continuation.mjs | |
| - name: Select one approved dry-run capsule | |
| id: approved_bootstrap_capsule | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && inputs.continuation_pointer == '' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| approved_artifacts=( | |
| --artifact oliphaunt-publication-lock | |
| --artifact oliphaunt-bootstrap-capsule | |
| ) | |
| bash .github/scripts/require-workflow-success.sh \ | |
| Release \ | |
| "$RELEASE_HEAD_SHA" \ | |
| 0 \ | |
| --event workflow_dispatch \ | |
| "${approved_artifacts[@]}" | |
| - name: Download approved lock and capsule from one dry-run | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| DRY_RUN_ID: ${{ steps.inspect_bootstrap_continuation.outputs.approved_run_id || steps.approved_bootstrap_capsule.outputs.run_id }} | |
| APPROVED_ARTIFACT_METADATA_JSON: ${{ steps.inspect_bootstrap_continuation.outputs.approved_artifact_metadata_json || steps.approved_bootstrap_capsule.outputs.artifact_metadata_json }} | |
| run: | | |
| node .github/scripts/download-build-artifacts.mjs \ | |
| Release \ | |
| "$RELEASE_HEAD_SHA" \ | |
| "$RUNNER_TEMP/approved-bootstrap" \ | |
| --run-id "$DRY_RUN_ID" \ | |
| --artifact-metadata-json "$APPROVED_ARTIFACT_METADATA_JSON" \ | |
| --artifact oliphaunt-publication-lock \ | |
| --artifact oliphaunt-bootstrap-capsule | |
| - name: Verify and install approved bootstrap capsule | |
| id: verify_bootstrap_capsule | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| run: | | |
| if [[ -e "$GITHUB_WORKSPACE/target" ]]; then | |
| echo 'bootstrap capsule installation requires an absent workspace target directory' >&2 | |
| exit 1 | |
| fi | |
| tools/dev/bun.sh tools/release/bootstrap-publication-capsule.mjs verify-extract \ | |
| --transport "$RUNNER_TEMP/approved-bootstrap/oliphaunt-bootstrap-capsule.tar" \ | |
| --approved-lock "$RUNNER_TEMP/approved-bootstrap/publication-lock.json" \ | |
| --products-json "$PRODUCTS_JSON" \ | |
| --head-ref "$RELEASE_HEAD_SHA" \ | |
| --workspace-root "$GITHUB_WORKSPACE" | |
| - name: Verify external lock equals installed capsule lock | |
| id: verify_bootstrap_lock | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| run: | | |
| if ! cmp -s "$RUNNER_TEMP/approved-bootstrap/publication-lock.json" "$PUBLICATION_LOCK_PATH"; then | |
| echo 'installed capsule lock differs from the separately downloaded approved publication lock' >&2 | |
| exit 1 | |
| fi | |
| tools/dev/bun.sh tools/release/publication-lock.mjs verify \ | |
| --lock "$PUBLICATION_LOCK_PATH" \ | |
| --head-ref "$RELEASE_HEAD_SHA" | |
| - name: Restore prior bootstrap checkpoint chain | |
| id: restore_bootstrap_checkpoint | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| RELEASE_CONTINUATION_ARCHIVE: ${{ runner.temp }}/release-continuation.zip | |
| run: node .github/scripts/download-bootstrap-ledger.mjs | |
| - name: Require bootstrap credentials before mutation | |
| id: require_bootstrap_credentials | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| CRATES_IO_BOOTSTRAP_TOKEN: ${{ secrets.CRATES_IO_BOOTSTRAP_TOKEN }} | |
| NPM_BOOTSTRAP_TOKEN: ${{ secrets.NPM_BOOTSTRAP_TOKEN }} | |
| SELECTED_NEEDS_CARGO: ${{ steps.registry_needs.outputs.needs_cargo }} | |
| SELECTED_NEEDS_NPM: ${{ steps.registry_needs.outputs.needs_npm }} | |
| run: | | |
| needs_cargo="$SELECTED_NEEDS_CARGO" | |
| needs_npm="$SELECTED_NEEDS_NPM" | |
| if [[ "$needs_cargo" != true && "$needs_cargo" != false ]]; then | |
| echo 'bootstrap Cargo credential decision is not boolean' >&2 | |
| exit 1 | |
| fi | |
| if [[ "$needs_npm" != true && "$needs_npm" != false ]]; then | |
| echo 'bootstrap npm credential decision is not boolean' >&2 | |
| exit 1 | |
| fi | |
| if [[ "$needs_cargo" == true && -z "$CRATES_IO_BOOTSTRAP_TOKEN" ]]; then | |
| echo 'selected products require CRATES_IO_BOOTSTRAP_TOKEN in the release-bootstrap environment' >&2 | |
| exit 1 | |
| fi | |
| if [[ "$needs_npm" == true && -z "$NPM_BOOTSTRAP_TOKEN" ]]; then | |
| echo 'selected products require NPM_BOOTSTRAP_TOKEN in the release-bootstrap environment' >&2 | |
| exit 1 | |
| fi | |
| { | |
| echo "needs_cargo_token=$needs_cargo" | |
| echo "needs_npm_token=$needs_npm" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Ensure exact immutable release transport ref | |
| id: ensure_bootstrap_transport_ref | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && inputs.continuation_pointer == '' }} | |
| timeout-minutes: 3 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_OPERATION: publish-bootstrap | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| RELEASE_TRANSPORT_CONTENT_WRITE_ADMISSION: isolated-bootstrap | |
| run: node .github/scripts/release-transport-ref.mjs ensure "$RELEASE_HEAD_SHA" | |
| - name: Start bounded bootstrap mutation window | |
| id: bootstrap_mutation_deadline | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| run: | | |
| if [[ ! "$BOOTSTRAP_REGISTRY_MUTATION_WINDOW_SECONDS" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'BOOTSTRAP_REGISTRY_MUTATION_WINDOW_SECONDS must be a positive integer' >&2 | |
| exit 1 | |
| fi | |
| if [[ ! "$REGISTRY_JOB_HARD_DEADLINE_EPOCH" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'REGISTRY_JOB_HARD_DEADLINE_EPOCH must be a positive Unix timestamp' >&2 | |
| exit 1 | |
| fi | |
| now=$(date +%s) | |
| window_deadline=$(( now + BOOTSTRAP_REGISTRY_MUTATION_WINDOW_SECONDS )) | |
| deadline=$window_deadline | |
| if (( REGISTRY_JOB_HARD_DEADLINE_EPOCH < deadline )); then | |
| deadline=$REGISTRY_JOB_HARD_DEADLINE_EPOCH | |
| fi | |
| echo "REGISTRY_MUTATION_DEADLINE_EPOCH=$deadline" >> "$GITHUB_ENV" | |
| echo "Bootstrap registry mutation must stop before Unix time $deadline." | |
| - name: Configure npm identity-bootstrap authentication | |
| id: configure_bootstrap_npm_auth | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' && steps.require_bootstrap_credentials.outputs.needs_npm_token == 'true' }} | |
| env: | |
| NPM_BOOTSTRAP_TOKEN: ${{ secrets.NPM_BOOTSTRAP_TOKEN }} | |
| run: | | |
| umask 077 | |
| npmrc="$RUNNER_TEMP/oliphaunt-bootstrap.npmrc" | |
| printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_BOOTSTRAP_TOKEN" > "$npmrc" | |
| - name: Bootstrap missing Cargo and npm identities | |
| id: bootstrap_registry_identities | |
| if: ${{ steps.bootstrap_scope.outputs.required == 'true' }} | |
| env: | |
| CARGO_REGISTRY_TOKEN: ${{ steps.require_bootstrap_credentials.outputs.needs_cargo_token == 'true' && secrets.CRATES_IO_BOOTSTRAP_TOKEN || '' }} | |
| NPM_CONFIG_USERCONFIG: ${{ runner.temp }}/oliphaunt-bootstrap.npmrc | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| REGISTRY_BOOTSTRAP_CARGO_SECONDS_PER_CARRIER: ${{ vars.REGISTRY_BOOTSTRAP_CARGO_SECONDS_PER_CARRIER || '30' }} | |
| REGISTRY_BOOTSTRAP_NPM_SECONDS_PER_CARRIER: ${{ vars.REGISTRY_BOOTSTRAP_NPM_SECONDS_PER_CARRIER || '30' }} | |
| REGISTRY_BOOTSTRAP_RECONCILIATION_SECONDS_PER_CARRIER: ${{ vars.REGISTRY_BOOTSTRAP_RECONCILIATION_SECONDS_PER_CARRIER || '6' }} | |
| REGISTRY_BOOTSTRAP_RESERVE_SECONDS: ${{ vars.REGISTRY_BOOTSTRAP_RESERVE_SECONDS || '600' }} | |
| run: bun .github/scripts/bootstrap-registry-identities.mjs | |
| - name: Require a typed bootstrap execution decision | |
| id: require_bootstrap_execution_decision | |
| if: ${{ steps.bootstrap_registry_identities.outcome == 'success' }} | |
| timeout-minutes: 1 | |
| env: | |
| COMPLETE: ${{ steps.bootstrap_registry_identities.outputs.complete }} | |
| DEFERRED: ${{ steps.bootstrap_registry_identities.outputs.deferred }} | |
| DEFERRAL_MODE: ${{ steps.bootstrap_registry_identities.outputs.deferral_mode }} | |
| PROGRESS_COUNT: ${{ steps.bootstrap_registry_identities.outputs.progress_count }} | |
| REMAINING_COUNT: ${{ steps.bootstrap_registry_identities.outputs.remaining_count }} | |
| NOT_BEFORE_EPOCH: ${{ steps.bootstrap_registry_identities.outputs.not_before_epoch }} | |
| run: | | |
| if [[ "$COMPLETE" == true && "$DEFERRED" == false ]]; then | |
| if [[ -n "$DEFERRAL_MODE" || "$REMAINING_COUNT" != 0 ]]; then | |
| echo 'complete bootstrap result retains a deferral mode or remaining carriers' >&2 | |
| exit 1 | |
| fi | |
| elif [[ "$COMPLETE" == false && "$DEFERRED" == true ]]; then | |
| if [[ ! "$REMAINING_COUNT" =~ ^[1-9][0-9]*$ || ! "$NOT_BEFORE_EPOCH" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'deferred bootstrap result requires remaining work and a positive not-before time' >&2 | |
| exit 1 | |
| fi | |
| if [[ "$DEFERRAL_MODE" == progress ]]; then | |
| if [[ ! "$PROGRESS_COUNT" =~ ^[1-9][0-9]*$ ]]; then | |
| echo 'bootstrap progress deferral requires nonzero durable progress' >&2 | |
| exit 1 | |
| fi | |
| elif [[ "$DEFERRAL_MODE" == rate-limit ]]; then | |
| if [[ "$PROGRESS_COUNT" != 0 ]]; then | |
| echo 'bootstrap rate-limit deferral cannot claim durable progress' >&2 | |
| exit 1 | |
| fi | |
| elif [[ "$DEFERRAL_MODE" == pre-mutation-deadline ]]; then | |
| if [[ "$PROGRESS_COUNT" != 0 ]]; then | |
| echo 'bootstrap pre-mutation deadline deferral cannot claim durable progress' >&2 | |
| exit 1 | |
| fi | |
| else | |
| echo 'deferred bootstrap result has an unsupported deferral mode' >&2 | |
| exit 1 | |
| fi | |
| else | |
| echo 'bootstrap publisher must emit exactly one of complete or deferred' >&2 | |
| exit 1 | |
| fi | |
| { | |
| echo "complete=$COMPLETE" | |
| echo "deferred=$DEFERRED" | |
| echo "deferral_mode=$DEFERRAL_MODE" | |
| echo "progress_count=$PROGRESS_COUNT" | |
| echo "remaining_count=$REMAINING_COUNT" | |
| echo "not_before_epoch=$NOT_BEFORE_EPOCH" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Seal exact bootstrap continuation contract | |
| id: prepare_bootstrap_continuation | |
| if: ${{ steps.require_bootstrap_execution_decision.outputs.deferred == 'true' }} | |
| timeout-minutes: 3 | |
| env: | |
| PRODUCTS_JSON: ${{ steps.release_plan.outputs.products_json }} | |
| APPROVED_RUN_ID: ${{ steps.inspect_bootstrap_continuation.outputs.approved_run_id || steps.approved_bootstrap_capsule.outputs.run_id }} | |
| APPROVED_ARTIFACT_METADATA_JSON: ${{ steps.inspect_bootstrap_continuation.outputs.approved_artifact_metadata_json || steps.approved_bootstrap_capsule.outputs.artifact_metadata_json }} | |
| RELEASE_OPERATION: publish-bootstrap | |
| RELEASE_CONTINUATION_POINTER: ${{ inputs.continuation_pointer }} | |
| RELEASE_EXECUTION_RESULT_PATH: target/release/bootstrap-execution-result.json | |
| RELEASE_CONTINUATION_STATE_PATH: target/release/bootstrap-ledger | |
| RELEASE_CONTINUATION_CONTRACT_PATH: target/release/release-continuation-contract.json | |
| run: bun .github/scripts/prepare-release-continuation.mjs | |
| - name: Record bootstrap identity result | |
| if: ${{ steps.require_bootstrap_execution_decision.outputs.complete == 'true' }} | |
| run: | | |
| lock_sha256="$(sha256sum "$PUBLICATION_LOCK_PATH" | awk '{print $1}')" | |
| { | |
| echo '## Registry identity bootstrap complete' | |
| echo | |
| echo "- Release commit: \`$RELEASE_HEAD_SHA\`" | |
| echo "- Publication lock SHA-256: \`$lock_sha256\`" | |
| echo '- Scope: selected Cargo and npm identities only' | |
| echo '- Next: configure trusted publishers, revoke bootstrap tokens, then run the normal publish operation' | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Remove bootstrap npm credentials | |
| id: remove_bootstrap_credentials | |
| if: ${{ always() }} | |
| run: rm -f "$RUNNER_TEMP/oliphaunt-bootstrap.npmrc" | |
| - name: Upload immutable deferred bootstrap continuation | |
| id: preserve_deferred_bootstrap_ledger | |
| if: ${{ steps.require_bootstrap_execution_decision.outputs.deferred == 'true' }} | |
| timeout-minutes: 10 | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: bootstrap-continuation-${{ github.sha }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.prepare_bootstrap_continuation.outputs.next_generation }} | |
| path: | | |
| target/release/bootstrap-ledger | |
| target/release/bootstrap-execution-result.json | |
| target/release/release-continuation-contract.json | |
| if-no-files-found: error | |
| include-hidden-files: true | |
| compression-level: 0 | |
| retention-days: 90 | |
| - name: Upload bootstrap identity ledger | |
| id: preserve_bootstrap_ledger | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: oliphaunt-bootstrap-ledger | |
| path: target/release/bootstrap-ledger | |
| if-no-files-found: warn | |
| overwrite: true | |
| retention-days: 90 | |
| dispatch-bootstrap-continuation: | |
| name: Dispatch verified bootstrap continuation | |
| needs: publish-bootstrap | |
| if: ${{ needs.publish-bootstrap.outputs.continuation_required == 'true' }} | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 60 | |
| permissions: | |
| actions: write | |
| contents: read | |
| steps: | |
| - name: Checkout exact release transport | |
| timeout-minutes: 3 | |
| uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd | |
| with: | |
| persist-credentials: false | |
| - name: Set up pinned Node.js | |
| id: setup_dispatch_node | |
| timeout-minutes: 3 | |
| uses: ./.github/actions/setup-node-runtime | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| - name: Dispatch exact verified child run | |
| id: dispatch_continuation | |
| timeout-minutes: 40 | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RELEASE_OPERATION: publish-bootstrap | |
| RELEASE_HEAD_SHA: ${{ github.sha }} | |
| CONTINUATION_ARTIFACT_ID: ${{ needs.publish-bootstrap.outputs.continuation_artifact_id }} | |
| CONTINUATION_ARTIFACT_DIGEST: ${{ needs.publish-bootstrap.outputs.continuation_artifact_digest }} | |
| CONTINUATION_CONTRACT_DIGEST: ${{ needs.publish-bootstrap.outputs.continuation_contract_digest }} | |
| CONTINUATION_AUTHORIZATION_PATH: ${{ runner.temp }}/release-continuation-authorization.json | |
| run: node .github/scripts/dispatch-release-continuation.mjs | |
| - name: Publish exact dispatched-child authorization | |
| id: preserve_continuation_authorization | |
| timeout-minutes: 5 | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | |
| with: | |
| name: ${{ steps.dispatch_continuation.outputs.authorization_artifact_name }} | |
| path: ${{ runner.temp }}/release-continuation-authorization.json | |
| if-no-files-found: error | |
| compression-level: 0 | |
| retention-days: 90 |