Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
carrying an unresolvable debug-id `missing_sym`, which is what prompts an upload — an unstamped
bundle is silently unsymbolicated instead.

- **`debug-files upload --strip-sources-content`** (`--type sourcemaps`) — upload each map without
its embedded original source. `sourcesContent` carries your code verbatim, and it is what lets a
symbolicated crash show source lines; stripping it keeps file/line/column resolution and drops the
snippet, for teams who would rather their source did not leave the build machine.

The map on disk is never modified — only the copy that is uploaded — and the declared `hash`
describes the stripped bytes rather than the file that was read. A map that carries no
`sourcesContent` (or is not the JSON object we expect) is uploaded byte-for-byte unchanged: this
is a privacy preference, not a validator. An INDEXED map (spec §Index-Map) keeps its source inside
`sections[].map`, and those are stripped too — removing only the top-level key would have shipped
the source while reporting success. Rejected (exit 20) for every other `--type`, since no
other symbol format embeds source.

Measured on a real esbuild bundle: 253 → 181 bytes uploaded, local map untouched.

### Fixed
- **`debug-files upload --type sourcemaps --dry-run` no longer fails on a map that has no
debug-id.** A dry run sends nothing, so an un-keyed map cannot register the unfindable symbol the
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bugsee-cli debug-files upload <paths>... \
[--force] # re-upload even if the server already has it (dsym/pdb/rust/il2cpp-linemap/sourcemaps)
[--concurrency N] # sourcemaps only: ceiling on uploads in flight, 1..=32 (default: scaled)
[--allow-empty] # sourcemaps only: "nothing to upload" is success, not exit 10
[--strip-sources-content] # sourcemaps only: upload maps without the embedded source
[--dry-run]
```

Expand Down Expand Up @@ -221,15 +222,23 @@ rather than fatal there (`unkeyed` in the completion log) — the whole flow can
freshly built directory, where `sourcemaps inject --dry-run` has deliberately written nothing yet.
A REAL run still refuses such a map (exit 11): uploading it would register a symbol nothing can find.

`--strip-sources-content` uploads each map WITHOUT its `sourcesContent` — including the copies an
indexed map keeps inside `sections[].map` — for teams who would rather their source did not leave
the build machine. Symbolication still resolves file, line and column;
what is lost is the source snippet shown beside a crash frame. The map on disk is never modified —
only the copy that is uploaded — and the declared `hash` describes the stripped bytes. A map that
carries no `sourcesContent` is uploaded byte-for-byte unchanged.

`--allow-empty` turns "nothing to upload" into success (exit 0) instead of
exit 10 — a monorepo package built without maps, or a framework whose server
output has none, is a legitimate no-op rather than a reason to fail the build.
A path that does not exist is an error regardless (`path does not exist: <p>`,
exit 10) — including when other paths do hold maps — so a typo or a build that
never ran cannot half-upload a build's symbols.

Both flags apply to `--type sourcemaps` only, and are rejected (exit 20) for any
other type rather than accepted and ignored.
`--concurrency`, `--allow-empty` and `--strip-sources-content` apply to
`--type sourcemaps` only, and are rejected (exit 20) for any other type rather
than accepted and ignored.

`--exclude <glob>` (repeatable) keeps `inject` out of part of a build output — `--exclude
'**/node_modules/**'` leaves vendored third-party code inside a server bundle untouched, `--exclude
Expand All @@ -247,7 +256,7 @@ unsymbolicated instead. Measured on a stock `next build` with browser source map
```
bugsee-cli sourcemaps inject <paths>... [--exclude <glob>]... [--dry-run]
bugsee-cli debug-files upload --type sourcemaps <paths>... --version <v> --build <b> \
[--concurrency N] [--allow-empty]
[--concurrency N] [--allow-empty] [--strip-sources-content]
```

### `xcode upload-dsyms`
Expand Down
30 changes: 30 additions & 0 deletions scripts/e2e_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,36 @@ def main():
os.path.join(fix, "mapping.txt")] + v)
results["sourcemaps_upload"] = run(binpath, "sourcemaps", ["debug-files", "upload", "--type", "sourcemaps",
os.path.join(fix, "dist", "app.js.map")] + v)

# --strip-sources-content: the uploaded copy must not carry the source, and the file on disk
# must be untouched — it is the user's build output.
sc_map = os.path.join(fix, "with-sources", "app.js.map")
os.makedirs(os.path.dirname(sc_map), exist_ok=True)
with open(sc_map, "w") as f:
json.dump({"version": 3, "debug_id": "77777777-7777-7777-7777-777777777777",
"sources": ["app.ts"], "sourcesContent": ["const secret = 1;"],
"names": [], "mappings": "AAAA"}, f)
before = open(sc_map, "rb").read()
results["sourcemaps_strip_sources_content"] = run(
binpath, "sourcemaps_strip",
["debug-files", "upload", "--type", "sourcemaps", "--strip-sources-content", sc_map] + v)
try:
import zipfile as _zf
uploaded = None
for name in os.listdir(STATE["cap"]):
if name.startswith("sourcemaps_strip__") and name.endswith(".bin"):
with _zf.ZipFile(os.path.join(STATE["cap"], name)) as z:
uploaded = json.loads(z.read(z.namelist()[0]))
results["sourcemaps_strip_uploads_no_source"] = (
uploaded is not None
and "sourcesContent" not in uploaded
and uploaded.get("mappings") == "AAAA"
and open(sc_map, "rb").read() == before)
if not results["sourcemaps_strip_uploads_no_source"]:
print(f" [warn] uploaded={uploaded!r}")
except Exception as e:
print(" [warn] could not verify the strip flow:", e)
results["sourcemaps_strip_uploads_no_source"] = False
results["elf"] = run(binpath, "elf", ["debug-files", "upload", "--type", "elf",
os.path.join(fix, "native-debug-symbols.zip"),
"--uuid", "11111111-2222-3333-4444-555555555555"] + v)
Expand Down
Loading
Loading