Skip to content

Commit 55f48b3

Browse files
mikepitreclaude
andcommitted
ci(repo): publish the evidence a borrowed-device Expo session hands off
A cloud agent cannot attach a run's video and screenshots to its pull request: its sandbox refuses the upload, and the Actions token cannot attach. `verify-attach.yml` does it instead. It runs on `workflow_run` when a `verify-remote` session ends, so GitHub always runs the copy on the default branch. The job downloads the session's `verify-evidence` artifact and treats it as data. `scripts/verify-attach.mjs` reads the zip itself, refuses anything outside a fixed manifest schema, file names, sizes, counts, and leading bytes, and writes only files it validated. It then edits one marked block in the description with `gh pr edit --attach`. The line it writes says that the session reported the result and links the session's run. It publishes only to the open pull request of the session's branch, in this repository, and only when the commit the session started on and the commit the run was made at are both commits of that pull request. When the pull request has moved on, the line says so. The bot token is the secret `VERIFY_EVIDENCE_TOKEN` of the environment `verify-evidence`, which allows only the default branch. A repository secret could be read by a workflow on any branch. Only the step that runs `gh` holds the token. Without the secret the job prints a notice. GitHub starts this workflow only when it and `verify-remote.yml` are both on the default branch. The runs for one branch take turns at the description, and `queue: max` keeps every run that waits. The `Unit Tests` job of `ci.yml` runs the script's tests on its Node 24 leg. `.github/actionlint.yaml` ignores the `queue` key in this workflow, which actionlint 1.7 does not know. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 61c054c commit 55f48b3

6 files changed

Lines changed: 2367 additions & 0 deletions

File tree

‎.changeset/expo-verify-attach.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

‎.github/actionlint.yaml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ paths:
1717
ignore:
1818
- 'unexpected key "background" for step to run shell command'
1919
- 'step must run script with "run" section or run action with "uses" section'
20+
# actionlint 1.7.x also predates `concurrency.queue`, which keeps every waiting run of a group.
21+
# Remove once actionlint supports that key.
22+
.github/workflows/verify-attach.yml:
23+
ignore:
24+
- 'unexpected key "queue" for "concurrency" section'

‎.github/workflows/ci.yml‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,10 @@ jobs:
374374
env:
375375
NODE_VERSION: ${{ matrix.node-version }}
376376

377+
- name: Run verify-attach script tests
378+
if: matrix.node-version == '24.15.0'
379+
run: pnpm vitest run --root scripts verify-attach
380+
377381
- name: Upload Turbo Summary
378382
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
379383
if: ${{ env.TURBO_SUMMARIZE == 'true' }}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: verify-attach
2+
3+
on:
4+
workflow_run:
5+
workflows:
6+
- verify-remote
7+
types:
8+
- completed
9+
10+
permissions:
11+
actions: read
12+
contents: read
13+
14+
jobs:
15+
publish:
16+
name: Publish handed-off evidence
17+
if: >-
18+
github.event.workflow_run.event == 'workflow_dispatch' &&
19+
github.event.workflow_run.path == '.github/workflows/verify-remote.yml' &&
20+
github.event.workflow_run.head_repository.full_name == github.repository
21+
runs-on: ${{ vars.RUNNER_NORMAL || 'ubuntu-latest' }}
22+
timeout-minutes: 15
23+
environment: verify-evidence
24+
concurrency:
25+
group: verify-attach-${{ github.event.workflow_run.head_repository.full_name }}-${{ github.event.workflow_run.head_branch }}
26+
cancel-in-progress: false
27+
queue: max
28+
env:
29+
HAS_EVIDENCE_TOKEN: ${{ secrets.VERIFY_EVIDENCE_TOKEN != '' }}
30+
REPO: ${{ github.repository }}
31+
RUN_ID: ${{ github.event.workflow_run.id }}
32+
RUN_ACTOR: ${{ github.event.workflow_run.actor.login }}
33+
steps:
34+
- name: Say that no token is set
35+
if: env.HAS_EVIDENCE_TOKEN != 'true'
36+
run: echo "::notice title=verify-attach::The environment verify-evidence has no secret VERIFY_EVIDENCE_TOKEN, so no evidence is published."
37+
38+
- name: Checkout the trusted script
39+
if: env.HAS_EVIDENCE_TOKEN == 'true'
40+
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
41+
with:
42+
ref: ${{ github.sha }}
43+
sparse-checkout: scripts/verify-attach.mjs
44+
sparse-checkout-cone-mode: false
45+
persist-credentials: false
46+
47+
- name: Set up Node
48+
if: env.HAS_EVIDENCE_TOKEN == 'true'
49+
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
50+
with:
51+
node-version: 24
52+
53+
- name: Fetch and validate the evidence
54+
id: evidence
55+
if: env.HAS_EVIDENCE_TOKEN == 'true'
56+
env:
57+
GITHUB_TOKEN: ${{ github.token }}
58+
WORK: ${{ runner.temp }}/verify-attach
59+
RUN_EVENT: ${{ github.event.workflow_run.event }}
60+
RUN_PATH: ${{ github.event.workflow_run.path }}
61+
RUN_HEAD_REPO: ${{ github.event.workflow_run.head_repository.full_name }}
62+
run: node scripts/verify-attach.mjs fetch
63+
64+
- name: Install a gh whose pr edit has --attach
65+
if: steps.evidence.outputs.found == 'true'
66+
env:
67+
GH_VERSION: '2.102.0'
68+
GH_SHA256: bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386
69+
run: |
70+
set -euo pipefail
71+
curl -fsSL --max-time 300 --retry 3 --retry-delay 2 -o "$RUNNER_TEMP/gh.tar.gz" "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz"
72+
echo "$GH_SHA256 $RUNNER_TEMP/gh.tar.gz" | sha256sum -c -
73+
tar -xzf "$RUNNER_TEMP/gh.tar.gz" -C "$RUNNER_TEMP"
74+
echo "$RUNNER_TEMP/gh_${GH_VERSION}_linux_amd64/bin" >> "$GITHUB_PATH"
75+
76+
- name: Put the evidence in the pull request description
77+
if: steps.evidence.outputs.found == 'true'
78+
env:
79+
GH_TOKEN: ${{ secrets.VERIFY_EVIDENCE_TOKEN }}
80+
WORK: ${{ runner.temp }}/verify-attach
81+
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
82+
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
83+
run: node scripts/verify-attach.mjs publish

0 commit comments

Comments
 (0)