Skip to content

feat(proguard): add 'proguard upload' command (chunk-upload of R8/ProGuard mappings)#1074

Merged
BYK merged 6 commits into
mainfrom
feat/proguard-upload
Jun 5, 2026
Merged

feat(proguard): add 'proguard upload' command (chunk-upload of R8/ProGuard mappings)#1074
BYK merged 6 commits into
mainfrom
feat/proguard-upload

Conversation

@BYK

@BYK BYK commented Jun 5, 2026

Copy link
Copy Markdown
Member

Summary

Implements sentry proguard upload <path>... to upload Android ProGuard/R8 mapping files via the DIF chunk-upload protocol. Part of the parity effort tracked in #600.

Closes #1053

Changes

  • Extract shared chunk-upload infrastructure from sourcemaps.ts into new src/lib/api/chunk-upload.ts — schemas, types, codec selection, chunking, hashing, chunk upload, and assembly polling are now shared between sourcemap and proguard uploads
  • Add in-memory buffer chunking (hashBuffer, uploadMissingBufferChunks) — ProGuard mappings are chunked as raw bytes (no ZIP wrapping), matching the legacy sentry-cli DIF protocol
  • Add proguard upload API (src/lib/api/proguard.ts) — each mapping file's raw bytes are independently chunked and assembled via the DIF endpoint (projects/{org}/{project}/files/difs/assemble/), with all mappings in a single assemble request keyed by individual checksums
  • Add sentry proguard upload command (src/commands/proguard/upload.ts) with flags:
    • --uuid — force a specific UUID instead of computing from content (single file only)
    • --no-upload — dry-run mode: compute and print UUIDs without uploading (no auth needed)
    • --require-one — error if no mapping files provided
  • Refactor sourcemaps.ts to import from shared chunk-upload.ts with backward-compatible re-exports
  • Tests: 7 tests for buffer chunking, 11 tests for command behavior (validation, dry-run, upload orchestration)

Protocol Notes

The DIF upload protocol differs from artifact bundles (sourcemaps) in three ways:

  1. Raw bytes are chunked directly — no ZIP wrapping, no SYSB source bundle header
  2. Different endpoint: projects/{org}/{project}/files/difs/assemble/ (per-project, not per-org)
  3. Per-checksum keyed request/response format — each mapping file gets its own entry in the assemble body

This matches the legacy sentry-cli (Rust) implementation byte-for-byte.

…Guard mappings)

Implement `sentry proguard upload <path>...` to upload Android ProGuard/R8
mapping files via the DIF chunk-upload + assemble protocol.

## Changes

- Extract shared chunk-upload infrastructure from `sourcemaps.ts` into
  `chunk-upload.ts` (schemas, types, codec selection, chunking, hashing,
  upload, and assembly polling)
- Add `proguard.ts` API module for building proguard ZIP bundles
  (`proguard/<uuid>.txt` entries, no manifest) and uploading via
  the DIF assemble endpoint (`projects/{org}/{project}/files/difs/assemble/`)
- Add `proguard upload` command with flags:
  --uuid (force UUID), --no-upload (dry-run), --require-one,
  --no-reprocessing
- Refactor `sourcemaps.ts` to import from shared `chunk-upload.ts`
  with backward-compatible re-exports

Closes #1053
@BYK BYK self-assigned this Jun 5, 2026
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-1074/

Built to branch gh-pages at 2026-06-05 10:06 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Comment thread src/lib/api/proguard.ts
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

❌ Patch coverage is 40.74%. Project has 4846 uncovered lines.
❌ Project coverage is 81.36%. Comparing base (base) to head (head).

Files with missing lines (4)
File Patch % Lines
src/lib/api/chunk-upload.ts 26.92% ⚠️ 76 Missing
src/lib/api/proguard.ts 4.26% ⚠️ 45 Missing
src/commands/proguard/upload.ts 92.06% ⚠️ 5 Missing and 4 partials
src/lib/api/sourcemaps.ts 0.00% ⚠️ 2 Missing
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
- Coverage    81.54%    81.36%    -0.18%
==========================================
  Files          369       372        +3
  Lines        25845     25992      +147
  Branches     16888     16949       +61
==========================================
+ Hits         21074     21146       +72
- Misses        4771      4846       +75
- Partials      1770      1772        +2

Generated by Codecov Action

Comment thread src/lib/api/proguard.ts Outdated
Comment thread src/lib/api/proguard.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 53a8de3. Configure here.

Comment thread src/lib/api/proguard.ts Outdated
Comment thread src/lib/api/proguard.ts Outdated
BYK and others added 3 commits June 5, 2026 09:46
…file assemble, drop --no-reprocessing

