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
63 changes: 27 additions & 36 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ jobs:
# `ref` empty so checkout defaults to GITHUB_REF (the pull_request
# merge SHA, always fetchable from the base repo with github.token).
ref: ${{ steps.token.outcome == 'success' && (github.head_ref || github.ref_name) || '' }}
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
- uses: actions/cache@v5
id: cache
with:
Expand All @@ -138,11 +138,11 @@ jobs:
- if: steps.cache.outputs.cache-hit != 'true'
run: pnpm install --frozen-lockfile
- name: Generate API Schema
run: bun run generate:schema
run: pnpm run generate:schema
- name: Generate docs and skill files
run: bun run generate:docs
run: pnpm run generate:docs
- name: Validate fragments
run: bun run check:fragments
run: pnpm run check:fragments
- name: Check skill files
id: check-skill
run: |
Expand Down Expand Up @@ -171,7 +171,7 @@ jobs:
- name: Fail for fork PRs with stale generated files
if: (steps.check-skill.outputs.stale == 'true' || steps.check-sections.outputs.stale == 'true') && steps.token.outcome != 'success'
run: |
echo "::error::Generated files are out of date. Run 'bun run generate:docs' locally and commit the result."
echo "::error::Generated files are out of date. Run 'pnpm run generate:docs' locally and commit the result."
exit 1

lint:
Expand All @@ -181,23 +181,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
- uses: actions/cache@v5
id: cache
with:
path: node_modules
key: node-modules-${{ hashFiles('pnpm-lock.yaml', '.npmrc', 'patches/**') }}
- if: steps.cache.outputs.cache-hit != 'true'
run: pnpm install --frozen-lockfile
- run: bun run generate:schema
- run: bun run lint
- run: bun run typecheck
- run: bun run check:deps
- run: bun run check:errors
- run: bun run check:patches
- run: pnpm run generate:schema
- run: pnpm run lint
- run: pnpm run typecheck
- run: pnpm run check:deps
- run: pnpm run check:errors
- run: pnpm run check:patches

test-unit:
name: Unit Tests
Expand All @@ -211,9 +211,6 @@ jobs:
statuses: write
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
Expand All @@ -226,9 +223,9 @@ jobs:
- if: steps.cache.outputs.cache-hit != 'true'
run: pnpm install --frozen-lockfile
- name: Generate API Schema
run: bun run generate:schema
run: pnpm run generate:schema
- name: Unit Tests
run: bun run test:unit
run: pnpm run test:unit
- name: Coverage Report
uses: getsentry/codecov-action@main
with:
Expand All @@ -253,6 +250,9 @@ jobs:
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
node-version: "22"
- uses: actions/cache@v5
id: cache
with:
Expand Down Expand Up @@ -637,9 +637,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
Expand All @@ -659,14 +656,14 @@ jobs:
- name: Make binary executable
run: chmod +x dist-bin/sentry-linux-x64
- name: Generate API Schema
run: bun run generate:schema
run: pnpm run generate:schema
- name: E2E Tests
env:
SENTRY_CLI_BINARY: ${{ github.workspace }}/dist-bin/sentry-linux-x64
# Pass API key only when skill files changed — the skill-eval e2e test
# auto-skips when the key is absent, so non-skill PRs aren't affected.
ANTHROPIC_API_KEY: ${{ needs.changes.outputs.skill == 'true' && secrets.ANTHROPIC_API_KEY || '' }}
run: bun run test:e2e
run: pnpm run test:e2e

