mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-01 23:43:16 +01:00
fix: assemble verified host runner assets before release
Reuse release verification producers for the supported native hosts. Validate source-bound data before publication and probe the installed Codex command in the existing offline consumer. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
5fa4abaead
commit
f8e2bee66e
10 files changed
+1061
-19
No files matched your search
@@ -7,18 +7,28 @@ on:
|
||||
description: Commit SHA, branch, or tag to verify
|
||||
required: true
|
||||
type: string
|
||||
runner_assets_only:
|
||||
description: Build only the release daemon/provider-pack data after the caller's source gate
|
||||
type: boolean
|
||||
default: false
|
||||
outputs:
|
||||
runner_assets_artifact:
|
||||
description: Run-bound verified release daemon/provider-pack data
|
||||
value: ${{ jobs.runner_release_assemble.outputs.artifact_name }}
|
||||
|
||||
# Caller-provided refs may name unmerged PR code. AWS is eligible only when
|
||||
# the caller runs on canonical master and verifies that event's exact SHA.
|
||||
# The organization group also restricts these workflow files to master.
|
||||
jobs:
|
||||
runner_chaos_evals:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Pre-release Runner chaos evals
|
||||
uses: ./.github/workflows/runner-chaos-evals.yml
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
|
||||
typecheck:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Typecheck
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 20
|
||||
@@ -97,6 +107,7 @@ jobs:
|
||||
run: pnpm -r typecheck
|
||||
|
||||
general_tests:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: General tests (${{ matrix.group_label }})
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 20
|
||||
@@ -204,6 +215,7 @@ jobs:
|
||||
fi
|
||||
|
||||
serialized_tests:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Serialized tests (${{ matrix.shard_label }})
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 20
|
||||
@@ -253,6 +265,7 @@ jobs:
|
||||
run: pnpm test:run:serialized -- --shard-index ${{ matrix.shard_index }} --shard-count ${{ matrix.shard_count }}
|
||||
|
||||
runner_workflow_evals:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Runner workflow eval scorer contract
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 10
|
||||
@@ -283,6 +296,7 @@ jobs:
|
||||
run: pnpm test:runner-workflow-evals
|
||||
|
||||
verify_paperclip_runner:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Verify Paperclip Runner (${{ matrix.lane }})
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 20
|
||||
@@ -396,6 +410,7 @@ jobs:
|
||||
run: pnpm --filter @paperclipai/paperclip-runner build:rust
|
||||
|
||||
build:
|
||||
if: ${{ !inputs.runner_assets_only }}
|
||||
name: Build
|
||||
runs-on: ${{ vars.AWS_POST_MERGE_CI_ENABLED == 'true' && github.repository == 'paperclipai/paperclip' && github.repository_id == '1170821064' && github.ref == 'refs/heads/master' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.sha != '' && inputs.ref == github.sha && 'runs-on/fleet=paperclip-post-merge-x64/env=public-ci' || 'ubuntu-latest' }}
|
||||
timeout-minutes: 20
|
||||
@@ -424,3 +439,178 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
|
||||
# Only the missing release producers. Source verification stays with the
|
||||
# caller; these unprivileged jobs neither publish nor receive provider secrets.
|
||||
runner_release_binaries:
|
||||
needs: runner_release_request
|
||||
if: inputs.runner_assets_only
|
||||
name: Release daemon (${{ matrix.target }})
|
||||
permissions:
|
||||
contents: read
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- target: darwin-arm64
|
||||
runner: macos-15
|
||||
- target: darwin-x64
|
||||
runner: macos-15-intel
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 24
|
||||
- name: Build the exact native release daemon
|
||||
working-directory: packages/paperclip-runner
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[[ "$SOURCE_SHA" =~ ^[a-f0-9]{40}$ ]]
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
rustup show
|
||||
cargo build --release --manifest-path runner/Cargo.toml --locked -p paperclip-runner-core --bin paperclip-runnerd
|
||||
- name: Record native architecture, checksum and executable metadata
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
TARGET: ${{ matrix.target }}
|
||||
run: |
|
||||
node scripts/release-runner-artifacts.mjs record-binary "$SOURCE_SHA" "$TARGET" \
|
||||
"$GITHUB_WORKSPACE/packages/paperclip-runner/runner/target/release/paperclip-runnerd" \
|
||||
"$RUNNER_TEMP/runner-release/$TARGET"
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: runner-release-${{ inputs.ref }}-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.target }}
|
||||
path: ${{ runner.temp }}/runner-release/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
runner_release_pack:
|
||||
needs: runner_release_request
|
||||
if: inputs.runner_assets_only
|
||||
name: Actual release image Linux daemon and provider-pack identity
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
|
||||
with:
|
||||
version: 9.15.4
|
||||
run_install: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 24
|
||||
- uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4
|
||||
- name: Extract the actual exact-source image provider-pack
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[[ "$SOURCE_SHA" =~ ^[a-f0-9]{40}$ ]]
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
mkdir -p "$RUNNER_TEMP/runner-release-input"
|
||||
owner="paperclip-release-pack-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
|
||||
trap 'docker rm -f "$owner" >/dev/null 2>&1 || true' EXIT
|
||||
# Canary images publish after npm. Build the existing image target
|
||||
# without registry credentials and consume its actual source bytes.
|
||||
pnpm install --resolution-only --ignore-scripts --no-frozen-lockfile
|
||||
image="$owner"
|
||||
docker buildx build --platform linux/amd64 --target runner-provider-pack \
|
||||
--build-arg "PAPERCLIP_BUILD_COMMIT=$SOURCE_SHA" \
|
||||
--label "org.opencontainers.image.revision=$SOURCE_SHA" \
|
||||
--cache-from type=registry,ref=ghcr.io/paperclipai/paperclip:buildcache-amd64 \
|
||||
--tag "$image" --load .
|
||||
pack_path=/provider-pack/provider-pack.json
|
||||
docker image inspect "$image" > "$RUNNER_TEMP/runner-release-input/image.json"
|
||||
daemon_path=/app/server/dist/vendor/paperclip-runner/bin/paperclip-runnerd
|
||||
docker create --name "$owner" --network none --user 1000:1000 --read-only \
|
||||
--cap-drop ALL --security-opt no-new-privileges \
|
||||
--entrypoint "$daemon_path" "$image" --build-metadata >/dev/null
|
||||
docker cp "$owner:$pack_path" "$RUNNER_TEMP/runner-release-input/provider-pack.json"
|
||||
docker cp "$owner:$daemon_path" "$RUNNER_TEMP/runner-release-input/paperclip-runnerd"
|
||||
# Execute only inside the source image. Its libc may be newer than
|
||||
# ubuntu-latest; host execution would not prove this image artifact.
|
||||
timeout 30s docker start --attach "$owner" > "$RUNNER_TEMP/runner-release-input/daemon-metadata.json"
|
||||
test "$(docker inspect --format '{{.State.ExitCode}}' "$owner")" = 0
|
||||
node scripts/release-runner-artifacts.mjs record-image-binary "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-input/paperclip-runnerd" "$RUNNER_TEMP/runner-release-input/daemon-metadata.json" \
|
||||
"$RUNNER_TEMP/runner-release-input/image.json" "$RUNNER_TEMP/runner-release/linux-x64"
|
||||
node scripts/release-runner-artifacts.mjs record-pack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-input/provider-pack.json" "$RUNNER_TEMP/runner-release-input/image.json" \
|
||||
"$RUNNER_TEMP/runner-release/provider-pack"
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: runner-release-${{ inputs.ref }}-${{ github.run_id }}-${{ github.run_attempt }}-provider-pack
|
||||
path: ${{ runner.temp }}/runner-release/
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
runner_release_assemble:
|
||||
if: inputs.runner_assets_only
|
||||
name: Assemble verified release runner data
|
||||
needs: [runner_release_binaries, runner_release_pack]
|
||||
permissions:
|
||||
contents: read
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
artifact_name: ${{ steps.identity.outputs.artifact_name }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
|
||||
with:
|
||||
node-version: 24
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: runner-release-${{ inputs.ref }}-${{ github.run_id }}-${{ github.run_attempt }}-*
|
||||
path: ${{ runner.temp }}/runner-release-inputs
|
||||
merge-multiple: true
|
||||
- name: Run the sole source-aware assembler and bound its data envelope
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node scripts/release-runner-artifacts.mjs assemble "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-inputs" "$RUNNER_TEMP/runner-release-assembled"
|
||||
- name: Record run-bound assembled artifact
|
||||
id: identity
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
run: echo "artifact_name=runner-release-assembled-$SOURCE_SHA-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT" >> "$GITHUB_OUTPUT"
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: ${{ steps.identity.outputs.artifact_name }}
|
||||
path: |
|
||||
${{ runner.temp }}/runner-release-assembled/runner-release-assets.tar.gz
|
||||
${{ runner.temp }}/runner-release-assembled/runner-release-assets.tar.gz.sha256
|
||||
if-no-files-found: error
|
||||
retention-days: 7
|
||||
|
||||
runner_release_request:
|
||||
if: inputs.runner_assets_only
|
||||
name: Validate immutable runner producer inputs
|
||||
permissions: {}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- name: Reject mutable sources before any producer checkout
|
||||
env:
|
||||
SOURCE_SHA: ${{ inputs.ref }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[[ "$SOURCE_SHA" =~ ^[a-f0-9]{40}$ ]]
|
||||
@@ -337,8 +337,11 @@ jobs:
|
||||
run: node scripts/cloud-source-verification.mjs "$SOURCE_SHA"
|
||||
|
||||
publish_canary:
|
||||
if: github.event_name == 'push'
|
||||
needs: verify_canary
|
||||
if: >-
|
||||
!cancelled() && github.event_name == 'push' && needs.verify_canary.result == 'success' &&
|
||||
needs.pin_runner_release_source.result == 'success' &&
|
||||
(needs.pin_runner_release_source.outputs.required != 'true' || needs.release_runner_assets.result == 'success')
|
||||
needs: [verify_canary, pin_runner_release_source, release_runner_assets]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 150
|
||||
environment: npm-canary
|
||||
@@ -350,10 +353,14 @@ jobs:
|
||||
# For the explicit docker.yml dispatch below.
|
||||
actions: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: source
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup pnpm
|
||||
@@ -366,6 +373,33 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: source/pnpm-lock.yaml
|
||||
|
||||
- name: Checkout trusted release transfer tools
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: trusted
|
||||
persist-credentials: false
|
||||
- name: Download this run's verified runner data
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ needs.release_runner_assets.outputs.runner_assets_artifact }}
|
||||
path: ${{ runner.temp }}/runner-release-archive
|
||||
- name: Validate runner data against the selected source before publishing
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node "$GITHUB_WORKSPACE/trusted/scripts/release-runner-artifacts.mjs" unpack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz.sha256" \
|
||||
"$RUNNER_TEMP/runner-release-assets" "$GITHUB_WORKSPACE/source"
|
||||
echo "PAPERCLIP_RELEASE_RUNNER_ASSETS=$RUNNER_TEMP/runner-release-assets" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Validate release package manifest
|
||||
run: node ./scripts/release-package-map.mjs check
|
||||
@@ -587,11 +621,13 @@ jobs:
|
||||
artifact_name: nightly-release-smoke
|
||||
|
||||
publish_nightly:
|
||||
needs: [select_nightly, smoke_nightly]
|
||||
needs: [select_nightly, smoke_nightly, pin_runner_release_source, release_runner_assets]
|
||||
# Publish when smoke passed, or when smoke was deliberately skipped by a
|
||||
# dry-run dispatch (the publish itself is a dry-run in that case).
|
||||
if: >-
|
||||
!cancelled() &&
|
||||
needs.pin_runner_release_source.result == 'success' &&
|
||||
(needs.pin_runner_release_source.outputs.required != 'true' || needs.release_runner_assets.result == 'success') &&
|
||||
needs.select_nightly.outputs.proceed == 'true' &&
|
||||
(needs.smoke_nightly.result == 'success' ||
|
||||
(needs.smoke_nightly.result == 'skipped' && github.event_name == 'workflow_dispatch' && inputs.dry_run))
|
||||
@@ -611,10 +647,14 @@ jobs:
|
||||
id-token: write
|
||||
actions: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: source
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: source
|
||||
ref: ${{ needs.select_nightly.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -628,6 +668,33 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: source/pnpm-lock.yaml
|
||||
|
||||
- name: Checkout trusted release transfer tools
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: trusted
|
||||
persist-credentials: false
|
||||
- name: Download this run's verified runner data
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ needs.release_runner_assets.outputs.runner_assets_artifact }}
|
||||
path: ${{ runner.temp }}/runner-release-archive
|
||||
- name: Validate runner data against the selected source before publishing
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node "$GITHUB_WORKSPACE/trusted/scripts/release-runner-artifacts.mjs" unpack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz.sha256" \
|
||||
"$RUNNER_TEMP/runner-release-assets" "$GITHUB_WORKSPACE/source"
|
||||
echo "PAPERCLIP_RELEASE_RUNNER_ASSETS=$RUNNER_TEMP/runner-release-assets" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Validate release package manifest
|
||||
run: node ./scripts/release-package-map.mjs check
|
||||
@@ -855,9 +922,11 @@ jobs:
|
||||
ref: ${{ needs.select_beta.outputs.sha }}
|
||||
|
||||
publish_beta:
|
||||
needs: [select_beta, verify_beta_candidate]
|
||||
needs: [select_beta, verify_beta_candidate, pin_runner_release_source, release_runner_assets]
|
||||
if: >-
|
||||
!cancelled() &&
|
||||
needs.pin_runner_release_source.result == 'success' &&
|
||||
(needs.pin_runner_release_source.outputs.required != 'true' || needs.release_runner_assets.result == 'success') &&
|
||||
needs.select_beta.result == 'success' &&
|
||||
(needs.verify_beta_candidate.result == 'success' ||
|
||||
(needs.verify_beta_candidate.result == 'skipped' && needs.select_beta.outputs.mode == 'promote'))
|
||||
@@ -877,10 +946,14 @@ jobs:
|
||||
outputs:
|
||||
beta_version: ${{ steps.result.outputs.beta_version }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: source
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: source
|
||||
ref: ${{ needs.select_beta.outputs.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -894,6 +967,33 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: source/pnpm-lock.yaml
|
||||
|
||||
- name: Checkout trusted release transfer tools
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: trusted
|
||||
persist-credentials: false
|
||||
- name: Download this run's verified runner data
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ needs.release_runner_assets.outputs.runner_assets_artifact }}
|
||||
path: ${{ runner.temp }}/runner-release-archive
|
||||
- name: Validate runner data against the selected source before publishing
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node "$GITHUB_WORKSPACE/trusted/scripts/release-runner-artifacts.mjs" unpack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz.sha256" \
|
||||
"$RUNNER_TEMP/runner-release-assets" "$GITHUB_WORKSPACE/source"
|
||||
echo "PAPERCLIP_RELEASE_RUNNER_ASSETS=$RUNNER_TEMP/runner-release-assets" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Validate release package manifest
|
||||
run: node ./scripts/release-package-map.mjs check
|
||||
@@ -1240,17 +1340,24 @@ jobs:
|
||||
ref: ${{ needs.preflight_stable.outputs.sha }}
|
||||
|
||||
preview_stable:
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.channel == 'stable' && inputs.dry_run
|
||||
needs: [preflight_stable, verify_stable]
|
||||
if: >-
|
||||
!cancelled() && github.event_name == 'workflow_dispatch' && inputs.channel == 'stable' && inputs.dry_run &&
|
||||
needs.verify_stable.result == 'success' && needs.pin_runner_release_source.result == 'success' &&
|
||||
(needs.pin_runner_release_source.outputs.required != 'true' || needs.release_runner_assets.result == 'success')
|
||||
needs: [preflight_stable, verify_stable, pin_runner_release_source, release_runner_assets]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: source
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
ref: ${{ needs.preflight_stable.outputs.sha }}
|
||||
|
||||
@@ -1264,6 +1371,33 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: source/pnpm-lock.yaml
|
||||
|
||||
- name: Checkout trusted release transfer tools
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: trusted
|
||||
persist-credentials: false
|
||||
- name: Download this run's verified runner data
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ needs.release_runner_assets.outputs.runner_assets_artifact }}
|
||||
path: ${{ runner.temp }}/runner-release-archive
|
||||
- name: Validate runner data against the selected source before publishing
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node "$GITHUB_WORKSPACE/trusted/scripts/release-runner-artifacts.mjs" unpack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz.sha256" \
|
||||
"$RUNNER_TEMP/runner-release-assets" "$GITHUB_WORKSPACE/source"
|
||||
echo "PAPERCLIP_RELEASE_RUNNER_ASSETS=$RUNNER_TEMP/runner-release-assets" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Validate release package manifest
|
||||
run: node ./scripts/release-package-map.mjs check
|
||||
@@ -1282,8 +1416,11 @@ jobs:
|
||||
./scripts/release.sh "${args[@]}"
|
||||
|
||||
publish_stable:
|
||||
if: github.event_name == 'workflow_dispatch' && inputs.channel == 'stable' && !inputs.dry_run
|
||||
needs: [preflight_stable, verify_stable]
|
||||
if: >-
|
||||
!cancelled() && github.event_name == 'workflow_dispatch' && inputs.channel == 'stable' && !inputs.dry_run &&
|
||||
needs.verify_stable.result == 'success' && needs.pin_runner_release_source.result == 'success' &&
|
||||
(needs.pin_runner_release_source.outputs.required != 'true' || needs.release_runner_assets.result == 'success')
|
||||
needs: [preflight_stable, verify_stable, pin_runner_release_source, release_runner_assets]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 150
|
||||
environment: npm-stable
|
||||
@@ -1294,10 +1431,14 @@ jobs:
|
||||
outputs:
|
||||
stable_version: ${{ steps.tag.outputs.version }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: source
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: source
|
||||
fetch-depth: 0
|
||||
ref: ${{ needs.preflight_stable.outputs.sha }}
|
||||
|
||||
@@ -1311,6 +1452,33 @@ jobs:
|
||||
with:
|
||||
node-version: 24
|
||||
cache: pnpm
|
||||
cache-dependency-path: source/pnpm-lock.yaml
|
||||
|
||||
- name: Checkout trusted release transfer tools
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: trusted
|
||||
persist-credentials: false
|
||||
- name: Download this run's verified runner data
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: ${{ needs.release_runner_assets.outputs.runner_assets_artifact }}
|
||||
path: ${{ runner.temp }}/runner-release-archive
|
||||
- name: Validate runner data against the selected source before publishing
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
env:
|
||||
SOURCE_SHA: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
|
||||
node "$GITHUB_WORKSPACE/trusted/scripts/release-runner-artifacts.mjs" unpack "$SOURCE_SHA" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz" \
|
||||
"$RUNNER_TEMP/runner-release-archive/runner-release-assets.tar.gz.sha256" \
|
||||
"$RUNNER_TEMP/runner-release-assets" "$GITHUB_WORKSPACE/source"
|
||||
echo "PAPERCLIP_RELEASE_RUNNER_ASSETS=$RUNNER_TEMP/runner-release-assets" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
@@ -1477,3 +1645,63 @@ jobs:
|
||||
echo "- Open and merge the PR (a human opens it so CI runs):"
|
||||
echo " https://github.com/${GITHUB_REPOSITORY}/compare/master...${branch}?expand=1"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
# Resolve one applicable release channel after its existing gate. Optional
|
||||
# channels are deliberately skipped, so use explicit results, never an
|
||||
# implicit all-needs-success dependency that would suppress promotions.
|
||||
pin_runner_release_source:
|
||||
needs: [verify_canary, select_nightly, select_beta, verify_beta_candidate, preflight_stable, verify_stable]
|
||||
if: >-
|
||||
!cancelled() &&
|
||||
((github.event_name == 'push' && needs.verify_canary.result == 'success') ||
|
||||
(needs.select_nightly.result == 'success' && needs.select_nightly.outputs.proceed == 'true') ||
|
||||
(needs.select_beta.result == 'success' &&
|
||||
(needs.verify_beta_candidate.result == 'success' || needs.select_beta.outputs.mode == 'promote')) ||
|
||||
(needs.preflight_stable.result == 'success' && needs.verify_stable.result == 'success'))
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
outputs:
|
||||
source_sha: ${{ steps.pin.outputs.source_sha }}
|
||||
required: ${{ steps.pin.outputs.required }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
- name: Pin the gated source and identify its release assembly contract
|
||||
id: pin
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
CANARY_SHA: ${{ github.sha }}
|
||||
NIGHTLY_SHA: ${{ needs.select_nightly.outputs.sha }}
|
||||
BETA_SHA: ${{ needs.select_beta.outputs.sha }}
|
||||
STABLE_SHA: ${{ needs.preflight_stable.outputs.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ "$EVENT_NAME" == push ]]; then source_sha="$CANARY_SHA";
|
||||
elif [[ -n "$NIGHTLY_SHA" ]]; then source_sha="$NIGHTLY_SHA";
|
||||
elif [[ -n "$BETA_SHA" ]]; then source_sha="$BETA_SHA";
|
||||
else source_sha="$STABLE_SHA"; fi
|
||||
[[ "$source_sha" =~ ^[a-f0-9]{40}$ ]]
|
||||
git cat-file -e "$source_sha^{commit}"
|
||||
required=false
|
||||
# Promoted older sources retain their original contract. New sources
|
||||
# opt in with the checked-in hook and may never publish partial assets.
|
||||
if git show "$source_sha:scripts/release.sh" | grep -F 'PAPERCLIP_RELEASE_RUNNER_ASSETS' >/dev/null; then
|
||||
git cat-file -e "$source_sha:scripts/release-runner-artifacts.mjs"
|
||||
required=true
|
||||
fi
|
||||
echo "source_sha=$source_sha" >> "$GITHUB_OUTPUT"
|
||||
echo "required=$required" >> "$GITHUB_OUTPUT"
|
||||
|
||||
release_runner_assets:
|
||||
needs: pin_runner_release_source
|
||||
if: needs.pin_runner_release_source.outputs.required == 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
uses: ./.github/workflows/release-verify.yml
|
||||
with:
|
||||
ref: ${{ needs.pin_runner_release_source.outputs.source_sha }}
|
||||
runner_assets_only: true
|
||||
Reference in new issue
Block a user