- Upload raw mapping bytes directly (no ZIP wrapping, no SYSB header)
  matching the legacy sentry-cli DIF protocol
- Each mapping gets its own entry in the assemble request body,
  keyed by individual checksum with correct name/chunks
- Add hashBuffer() and uploadMissingBufferChunks() to chunk-upload.ts
  for in-memory buffer chunking
- Remove --no-reprocessing flag (not in legacy CLI for proguard)
- Extract checkAssembleResponse() helper to reduce cognitive complexity
- Update tests for new raw-byte approach
…entical content, import shared constants

- Set auth:false so --no-upload works without credentials
- Guard against empty mapping files with ValidationError
- Deduplicate mappings with identical content (same UUID) with warning
- Import ASSEMBLE_POLL_INTERVAL_MS/ASSEMBLE_MAX_WAIT_MS from chunk-upload.ts
- Remove unnecessary Buffer.from() copy in uploadBufferChunk
- Add logging to silent catch in uploadBufferChunk
- Extract deduplicateMappings() to keep func() under complexity limit
Comment thread src/lib/api/proguard.ts
Comment on lines +193 to +202
for (const cm of chunkedMappings) {
await uploadMissingBufferChunks({
chunks: cm.chunks,
missingChecksums,
content: cm.mapping.content,
serverOptions,
encoding,
regionUrl,
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The loop for uploading ProGuard mapping chunks is sequential (for...await) instead of parallel, slowing down uploads of multiple mapping files.
Severity: MEDIUM

Suggested Fix

To fix the performance issue, the sequential for...await loop should be replaced with a Promise.all() call. This would involve mapping over chunkedMappings and calling uploadMissingBufferChunks for each, allowing all uploads to run concurrently up to the concurrency limit. Example: await Promise.all(chunkedMappings.map((cm) => uploadMissingBufferChunks({...})));

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: src/lib/api/proguard.ts#L193-L202

Potential issue: The code at `src/lib/api/proguard.ts` uses a sequential `for...await`
loop to upload chunks for multiple ProGuard mappings. This contradicts a comment stating
the uploads happen "in parallel across all mappings". Each `uploadMissingBufferChunks`
call completes before the next one begins, processing each mapping file serially. This
underutilizes the available concurrency, leading to a performance degradation when a
user uploads multiple mapping files simultaneously. The total upload time is
unnecessarily extended because the uploads for different files do not run in parallel as
intended.

@BYK BYK enabled auto-merge (squash) June 5, 2026 10:11
@BYK BYK merged commit 65689e0 into main Jun 5, 2026
29 checks passed
@BYK BYK deleted the feat/proguard-upload branch June 5, 2026 10:14
Comment thread src/lib/api/proguard.ts
Comment on lines +231 to +232
const { data: pollResult } = await apiRequestToRegion<DifAssembleResponse>(
regionUrl,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing chunks reported during DIF assembly polling are never re-uploaded, leading to timeout

In pollDifAssembly, only allDone is destructured from checkAssembleResponse; the missingChecksums set is discarded. uploadProguardMappings uploads missing chunks exactly once (step 5) before entering the poll loop and never retries. If the server reports missingChunks in any poll response (e.g. a partially-failed initial upload, chunk eviction, or transient storage delay), those chunks are never re-uploaded; the loop spins until the 300 s deadline and then throws an ApiError(408) timeout instead of completing the upload. This is a robustness/correctness gap rather than a guaranteed failure, since it depends on the server reporting missing chunks mid-poll.

Evidence
  • checkAssembleResponse returns { allDone, missingChecksums }, populating missingChecksums from each entry's missingChunks whenever its state is not ok/created/error.
  • In pollDifAssembly the poll loop destructures only allDone: const { allDone } = checkAssembleResponse(pollResult, checksums, endpoint)missingChecksums is dropped, and there is no uploadMissingBufferChunks call inside the loop.
  • uploadProguardMappings step 5 calls uploadMissingBufferChunks once before invoking pollDifAssembly; no retry path exists if the server still reports chunks missing on a later poll.
  • On deadline expiry (ASSEMBLE_MAX_WAIT_MS = 300 000 ms) the function throws ApiError(... 408 ...) rather than re-sending the chunks.
Also found at 1 additional location
  • src/lib/api/chunk-upload.ts:243-246

Identified by Warden find-bugs · 9S8-2G9

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proguard: add 'proguard upload' (chunk-upload of R8/ProGuard mappings)

1 participant