Skip to content

feat(cli): upload fork PRs on v2 by swapping the job's GITHUB_TOKEN - #1293

Merged
trunk-io[bot] merged 2 commits into
mainfrom
tylerbeebe/trunk-23482-analytics-cli-upload-fork-prs-on-v2
Nov 9, 2026
Merged

trunk-io[bot] merged 2 commits into
mainfrom
tylerbeebe/trunk-23482-analytics-cli-upload-fork-prs-on-v2

Conversation

@acatxnamedvirtue

@acatxnamedvirtue acatxnamedvirtue commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

On GitHub Actions, an upload of a fork pull request with --allow-forked-pr-uploads, no token, no public repo id and a test collection id can now upload over Trunk API v2. Until now it always used v1, and 1.0.0 is v2-only.

How it works

  • The run asks the decision route the way v1's collection lane does: the x-trunk-allow-forked-pr-uploads header and no credential (trunk-io/trunk#35089).
  • On v2 it swaps the job's GITHUB_TOKEN, the PR number and pull_request.head.sha at POST /v2/auth/github-actions/login. This is the same lane impacted targets uses, live in trunk2 since #7717/#7718.
  • It then sends that login on x-trunk-token, never Authorization, on the upload, quarantine-status and all-quarantined-tests calls.
  • Any failure keeps the run on v1's collection lane, as today: no GITHUB_TOKEN, or a refused or unreadable swap. It logs one warning with the HTTP status and the server's error code. The GITHUB_TOKEN and the login are never logged.

What does not change

  • Runs with a token or a public repo id.
  • Credential-free runs that are not a GitHub Actions fork PR (for example protected-branch runs, or a CircleCI fork build). They still never ask the decision route and stay on v1. They need a v2 API key before v1 retires.

Out of scope: test-runner plugins, login caching, renewal and a separate 429 retry.

The v2 client's credential is now a small enum: an API key on Authorization, or a Trunk login on x-trunk-token. Accepting a Trunk JWT for --token in 1.0.0 (TRUNK-23372) can reuse it.

The --allow-forked-pr-uploads help now says it works with no credential on v1, and on v2 only for a fork PR on GitHub Actions, using the job's GITHUB_TOKEN. analytics-uploader passes it by default (trunk-io/analytics-uploader#155); when running the CLI directly, set GITHUB_TOKEN: ${{ github.token }}.

Release: this gets cherry-picked onto 0.17.x for 0.17.0. The cherry-pick has one test-file conflict (main's newer log-routing tests), resolved by keeping only the fork tests; there the fork tests assert logs on stdout, since 0.17.x does not route logs to stderr.

Tests

  • API client: the exact swap body; the login on x-trunk-token with no Authorization; refusals (400/401/422/502/503/429/unreadable) falling back to v1; a missing GITHUB_TOKEN; a decision of v1 or 401 meaning no swap; a non-fork run never asking; a token run never swapping.
  • End-to-end CLI tests on the same cases, including that verbose logs contain neither the token nor the login.
  • Locally everything passes except 3 Swift xunit tests that need a Swift language server.

Part of TRUNK-23482

🤖 Generated with Claude Code

@trunk-io

trunk-io Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@acatxnamedvirtue
acatxnamedvirtue force-pushed the tylerbeebe/trunk-23482-analytics-cli-upload-fork-prs-on-v2 branch from c9773be to d2d0306 Compare November 6, 2026 18:41
@acatxnamedvirtue acatxnamedvirtue changed the title feat(cli): upload fork PRs on v2 by exchanging the job's GITHUB_TOKEN feat(cli): upload forked-PR runs on v2 with the collection's login Nov 6, 2026
@acatxnamedvirtue
acatxnamedvirtue force-pushed the tylerbeebe/trunk-23482-analytics-cli-upload-fork-prs-on-v2 branch from d2d0306 to ebc98d7 Compare November 6, 2026 19:10
@acatxnamedvirtue acatxnamedvirtue changed the title feat(cli): upload forked-PR runs on v2 with the collection's login feat(cli): upload fork PRs on v2 by swapping the job's GITHUB_TOKEN Nov 6, 2026
Comment thread api/src/client/v2.rs Resolved
Comment thread api/src/client/v2.rs Resolved
acatxnamedvirtue and others added 2 commits November 9, 2026 17:46
On GitHub Actions, an `upload` for a pull request from a fork with
--allow-forked-pr-uploads, no token, no public repo id and a test collection
id now asks the decision route with v1's collection-lane header and no
credential. On v2 it exchanges the job's GITHUB_TOKEN, the PR number and
pull_request.head.sha at POST /v2/auth/github-actions/login, the lane
impacted targets uses, and sends the login on x-trunk-token, never
Authorization, on the upload, quarantine-status and all-quarantined-tests
calls.

Any failure keeps the run on v1's collection lane, as today: a missing
GITHUB_TOKEN, or a refused or unreadable exchange, logs a warning with the
status and the server's error code. Neither the GITHUB_TOKEN nor the login is
logged.

The v2 client's credential is now a small enum: an API key on Authorization
or a Trunk login on x-trunk-token. Runs with a token or a public repo id, and
credential-free runs that are not a GitHub Actions fork PR, are unchanged:
the latter still never ask the decision route.

TRUNK-23482

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on the swap

Review follow-up. use_upload_api_version's doc now says it takes the
decision route's answer as given, overriding it only when a fork PR's
GITHUB_TOKEN swap fails. The swap request carries
x-trunk-allow-forked-pr-uploads like the rest of the fork-lane traffic,
and the mock login route checks for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@acatxnamedvirtue
acatxnamedvirtue force-pushed the tylerbeebe/trunk-23482-analytics-cli-upload-fork-prs-on-v2 branch from 9e50900 to 5a5757f Compare November 9, 2026 17:47
@acatxnamedvirtue

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io
trunk-io Bot merged commit 46eeea6 into main Nov 9, 2026
23 checks passed
acatxnamedvirtue added a commit that referenced this pull request Nov 9, 2026
…1293)

* feat(cli): upload fork PRs on v2 by swapping the job's GITHUB_TOKEN

On GitHub Actions, an `upload` for a pull request from a fork with
--allow-forked-pr-uploads, no token, no public repo id and a test collection
id now asks the decision route with v1's collection-lane header and no
credential. On v2 it exchanges the job's GITHUB_TOKEN, the PR number and
pull_request.head.sha at POST /v2/auth/github-actions/login, the lane
impacted targets uses, and sends the login on x-trunk-token, never
Authorization, on the upload, quarantine-status and all-quarantined-tests
calls.

Any failure keeps the run on v1's collection lane, as today: a missing
GITHUB_TOKEN, or a refused or unreadable exchange, logs a warning with the
status and the server's error code. Neither the GITHUB_TOKEN nor the login is
logged.

The v2 client's credential is now a small enum: an API key on Authorization
or a Trunk login on x-trunk-token. Runs with a token or a public repo id, and
credential-free runs that are not a GitHub Actions fork PR, are unchanged:
the latter still never ask the decision route.

TRUNK-23482

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(api): say the decision route is trusted, and send the fork opt-in on the swap

Review follow-up. use_upload_api_version's doc now says it takes the
decision route's answer as given, overriding it only when a fork PR's
GITHUB_TOKEN swap fails. The swap request carries
x-trunk-allow-forked-pr-uploads like the rest of the fork-lane traffic,
and the mock login route checks for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 46eeea6)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants