Skip to content

Release / publish-dry-run / f0rr0/wasix-ts-napi-release-probe #135

Release / publish-dry-run / f0rr0/wasix-ts-napi-release-probe

Release / publish-dry-run / f0rr0/wasix-ts-napi-release-probe #135

Workflow file for this run

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