Skip to content

feat(upload): print a short link to the upload when a collection id is passed - #1168

Merged
trunk-io[bot] merged 2 commits into
mainfrom
tyler/collection-upload-short-link
Aug 27, 2026
Merged

feat(upload): print a short link to the upload when a collection id is passed#1168
trunk-io[bot] merged 2 commits into
mainfrom
tyler/collection-upload-short-link

Conversation

@TylerJang27

Copy link
Copy Markdown
Collaborator

Summary

The end-of-run 🏷️ Bundle Upload ID line becomes a link when the run is in test-collection link mode:

🏷️  Bundle Upload: https://app.trunk.io/{org}/flaky-tests/collections/{short_id}/u/{bundle_meta_id}

The webapp resolves the upload's createdAt from the id and redirects to the canonical uploads page. No RPC or response changesCreateBundleUploadResponse.test_collection_bundle_meta_id already carries the id, and context.rs already threads it onto base_props.test_collection.

Unlike the per-test /t/ links from #1165, there is no ?repo= param: the collection short id fully scopes the webapp's lookup, so there is no repo name to resolve.

Companion webapp PR (must deploy first, it hosts the redirect page): trunk-io/trunk2#5440

Behavior

collection id passed --hide-test-collection-links / TRUNK_HIDE_TEST_COLLECTION_LINKS printed
yes no 🏷️ Bundle Upload: …/collections/{short_id}/u/{bundle_meta_id}
yes yes 🏷️ Bundle Upload ID: {id} (unchanged)
no 🏷️ Bundle Upload ID: {id} (unchanged)

Same gate as #1165, so the one flag controls both link kinds.

Note on the gate

collection_upload_url() reads test_collection_short_id rather than base_props.test_collection.short_id. An exported-but-blank TRUNK_TEST_COLLECTION_ID is Some(""), not None, so the latter yields a malformed /collections//u/… link — the same edge #1165 added a test for. The existing upload_bundle_without_test_collection_id_prints_repo_links test caught this.

upload_bundle's assertion changed: that test passes --test-collection-id tc_123, so by design it now gets the link rather than the bare id. The bare-id form stays covered by the new links-hidden test.

Test plan

  • cargo test -p api — URL unit test for the new form (20 passing)
  • cargo test -p trunk-analytics-cli — full suite green (53 + 23 + 13 + 9), including 2 new integration tests covering the gate matrix above
  • cargo fmt applied; cargo clippy findings unchanged from main (5 pre-existing in upload_command.rs before and after)

🤖 Generated with Claude Code

@trunk-io

trunk-io Bot commented Aug 26, 2026

Copy link
Copy Markdown

😎 This pull request was merged.

@codecov-commenter

