ci(hermes): provision pinned runtime for selected paid campaigns

Select candidate assets consistently for immutable image identity and remote packs; provision local Linux assets before the protected paid step exposes credentials. Preserve default-branch dispatch and qualification gates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-10-08 12:28:11 -05:00
1 parent 4a0b327727
commit 5c2a78fa45
4 files changed
+70 -7

No files matched your search

+28 -7
View File
@@ -223,6 +223,7 @@ jobs:
needs_runner_typescript: ${{ steps.catalog.outputs.needs_runner_typescript }}
needs_native_binaries: ${{ steps.catalog.outputs.needs_native_binaries }}
needs_remote_provider_pack: ${{ steps.catalog.outputs.needs_remote_provider_pack }}
candidate_providers: ${{ steps.catalog.outputs.candidate_providers }}
execution_ids: ${{ steps.catalog.outputs.execution_ids }}
max_parallel: ${{ steps.catalog.outputs.max_parallel }}
daytona_image_content_id: ${{ steps.daytona_image_content.outputs.content_id }}
@@ -271,12 +272,6 @@ jobs:
- run: pnpm install --frozen-lockfile
# The v2 contract fails closed unless every Docker FROM is digest-pinned,
# and hashes those exact base references into the immutable image tag.
- name: Compute Daytona image content ID with pinned bases
id: daytona_image_content
run: echo "content_id=$(pnpm --silent test:e2e:runner:image-id)" >> "$GITHUB_OUTPUT"
- name: Validate selectors and emit matrix
id: catalog
env:
@@ -341,6 +336,7 @@ jobs:
echo "needs_runner_typescript=$(jq -r '[.include[] | select((.profileId == "runner-opencode") or (.profileId | startswith("runner-acpx-")) or (.suiteId == "openrouter-model-breadth"))] | length > 0' <<< "$catalog_json")"
echo "needs_native_binaries=$(jq -r '[.include[] | select((.profileId | startswith("runner-")) or (.suiteId == "openrouter-model-breadth"))] | length > 0' <<< "$catalog_json")"
echo "needs_remote_provider_pack=$(jq -r '[.include[] | select((.environmentId == "daytona") and ((.profileId == "runner-opencode") or (.profileId | startswith("runner-acpx-"))))] | length > 0' <<< "$catalog_json")"
echo "candidate_providers=$(jq -r 'if any(.include[]; .profileId == "runner-acpx-hermes") then "hermes" else "" end' <<< "$catalog_json")"
echo "execution_ids=$(jq -c '.executionIds' <<< "$catalog_json")"
} >> "$GITHUB_OUTPUT"
if ! [[ "$MAX_PARALLEL_LIMIT" =~ ^[1-9][0-9]*$ ]] || [ "$MAX_PARALLEL_LIMIT" -gt 100 ]; then
@@ -360,6 +356,14 @@ jobs:
fi
echo "max_parallel=$MAX_PARALLEL" >> "$GITHUB_OUTPUT"
# Exact optional provider assets are part of the immutable image identity.
# Selecting assets never promotes a pending profile to qualified.
- name: Compute Daytona image content ID with pinned bases
id: daytona_image_content
env:
CANDIDATE_PROVIDERS: ${{ steps.catalog.outputs.candidate_providers }}
run: echo "content_id=$(pnpm --silent test:e2e:runner:image-id "--candidate-providers=$CANDIDATE_PROVIDERS")" >> "$GITHUB_OUTPUT"
daytona_image:
name: Publish verified Daytona image
needs: [authorize, target_lock, catalog]
@@ -433,6 +437,7 @@ jobs:
IMAGE_CACHE: ghcr.io/paperclipai/paperclip-daytona-runner:e2e-buildcache-amd64
TARGET_SHA: ${{ needs.authorize.outputs.target_sha }}
TARGET_LOCK_SHA256: ${{ needs.target_lock.outputs.lock_sha256 }}
CANDIDATE_PROVIDERS: ${{ needs.catalog.outputs.candidate_providers }}
TARGET_REF: ${{ needs.authorize.outputs.target_ref }}
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: |
@@ -467,6 +472,7 @@ jobs:
--build-arg "PAPERCLIP_RUNNER_CONTENT_ID=${IMAGE_CONTENT_ID}" \
--build-arg "PAPERCLIP_RUNNER_SOURCE_REVISION=${TARGET_SHA}" \
--build-arg "PAPERCLIP_RUNNER_LOCK_SHA256=${TARGET_LOCK_SHA256}" \
--build-arg "PAPERCLIP_RUNNER_CANDIDATE_PROVIDERS=${CANDIDATE_PROVIDERS}" \
--file docker/daytona-runner/Dockerfile \
--tag "$IMAGE_TAG" \
"${cache_args[@]}" \
@@ -721,6 +727,13 @@ jobs:
test -d packages/paperclip-eval-kernel/dist
test -d packages/paperclip-runner/dist
- name: Provision pinned Hermes assets before assembling the remote pack
if: needs.catalog.outputs.needs_remote_provider_pack == 'true' && needs.catalog.outputs.candidate_providers == 'hermes'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends python3-venv
bash tests/runner-e2e/provision-hermes-linux.sh
- name: Assemble native remote provider pack
if: needs.catalog.outputs.needs_remote_provider_pack == 'true'
env:
@@ -728,7 +741,8 @@ jobs:
# content ID. Matching that revision lets remote execution reuse the
# verified pack already installed in the immutable image.
PAPERCLIP_RUNNER_SOURCE_REVISION: ${{ needs.daytona_image.outputs.source_revision }}
run: node packages/paperclip-runner/scripts/build-provider-pack.mjs packages/paperclip-runner/provider-pack
CANDIDATE_PROVIDERS: ${{ needs.catalog.outputs.candidate_providers }}
run: node packages/paperclip-runner/scripts/build-provider-pack.mjs packages/paperclip-runner/provider-pack "--candidate-providers=$CANDIDATE_PROVIDERS"
- name: Package verified remote provider pack
if: needs.catalog.outputs.needs_remote_provider_pack == 'true'
@@ -890,6 +904,13 @@ jobs:
if: matrix.environmentId == 'local' && (matrix.profileId == 'legacy-opencode' || matrix.profileId == 'runner-opencode' || matrix.suiteId == 'openrouter-model-breadth')
run: node packages/paperclip-runner/scripts/materialize-opencode-binary.mjs
- name: Provision pinned Hermes before the paid local test
if: matrix.environmentId == 'local' && matrix.profileId == 'runner-acpx-hermes'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends python3-venv bubblewrap
bash tests/runner-e2e/provision-hermes-linux.sh
- name: Install checksum-verified Grok executable
if: matrix.environmentId == 'local' && (matrix.profileId == 'runner-acpx-grok' || matrix.profileId == 'runner-acpx-grok-subscription')
run: sudo node packages/paperclip-runner/scripts/provision-grok.mjs /opt/paperclip/providers/grok/1.0.13/grok