build-npm:
name: Build npm Package (Node ${{ matrix.node }})
Expand All @@ -679,9 +676,6 @@ jobs:
node: ["22", "24"]
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v6
with:
Expand All @@ -697,7 +691,7 @@ jobs:
env:
# Environment-scoped (production) — see note in build-binary.
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
run: bun run bundle
run: pnpm run bundle
- name: Smoke test (Node.js)
run: node dist/bin.cjs --help
- run: npm pack
Expand All @@ -723,9 +717,6 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
steps:
- uses: actions/checkout@v6
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.13"
- uses: pnpm/action-setup@v4
# Astro 6 requires Node >= 22.12. Pin an explicit version so the docs
# build doesn't rely on whatever ships on the runner image.
Expand All @@ -750,16 +741,16 @@ jobs:
- name: Make binary executable
run: chmod +x dist-bin/sentry-linux-x64
- name: Generate docs content
run: bun run generate:schema && bun run generate:docs
run: pnpm run generate:schema && pnpm run generate:docs
- name: Build Docs
working-directory: docs
env:
PUBLIC_SENTRY_ENVIRONMENT: production
SENTRY_RELEASE: ${{ steps.version.outputs.version }}
PUBLIC_SENTRY_RELEASE: ${{ steps.version.outputs.version }}
run: |
bun install --frozen-lockfile
bun run build
pnpm install --frozen-lockfile
pnpm run build
Comment thread
cursor[bot] marked this conversation as resolved.
# Inject debug IDs and upload sourcemaps. The inject step adds
# //# debugId= and the _sentryDebugIds IIFE to deployed JS files.
# Both steps require SENTRY_AUTH_TOKEN (the CLI checks auth on startup).
Expand Down
14 changes: 7 additions & 7 deletions .lore.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,8 @@

### Gotcha