codecov-commenter commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.36%. Comparing base (8c100a4) to head (ca5d99b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1168      +/-   ##
==========================================
+ Coverage   83.07%   83.36%   +0.28%     
==========================================
  Files          72       72              
  Lines       16313    16359      +46     
==========================================
+ Hits        13552    13637      +85     
+ Misses       2761     2722      -39     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@trunk-staging-io

trunk-staging-io Bot commented Aug 26, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

Failed Test Failure Summary Logs
pending_quarantine_test should be quarantined when run with variant A test marked as pending was expected to fail but unexpectedly passed. Logs ↗︎
variant_quarantine_test should be quarantined when run with variant A test expected the sum of 2 + 2 to be 5, but it was actually 4, indicating a failing assertion. Logs ↗︎

View Full Report ↗︎Docs

@trunk-io

trunk-io Bot commented Aug 26, 2026

Copy link
Copy Markdown

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

@TylerJang27
TylerJang27 marked this pull request as draft August 26, 2026 02:40
@TylerJang27
TylerJang27 marked this pull request as ready for review August 26, 2026 20:29
@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

Stacked PR 1169 failed testing in the merge queue. Please investigate the failure and re-submit the stack.

TylerJang27 and others added 2 commits August 27, 2026 14:42
…s passed

The end-of-run "Bundle Upload ID" line becomes a link when the run is in
test-collection link mode:

  🏷️  Bundle Upload: https://app.trunk.io/{org}/flaky-tests/collections/{short_id}/u/{bundle_meta_id}

The webapp resolves the upload's createdAt from the id and redirects to the
canonical uploads page (companion PR: trunk-io/trunk2#5440). Unlike the
per-test `/t/` links there is no `?repo=` param — the collection short id
fully scopes the lookup.

Gated exactly as the `/t/` links are: a collection id must be passed and
`--hide-test-collection-links` / `TRUNK_HIDE_TEST_COLLECTION_LINKS` must be
unset. Otherwise the bare bundle upload id prints as before.

The gate reads `test_collection_short_id` rather than
`base_props.test_collection.short_id` because an exported-but-blank
`TRUNK_TEST_COLLECTION_ID` is `Some("")`, not `None`, and would otherwise
produce a malformed `/collections//u/` link.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TylerJang27 added a commit that referenced this pull request Aug 27, 2026
…solve

Alternative to the short-link form in #1168, for comparison — see trunk-io/trunk2#5440.

The short link carries only the bundle_meta id, so the webapp has to recover
`bundle_meta_created_at` before it can address the upload, then redirect. That
lookup cannot use `test_collection_upload`'s primary key: it leads with
`(test_collection_id, repo_id, bundle_meta_created_at, …)` and a link carries no
repo, so with `repo_id` unconstrained the index resolves only the leading column
and every granule in the collection becomes a bloom-filter candidate. The 45-day
TTL means the query's own 45-day bound prunes almost nothing on top of that. It
measures as one granule on staging today only because staging collections fit in
one granule.

The CLI already has both halves — `CreateBundleUploadResponse` returns
`test_collection_bundle_meta_created_at` alongside the id, and both land in the
bundle — so it can emit the webapp's canonical `uploads/{bundleMetaKey}` URL
directly and skip the lookup and the redirect entirely.

Costs, stated plainly:

- The printed URL grows from 105 to 170 characters (+62%), which is the tradeoff
  the short-link design was chosen to avoid.
- It makes the CLI a second producer of the `bundleMetaKey` encoding, a frozen
  contract with `encodeBundleMetaKey` in trunk2. Pinned here by a golden vector
  generated from that function. Mitigating: `bundleMetaKey` is already a permanent
  public URL contract the webapp emits, and unlike a hash its drift is visible —
  it is base64 of a two-field JSON object.

Field order and whitespace match `JSON.stringify({ id, createdAt })` byte for
byte via a `Serialize` struct rather than a `format!`, so the golden test is
meaningful and an id containing a quote cannot break the payload. The webapp's
decoder parses JSON and is order-insensitive, so a drift would still resolve —
it would just stop matching the string the webapp emits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@TylerJang27
TylerJang27 force-pushed the tyler/collection-upload-short-link branch from ca5d99b to 0eeb6c4 Compare August 27, 2026 18:42
@trunk-io
trunk-io Bot merged commit 35e055b into main Aug 27, 2026
23 checks passed
@trunk-io

trunk-io Bot commented Aug 27, 2026

Copy link
Copy Markdown

This pull request was merged into main as part of stacked PR 1169.

trunk-io Bot pushed a commit that referenced this pull request Aug 27, 2026
…solve (#1169)

* feat(upload): print the canonical upload link, not a short link to resolve

Alternative to the short-link form in #1168, for comparison — see trunk-io/trunk2#5440.

The short link carries only the bundle_meta id, so the webapp has to recover
`bundle_meta_created_at` before it can address the upload, then redirect. That
lookup cannot use `test_collection_upload`'s primary key: it leads with
`(test_collection_id, repo_id, bundle_meta_created_at, …)` and a link carries no
repo, so with `repo_id` unconstrained the index resolves only the leading column
and every granule in the collection becomes a bloom-filter candidate. The 45-day
TTL means the query's own 45-day bound prunes almost nothing on top of that. It
measures as one granule on staging today only because staging collections fit in
one granule.

The CLI already has both halves — `CreateBundleUploadResponse` returns
`test_collection_bundle_meta_created_at` alongside the id, and both land in the
bundle — so it can emit the webapp's canonical `uploads/{bundleMetaKey}` URL
directly and skip the lookup and the redirect entirely.

Costs, stated plainly:

- The printed URL grows from 105 to 170 characters (+62%), which is the tradeoff
  the short-link design was chosen to avoid.
- It makes the CLI a second producer of the `bundleMetaKey` encoding, a frozen
  contract with `encodeBundleMetaKey` in trunk2. Pinned here by a golden vector
  generated from that function. Mitigating: `bundleMetaKey` is already a permanent
  public URL contract the webapp emits, and unlike a hash its drift is visible —
  it is base64 of a two-field JSON object.

Field order and whitespace match `JSON.stringify({ id, createdAt })` byte for
byte via a `Serialize` struct rather than a `format!`, so the golden test is
meaningful and an id containing a quote cannot break the payload. The webapp's
decoder parses JSON and is order-insensitive, so a drift would still resolve —
it would just stop matching the string the webapp emits.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(upload): pin the key to a verified golden; tighten comments

The golden vector now uses a case verified three ways: against a base64 of the
literal JSON, against trunk2's own `encodeBundleMetaKey`, and by decoding the
Rust output back (byte-identical, field order and no-whitespace included).
Confirmed load-bearing — swapping the struct's field order fails it.

The three url_for_upload cases shared five identical arguments; they go through
one `upload_url(created_at)` helper so each test shows only what it varies.
Comments trimmed to the non-obvious parts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

4 participants