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
3 changes: 3 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@
"MSVC",
"rustls",
"RUSTSEC",
"jsbundle",
"rustflags",
"sandboxing",
"libil"
]
}
1 change: 1 addition & 0 deletions docs/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ See [Configuration](/cli/configuration/) for details.
| `upload build-info` | Upload per-build metadata sidecars in one bundle. | [Builds](/cli/builds/#upload-build-info) |
| `pack` | Build the normalized upload ZIP locally, without uploading. | [Builds](/cli/builds/#pack) |
| `xcode post-action` | Run the whole iOS build-publish flow from an Xcode post-action. | [iOS build publishing](/cli/xcode/) |
| `xcode upload-dsyms` | Upload dSYMs from an Xcode Run Script build phase, with no build registration. | [iOS build publishing](/cli/xcode/#xcode-upload-dsyms) |
| `vcs-metadata` | Resolve VCS metadata (provider, commit, branch, PR, repo). | [Metadata resolvers](/cli/metadata/#vcs-metadata) |
| `ios-deps collect` | Collect the iOS dependency graph from lockfiles + linked frameworks. | [Metadata resolvers](/cli/metadata/#ios-deps-collect) |
| `build-env xcode-version` | Resolve the dotted Xcode version. | [Metadata resolvers](/cli/metadata/#build-env) |
Expand Down
82 changes: 80 additions & 2 deletions docs/cli/debug-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ relevant debug files for the chosen type.
| `proguard` *(default)* | Android R8 / ProGuard `mapping.txt` | The default when `--type` is unset. |
| `elf` | Native ELF symbols (Android NDK / Linux) | Also uploaded with a Breakpad transform. |
| `dsym` | Apple dSYM bundles | Recursive discovery + pre-upload UUID dedup. |
| `pdb` | Windows PDB files | Keyed by the PDB debug id (GUID + age). |
| `sourcemaps` | JavaScript source maps | See [Source maps](/cli/sourcemaps/). |
| `rust` | Rust (Cargo) build output | Discovers whichever format the target emitted. See [Rust](#rust-cargo). |
| `il2cpp-linemap` | Unity IL2CPP line-number mappings | See [Unity IL2CPP](#unity-il2cpp). |

Other types (`pe`, `pdb`, `portable-pdb`, `breakpad`, `jvm`, `wasm`,
`sourcebundle`) are recognised by the discovery layer but not yet processed.
Other types (`pe`, `portable-pdb`, `breakpad`, `jvm`, `wasm`, `sourcebundle`)
are recognised by the discovery layer but not yet processed.

## Common options

Expand All @@ -42,6 +45,25 @@ Other types (`pe`, `pdb`, `portable-pdb`, `breakpad`, `jvm`, `wasm`,
| `--zstd-level <N>` | Zstd level `9..=22` (default `11`); or pass `--no-zstd`. |
| `--force` | Re-upload even if the server already has the symbol. |
| `--dry-run` | Discover and pack files but skip the HTTP upload. |
| `--concurrency <N>` | *(`--type sourcemaps` only, CLI 0.7.10+)* Ceiling on uploads in flight, `1..=32`. |
| `--allow-empty` | *(`--type sourcemaps` only, CLI 0.7.10+)* Treat "nothing to upload" as success. |
| `--il2cpp-uuid <UUID>` | *(`--type il2cpp-linemap` only)* Additional module UUID for a multi-ABI build. |
| `--il2cpp-root <PATH>` | *(`--type il2cpp-linemap` only)* Path to `il2cppFileRoot.txt` when it isn't beside the JSON. |

`--concurrency` and `--allow-empty` are **rejected with exit `20`** for any other
`--type`, rather than accepted and ignored — so a caller who passed
`--allow-empty` to keep a build green can't silently still get exit `10`.

## Re-uploads and missing paths

A symbol the server already has is **skipped, and the batch continues** — so
rebuilding an app uploads only what changed. Pass `--force` to re-upload anyway.

A path that **does not exist is an error** (exit `10`, `path does not exist: …`),
even when other paths in the same invocation do hold symbols, and nothing is
uploaded. A path you named and the tool cannot find is a typo or a build that
never ran, and half-uploading a build's symbols hides that until a crash arrives
unsymbolicated.

## Android (R8 / ProGuard)

Expand Down Expand Up @@ -111,6 +133,62 @@ part of the whole build-publish flow. Use `debug-files upload --type dsym`
directly for standalone or after-the-fact symbol uploads.
:::

## Rust (Cargo)

A Rust project has no single symbol format: it's a `.dSYM` bundle for Apple
targets, a `.pdb` for `*-pc-windows-msvc`, and the ELF binary itself (keyed by
its GNU build-id) for Linux and Android. `--type rust` discovers whichever the
build produced, so one command covers every target:

```bash
bugsee-cli debug-files upload --type rust target/release \
--version 1.4.0 --build 250
```

Artefacts are classified by container magic rather than by host OS, so a
cross-compiled `target/<triple>/release` uploads correctly from any machine.
Cargo intermediates (`deps/`, `build/`, `incremental/`, `.fingerprint/`) are
skipped, and `--uuid` is rejected — every Rust debug format carries its own
identity, which is what the SDK reports at crash time.

A stock `cargo build --release` emits nothing uploadable. Each format needs a
build setting that, if missing, produces an upload that is accepted and then
resolves nothing:

```toml
# Cargo.toml
[profile.release]
debug = 1 # emit DWARF at all
split-debuginfo = "packed" # macOS/iOS: collect it into a .dSYM
```

```toml
# .cargo/config.toml — Linux and Android only
[target.'cfg(target_os = "linux")']
rustflags = ["-C", "link-arg=-Wl,--build-id"]
```

The command reports whichever setting is missing and exits `10` when it finds no
symbols at all. Use `--dry-run` to see the diagnosis without uploading.

## Unity IL2CPP

An IL2CPP build needs its line-number mapping in addition to the platform's
native symbols (iOS dSYMs, Android ELF):

```bash
bugsee-cli debug-files upload path/to/Symbols/LineNumberMappings.json \
--type il2cpp-linemap \
--version 1.2.3 --build 45 \
--uuid <arm64-build-id>,<armeabi-build-id>
```

The mapping is keyed by the IL2CPP module UUID(s) (`libil2cpp` on Android,
`UnityFramework` on iOS). For a multi-ABI Android build, comma-separate the
values, repeat `--uuid`, or append with `--il2cpp-uuid`. Sibling
`MethodMap.tsv` and `il2cppFileRoot.txt` files are picked up automatically when
they sit next to the JSON.

## Compression

Symbol payloads are Zstd-compressed at level `11` by default. Tune it with
Expand Down
2 changes: 1 addition & 1 deletion docs/cli/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ themselves.
| Debug information files | [`debug-files upload`](/cli/debug-files/) |
| JavaScript source maps | [`sourcemaps inject`](/cli/sourcemaps/) + `debug-files upload --type sourcemaps` |
| Build artefacts & metadata | [`upload build`](/cli/builds/), [`upload build-info`](/cli/builds/), [`pack`](/cli/builds/) |
| iOS build publishing | [`xcode post-action`](/cli/xcode/) |
| iOS build publishing | [`xcode post-action`](/cli/xcode/), [`xcode upload-dsyms`](/cli/xcode/#xcode-upload-dsyms) |
| Build metadata resolvers | [`vcs-metadata`, `ios-deps`, `build-env`, `dsym`](/cli/metadata/) |
| Self-update | [`update`](/cli/update/) |

Expand Down
18 changes: 16 additions & 2 deletions docs/cli/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ a hint.
powershell -ExecutionPolicy ByPass -c "irm https://download.bugsee.com/cli/install.ps1 | iex"
```

This installs to `%LOCALAPPDATA%\Bugsee\bin`. Only 64-bit (x86_64/AMD64) Windows
is published.
This installs to `%LOCALAPPDATA%\Bugsee\bin`. Both 64-bit (x86_64/AMD64) and
ARM64 Windows are published; the script detects the host architecture, including
when a 32-bit PowerShell is running on 64-bit Windows.

## Installer environment overrides

Expand Down Expand Up @@ -57,9 +58,22 @@ work for direct installs too.
| Channel | Package |
|---|---|
| npm | `@bugsee/cli` (per-OS `optionalDependencies` pull the right binary) |
| npm | `@bugsee/bugsee-cli` (single package; downloads the binary on install) |
| Homebrew | the Bugsee tap |
| Maven Central | `com.bugsee:bugsee-cli` (consumed by the Android Gradle plugin) |

:::tip
Prefer `@bugsee/cli` in JavaScript projects. Its binary ships inside per-platform
packages, so npm resolves exactly one and **nothing runs at install time** — it
works under `--ignore-scripts`, under a lockfile-pinned CI install, and offline
from a warm cache. `@bugsee/bugsee-cli` is the older single package whose
`postinstall` downloads the binary on every fresh install; it is still published
and supported, but it needs network access at install time.
:::

Published platforms: macOS arm64 and x86_64, Linux x86_64 and aarch64, Windows
x86_64 and arm64.

:::note
You normally don't install the CLI yourself for an SDK integration — the Bugsee
SDKs and build plugins download and pin a compatible CLI automatically. Install
Expand Down
58 changes: 58 additions & 0 deletions docs/cli/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,64 @@ date for you.

## 0.7.x

### 0.7.10 (September 18 2026)

Source maps upload several at a time, an empty build can be a no-op, and a path
you named but the tool can't find now stops the run.

- **Source-map uploads run concurrently.** `debug-files upload --type sourcemaps`
sent one map at a time, so a web build with one map per chunk spent most of its
upload time waiting on round-trips. Against a server with 50 ms of latency, 60
maps went from 7.1 s to 1.3 s and 200 maps from 23.6 s to 4.1 s.

`--concurrency <N>` (`1..=32`) sets a ceiling; left unset it scales with the
batch — one upload per 8 maps, at least 4, at most 8. `--concurrency 1`
restores the previous sequential behaviour. An explicit `--uuid` forces
sequential uploads whatever the ceiling, because it keys every map under one
ID and those registrations must not race. See
[Source maps](/cli/sourcemaps/#concurrency).

- **`--allow-empty` makes "nothing to upload" a success.** A monorepo package
built without maps, or a framework whose server output has none, previously
failed the caller's build with exit `10`. The flag applies to
`--type sourcemaps`; `xcode upload-dsyms` already treated nothing-to-upload as
success.

- **A path that does not exist is now an error.**
`debug-files upload --type sourcemaps dist/ missing/` used to warn about
`missing/`, upload what it found under `dist/`, and exit `0`. It now exits `10`
with `path does not exist: …` before uploading anything — a path you named and
the tool can't find is a typo or a build that didn't run, and half-uploading a
build's symbols hides that until a crash is unsymbolicated. Drop the missing
path from the invocation if you relied on the old leniency. The bundler plugins
pass a single output directory and are unaffected.

- **A failed upload stops the batch.** Now that uploads are concurrent, the first
failure cancels the rest instead of letting every remaining map pack, register
and transfer into a server that has already refused one — a rejected token on a
200-chunk build was 400 doomed round-trips.

- **`--concurrency` and `--allow-empty` are rejected for other `--type`s**
(exit `20`) rather than accepted and ignored, so a caller who passed
`--allow-empty` to keep a build green can't still get exit `10`.

- **A throttled request is retried.** The symbol-metadata and build-registration
`POST`s are sent without status retries, because a 5xx may mean the server
processed the request and only the response was lost. A `429` carries no such
ambiguity — the request was rejected without being processed — so it is now
retried with the usual backoff. Those requests are also the first thing a
server throttles when several uploads run at once.

### 0.7.9 (September 17 2026)

- **`sourcemaps inject` registers a debug ID another tool already wrote.** A
bundle carrying its own `//# debugId=` — Rollup 4 writes one with
`output.sourcemapDebugIds` — counted as already injected, so it never got the
`globalThis._bugseeDebugIds` runtime registration and the SDK could not attach
its debug ID to a crash frame. Such a bundle now keeps its ID, since its map
already carries it, and gains only the registration, without a second comment.
It is still never re-keyed. See [Source maps](/cli/sourcemaps/).

### 0.7.8 (September 17 2026)

Re-uploading a symbol the server already has no longer fails, including in an
Expand Down
61 changes: 61 additions & 0 deletions docs/cli/sourcemaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,21 @@ This debug ID is what ties a crashing bundle in production to the right source
map on the server — the runtime stub means the SDK can report the debug ID of
the exact bundle that ran, and the uploaded map carries the matching key.

:::warning[`inject` only rewrites `.js`, `.cjs` and `.mjs` files]
A bundle with any other extension is skipped **silently**: the run reports
`js_injected=0` and exits `0`, and the problem only surfaces at upload time as a
map with no debug ID. React Native's default `main.jsbundle` output is the
common case — either emit the bundle with a `.js` name, or use the
[`bugsee-sourcemaps`](/tools/sourcemaps/) tool, which handles that naming. Check
the `js_injected` count in the log to confirm injection actually happened.
:::

A bundle that already carries a `//# debugId=` written by **another tool** — for
example Rollup 4 with `output.sourcemapDebugIds` — keeps that ID, since its map
already carries it, and gains only the `globalThis._bugseeDebugIds`
registration, without a second comment. The SDK needs that registration to
attach the debug ID to a crash frame. *(CLI 0.7.9 and newer.)*

## `sourcemaps inject`

```bash
Expand Down Expand Up @@ -66,6 +81,52 @@ The upload discovers `.map` files and keys each one by its embedded debug ID
server auto-detects the source-map format by content and re-derives the same
key.

A map the server already has is skipped and the batch continues, so rebuilding
an app with unchanged chunks uploads only the ones that changed. `--force`
re-uploads anyway.

### What fails, and when

Directory scans are processed in sorted order, and every map is identified
**before** anything is uploaded — so these failures don't leave a partial upload
behind (a network or server error part-way through still can).

| Situation | Result |
|---|---|
| A map named as a stylesheet or type declaration (`.css.map`, `.d.ts.map`, `.d.mts.map`, `.d.cts.map`) in a scanned directory | Skipped without being read |
| Any other map with no debug ID | **Exit `11`** — `inject` never stamped that bundle |
| A path that does not exist | **Exit `10`**, even when other paths hold maps |
| Nothing found to upload | **Exit `10`**, unless `--allow-empty` |
| One upload fails | The rest of the batch is cancelled |

`--allow-empty` (CLI 0.7.10+) turns "nothing to upload" into success. 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 still an error, so a typo'd output directory isn't swallowed by the
flag.

### Concurrency

*(CLI 0.7.10 and newer.)* Each map is an independent register + `PUT` pair, so a
web build with one map per chunk used to spend its upload time waiting on
round-trips. Maps now upload several at a time.

```bash
bugsee-cli debug-files upload ./dist --type sourcemaps \
--version 1.4.0 --build 1400 --concurrency 8
```

`--concurrency N` (`1..=32`) is a **ceiling**, not a fixed width — no more
uploads run than there are maps. Left unset it scales with the batch: one upload
per 8 maps, at least 4, at most 8. That default is deliberately modest, because
the machine that suffers most from serial uploads is a CI box on a thin uplink,
where the transfer is bandwidth-bound and extra streams only add latency. Raise
it if you've measured your own link.

`--concurrency 1` restores strictly sequential uploads. An explicit `--uuid`
forces sequential uploads whatever the ceiling says: it keys every map in the
scan under one ID, and those registrations must not race each other.

## Relationship to the legacy `bugsee-sourcemaps` tool

The older [`bugsee-sourcemaps`](/tools/sourcemaps/) npm tool generates and
Expand Down
Loading
Loading