<!-- lore:019dc095-9ce4-7fe1-89ab-12efeffddcee -->
* **Biome noUselessUndefined also rejects () => {} empty arrow callbacks**: Biome lint traps (run \`bun run lint\` not \`lint:fix\` before pushing): (1) \`noUselessUndefined\`+\`noEmptyBlockStatements\`: use \`function noop():void{/\* noop \*/}\` not \`()=>undefined\`. (2) \`noExcessiveCognitiveComplexity\` caps at 15; \`biome-ignore\` on SAME line as function definition. (3) \`noPrecisionLoss\` on int >2^53 — use \`Number(string)\`. (4) \`noIncrementDecrement\` — use \`i+=1\`. (5) \`useYield\` on \`async \*func()\` needs \`biome-ignore\`. (6) Plugin forbids raw \`metadata\` table queries — use \`getMetadata\`/\`setMetadata\`/\`clearMetadata\`. (7) \`noMisplacedAssertion\` — inline \`biome-ignore\` above each \`expect()\`, NOT file-level. (8) \`AuthError(reason, message?)\`: \`new AuthError("expired", "Token expired")\`. (9) \`noShadow\`: \`vi.hoisted()\` inner vars shadowing outer destructured names — prefix inner vars with \`\_\`; unused outer destructured names from \`vi.hoisted()\` classes trigger \`noUnusedVariables\` — remove them. (10) \`noNamespaceImport\` on \`import \*\` in tests — add \`biome-ignore\` for \`vi.mocked()\` partial mocks. (11) \`noSkippedTests\` on intentional \`test.skip\` — add \`biome-ignore\` with explanation. Tests aren't type-checked but ARE lint-checked. Biome hits type limit on large files — split o \[truncated — entry too long]

<!-- lore:019e4b21-84ea-7abb-a2b7-f8bac1cf28dc -->
* **io\_uring crash on GitHub Actions — set UV\_USE\_IO\_URING=0**: GitHub Actions runners have kernels that don't support io\_uring properly. Node.js (via libuv) crashes with \`libuv: io\_uring\_enter(getevents): Operation not supported\` + exit code 134 (SIGABRT). Affects both Node 22 and 24. Fix: set \`UV\_USE\_IO\_URING=0\` env var in CI job steps to disable io\_uring in libuv. Trap: looks like a Node version issue because it appears in test runs, but switching Node versions doesn't help — it's a kernel capability issue on the runner.
<!-- lore:019e4c36-52d4-7188-b0aa-c0f9d5709531 -->
* **http.createServer async callback — unhandled promise rejections crash test server**: (gotcha) \`http.createServer(async (req, res) => { await ... })\` — Node accepts async callbacks but ignores their return value. If the inner \`await\` throws, it causes an unhandled promise rejection that crashes the test server. Fix: wrap entire async callback body in \`try...catch\` and call \`res.end()\` or \`res.destroy()\` in the catch block. Applies to \`test/mocks/server.ts\` and any test HTTP server using async request handlers.

<!-- lore:019db776-111b-73db-b4ad-b762dfd4808f -->
* **MastraClient has no dispose API — use AbortController for cleanup**: MastraClient has no \`close()\`/\`dispose()\` API — cleanup via \`ClientOptions.abortSignal\` (constructor) or per-prompt \`signal\`. Without explicit abort, fetch keep-alive sockets hold the event loop alive past natural exit. Pattern in \`src/lib/init/wizard-runner.ts\`: create \`AbortController\` per \`runWizard\`, pass \`abortSignal: controller.signal\` to \`new MastraClient(...)\`, abort via \`using \_ = { \[Symbol.dispose]: () => controller.abort() }\`. Custom \`fetch\` wrapper must preserve \`init.signal\` via spread. Tests capture \`ClientOptions\` via \`spyOn(MastraClient.prototype, 'getWorkflow').mockImplementation(function() { capturedOpts.push(this.options); ... })\`.
Expand All @@ -68,8 +65,11 @@
<!-- lore:019e471c-df3f-7e78-9df7-6b36d2d258db -->
* **SQLite transaction() ROLLBACK can throw, discarding original error**: (gotcha) SQLite transaction ROLLBACK error-swallowing trap: In \`src/lib/db/sqlite.ts\`, \`transaction()\` catches errors and runs \`this.db.exec('ROLLBACK')\`. If ROLLBACK itself throws, the original error is lost. Fix: \`const origErr = e; try { this.db.exec('ROLLBACK'); } catch (rbErr) { log.debug(...); } throw origErr;\`

<!-- lore:019e4b91-b3fb-7e62-b607-8d1ec425a6a0 -->
* **Vitest worker pool requires pool:forks + UV\_USE\_IO\_URING=0 on GitHub Actions**: (gotcha) Vitest worker pool + CI issues: (1) On GitHub Actions, io\_uring crashes Node.js workers (exit 134/SIGABRT). Fix: set \`pool: 'forks'\` in \`vitest.config.ts\` AND \`UV\_USE\_IO\_URING=0\` env var in CI — it's a kernel capability issue, not a Node version issue. (2) Tests internally calling \`Bun.spawn\` must be skipped in Vitest Node workers via \`skipIf\`. (3) npm build smoke test uses system Node — \`setup-node\` (with \`node-version: ${{ matrix.node }}\`) must not be deleted from the npm build CI job; smoke test on \`dist/bin.cjs\` rejects Node < 22.15 and fails silently if setup-node is missing. (4) Vitest 4 removed \`test(name, fn, { timeout })\` signature — options must be second arg: \`test(name, { timeout }, fn)\`. Bare numeric timeout \`beforeAll(fn, 60\_000)\` remains valid.

<!-- lore:019e464f-8fcb-7d16-b7ee-f3b157e1565e -->
* **whichSync must use 'command -v' not 'which' for PATH-restricted lookups**: (gotcha) Bun→Node.js API replacements: \`Bun.which(cmd,{PATH})\` → \`whichSync()\` from \`src/lib/which.ts\` (uses 'command -v'). \`Bun.spawn\` → \`spawn(cmd,args,{stdio:\['pipe','pipe','pipe'],...opts})\`; \`proc.exited\` → \`new Promise(r=>proc.on('close',c=>r(c??1)))\`; stdout via \`proc.stdout.on('data',(d)=>{out+=d;})\`. \*\*CRITICAL: always attach \`proc.on('error',noop)\` — Node crashes on unhandled spawn errors.\*\* \`Bun.spawnSync\` → \`spawnSync\`; \`proc.success\`→\`proc.status===0\`. \`Bun.write\`→\`writeFileSync\`. \`Bun.sleep(ms)\`→\`import {setTimeout as sleepMs} from 'node:timers/promises'\`. \`new Bun.Glob(p).match(i)\`→\`picomatch(p,{dot:true})(i)\`. \`Bun.randomUUIDv7()\`→\`uuidv7()\`. \`Bun.semver.order()\`→\`compare()\` from \`semver\` (guard with \`semverValid(v)\`). \`Bun.file().writer()\`→\`createWriteStream\` — never pass \`resolve\` directly to \`writer.end()\`; use \`writer.end((err?)=>err?reject(err):resolve())\`. Node version: \`engines.node >=22.15\` (zstd requires 22.15+). CI builds \`\["22","24"]\`; E2E jobs MUST use \`actions/setup-node\` with \`node-version: 22\` — \`ubuntu-latest\` defaults to Node 20.
* **whichSync must use 'command -v' not 'which' for PATH-restricted lookups**: (gotcha) Bun→Node.js API replacements: \`Bun.which(cmd,{PATH})\` → \`whichSync()\` from \`src/lib/which.ts\` (uses 'command -v'). \`Bun.spawn\` → \`spawn(cmd,args,{stdio:\['pipe','pipe','pipe'],...opts})\`; \`proc.exited\` → \`new Promise(r=>proc.on('close',c=>r(c??1)))\`; stdout via \`proc.stdout.on('data',(d)=>{out+=d;})\`. \*\*CRITICAL: always attach \`proc.on('error',noop)\` — Node crashes on unhandled spawn errors.\*\* \`Bun.spawnSync\` → \`spawnSync\`; \`proc.success\`→\`proc.status===0\`. \`Bun.write\`→\`writeFileSync\`. \`Bun.sleep(ms)\`→\`import {setTimeout as sleepMs} from 'node:timers/promises'\`. \`new Bun.Glob(p).match(i)\`→\`picomatch(p,{dot:true})(i)\`. \`Bun.randomUUIDv7()\`→\`uuidv7()\`. \`Bun.semver.order()\`→\`compare()\` from \`semver\` (guard with \`semverValid(v)\`). \`Bun.file().writer()\`→\`createWriteStream\`. Node version: \`engines.node >=22.15\` (zstd requires 22.15+). CI builds \`\["22","24"]\`; E2E jobs MUST use \`actions/setup-node\` with \`node-version: 22\`. Tests using \`Bun.spawn\` internally must be skipped in Vitest Node workers via \`skipIf\`. PRESERVE intentional Bun usage: \`Bun.build()\` in \`script/build.ts\` for native binary compilation must stay Bun; \`build-binary\` CI job retains \`oven-sh/setup-bun\`; \`script/nod \[truncated — entry too long]

<!-- lore:019db0c9-9cc7-7352-b1f2-61b34b87b252 -->
* **Whole-buffer matchAll slower than split+test when aggregated over many files**: (gotcha) Grep/scan traps in \`src/lib/scan/\`: (1) Whole-buffer \`regex.exec\` 12× faster per-file but ~1.6× SLOWER over 10k files — early-exit at \`maxResults\` via \`mapFilesConcurrent.onResult\` wins. (2) Literal prefilter is FILE-LEVEL gate (\`indexOf\`→skip); per-line verify breaks cross-newline patterns and Unicode length-changing \`toLowerCase\`. (3) Extractor \`hasTopLevelAlternation\`+\`skipGroup\` must call \`skipCharacterClass\` (PCRE \`\[]abc]\` ≠ JS empty class). (4) Wake-latch race: use latched \`pendingWake\` flag, not \`let notify=null; await new Promise(r=>notify=r)\`. (5) \`mapFilesConcurrent\` filters \`null\` but NOT \`\[]\` — return \`null\` for no-op files. (6) \`collectGlob\`/\`collectGrep\` must NOT forward \`maxResults\` to iterator; drain uncapped, set \`truncated=true\`.
Expand All @@ -85,4 +85,4 @@
### Preference

<!-- lore:019e4a9c-430a-74a8-a5e4-8dd98c672cef -->
* **Always wait for Sentry Seer and Cursor BugBot CI jobs before merging and address all unresolved review comments**: (preference) Lint discipline: fix errors immediately with minimal, surgical changes — prefix unused/shadowing vars with \`\_\`, use optional chaining, rename rather than restructure. No broad refactors. After fixing, re-run lint to confirm exit code 0 before committing/pushing. Intentional \`test.skip\` from known Vitest limitations: suppress with inline ignore. \`node:sqlite\` requires \`--experimental-sqlite\` on Node 22 — top-level import crashes before any try/catch. After removing \`bun:sqlite\` fallback, remove dead branches like \`this.db.query ?? this.db.prepare\` and \`typeof this.db.transaction === 'function'\` guards entirely. (preference) Adversarial PR review + CI monitoring: run 4-5 rounds of adversarial review (security, edge cases, error handling, lint, test coverage), severity-tiered (CRITICAL/MEDIUM/LOW/NON-BLOCKING), explicit MERGE/NO-MERGE verdict. Wait for 'Sentry Seer' and 'Cursor BugBot' CI jobs; address all unresolved comments. \`dorny/paths-filter\` diffs against base — empty commits produce all-false outputs, silently skipping jobs; make a real file change to trigger CI.
* **Always wait for Sentry Seer and Cursor BugBot CI jobs before merging and address all unresolved review comments**: (preference) PR/CI discipline: Run adversarial review rounds (security, edge cases, error handling, lint, test coverage), severity-tiered (CRITICAL/MEDIUM/LOW/NON-BLOCKING), explicit MERGE/NO-MERGE verdict. Wait for 'Sentry Seer' and 'Cursor BugBot' CI jobs; address all unresolved comments. \`dorny/paths-filter\` diffs against base — empty commits produce all-false outputs, silently skipping jobs; make a real file change to trigger CI. Lint: fix errors immediately with minimal surgical changes — prefix unused/shadowing vars with \`\_\`, use optional chaining. Re-run lint to confirm exit code 0 before committing. \`node:sqlite\` requires \`--experimental-sqlite\` on Node 22 — top-level import crashes before any try/catch. Always exclude \`build/\` and \`dist/\` from Biome analysis to avoid type-limit warnings. Biome version mismatch: local may pass while CI fails — CI is authoritative; apply \`biome format --write\` on failing file. Lazy \`require()\` in test fixtures bypasses Vite's \`.js→.ts\` resolver — use top-level \`import\` instead.
12 changes: 8 additions & 4 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
1. Install dependencies:

```bash
bun install
pnpm install
```

2. Create a `.env.local` file in the project root:
Expand All @@ -25,16 +25,18 @@ Get the client ID from your Sentry OAuth application settings.

## Running Locally

Load environment variables from `.env.local` (e.g. via `dotenv` or `export $(cat .env.local | xargs)`), then:

```bash
bun run --env-file=.env.local cli auth login
pnpm run cli -- auth login
```

## Testing the Device Flow

1. Run the CLI login command:

```bash
bun run --env-file=.env.local cli auth login
pnpm run cli -- auth login
```

2. You'll see output like:
Expand Down Expand Up @@ -86,8 +88,10 @@ The table below lists the most common development variables. For the complete re

## Building

Building the native binary still requires Bun:

```bash
bun run build
pnpm run build
```

## Architecture
Expand Down
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,30 +126,30 @@ Errors are thrown as `SentryError` with `.exitCode` and `.stderr`.
```bash
git clone https://github.com/getsentry/cli.git
cd cli
bun install
pnpm install
```

### Running Locally

```bash
# Run CLI in development mode
bun run cli --help
pnpm run cli -- --help

# With environment variables
bun run --env-file=.env.local cli --help
# With environment variables (create .env.local first, see DEVELOPMENT.md)
pnpm run cli -- --help
```

### Scripts

<!-- GENERATED:START dev-scripts -->
```bash
bun run build # Build for current platform
bun run typecheck # Type checking
bun run lint # Check for issues
bun run lint:fix # Auto-fix issues
bun run test:unit # Run unit tests
bun run test:e2e # Run end-to-end tests
bun run generate:docs # Regenerate command docs and skills
pnpm run build # Build for current platform
pnpm run typecheck # Type checking
pnpm run lint # Check for issues
pnpm run lint:fix # Auto-fix issues
pnpm run test:unit # Run unit tests
pnpm run test:e2e # Run end-to-end tests
pnpm run generate:docs # Regenerate command docs and skills
```
<!-- GENERATED:END dev-scripts -->

Expand Down
Loading
Loading