Skip to content

Commit 537f2b9

Browse files
committed
merge: resolve conflict with main in ci.yml
2 parents 7cc8c95 + bf4661a commit 537f2b9

37 files changed

Lines changed: 1890 additions & 661 deletions

.github/workflows/ci.yml

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ on:
77
- 'src/**'
88
- 'test/**'
99
- 'script/**'
10+
- 'patches/**'
1011
- 'package.json'
1112
- 'bun.lock'
1213
- '.github/workflows/ci.yml'
@@ -15,6 +16,7 @@ on:
1516
- 'src/**'
1617
- 'test/**'
1718
- 'script/**'
19+
- 'patches/**'
1820
- 'package.json'
1921
- 'bun.lock'
2022
- '.github/workflows/ci.yml'
@@ -41,8 +43,8 @@ jobs:
4143
- run: bun run lint
4244
- run: bun run typecheck
4345

44-
test:
45-
name: Test
46+
test-unit:
47+
name: Unit Tests
4648
runs-on: ubuntu-latest
4749
permissions:
4850
contents: read
@@ -58,12 +60,8 @@ jobs:
5860
key: node-modules-${{ hashFiles('bun.lock') }}
5961
- if: steps.cache.outputs.cache-hit != 'true'
6062
run: bun install --frozen-lockfile
61-
- name: Test
62-
env:
63-
SENTRY_TEST_AUTH_TOKEN: ${{ secrets.SENTRY_TEST_AUTH_TOKEN }}
64-
SENTRY_TEST_ORG: ${{ secrets.SENTRY_TEST_ORG }}
65-
SENTRY_TEST_PROJECT: ${{ secrets.SENTRY_TEST_PROJECT }}
66-
run: bun test --coverage --coverage-reporter=lcov
63+
- name: Unit Tests
64+
run: bun run test:unit --coverage --coverage-reporter=lcov
6765
- name: Upload Coverage
6866
uses: getsentry/codecov-action@main
6967
with:
@@ -72,7 +70,7 @@ jobs:
7270

7371
build-binary:
7472
name: Build Binary (${{ matrix.target }})
75-
needs: [lint, test]
73+
needs: [lint, test-unit]
7674
runs-on: ${{ matrix.os }}
7775
strategy:
7876
fail-fast: false
@@ -124,9 +122,35 @@ jobs:
124122
name: sentry-${{ matrix.target }}
125123
path: dist-bin/sentry-*
126124

125+
test-e2e:
126+
name: E2E Tests
127+
needs: [build-binary]
128+
runs-on: ubuntu-latest
129+
steps:
130+
- uses: actions/checkout@v4
131+
- uses: oven-sh/setup-bun@v2
132+
- uses: actions/cache@v4
133+
id: cache
134+
with:
135+
path: node_modules
136+
key: node-modules-${{ hashFiles('bun.lock') }}
137+
- if: steps.cache.outputs.cache-hit != 'true'
138+
run: bun install --frozen-lockfile
139+
- name: Download Linux binary
140+
uses: actions/download-artifact@v4
141+
with:
142+
name: sentry-linux-x64
143+
path: dist-bin
144+
- name: Make binary executable
145+
run: chmod +x dist-bin/sentry-linux-x64
146+
- name: E2E Tests
147+
env:
148+
SENTRY_CLI_BINARY: ${{ github.workspace }}/dist-bin/sentry-linux-x64
149+
run: bun run test:e2e
150+
127151
build-npm:
128152
name: Build npm Package (Node ${{ matrix.node }})
129-
needs: [lint, test]
153+
needs: [lint, test-unit]
130154
runs-on: ubuntu-latest
131155
environment: ${{ (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && 'production' || '' }}
132156
strategy:
@@ -163,7 +187,7 @@ jobs:
163187

164188
build-docs:
165189
name: Build Docs
166-
needs: [lint, test]
190+
needs: [lint, test-unit]
167191
runs-on: ubuntu-latest
168192
steps:
169193
- uses: actions/checkout@v4
@@ -185,7 +209,7 @@ jobs:
185209

186210
merge-artifacts:
187211
name: Merge Artifacts
188-
needs: [build-binary, build-npm, build-docs]
212+
needs: [build-binary, build-npm, build-docs, test-e2e]
189213
runs-on: ubuntu-latest
190214
steps:
191215
- uses: actions/download-artifact@v4

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ dist-bin
1414
coverage
1515
*.lcov
1616

17+
# test artifacts
18+
.test-tmp
19+
1720
# logs
1821
logs
1922
_.log

bun.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"lint": "bunx ultracite check",
1919
"lint:fix": "bunx ultracite fix",
2020
"test": "bun test",
21+
"test:unit": "bun test test/lib test/commands test/types",
2122
"test:e2e": "bun test test/e2e"
2223
},
2324
"devDependencies": {
@@ -48,6 +49,7 @@
4849
},
4950
"packageManager": "bun@1.3.3",
5051
"patchedDependencies": {
51-
"@stricli/core@1.2.5": "patches/@stricli%2Fcore@1.2.5.patch"
52+
"@stricli/core@1.2.5": "patches/@stricli%2Fcore@1.2.5.patch",
53+
"@sentry/core@10.36.0": "patches/@sentry%2Fcore@10.36.0.patch"
5254
}
5355
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
diff --git a/build/esm/client.js b/build/esm/client.js
2+
index 1111111111111111111111111111111111111111..2222222222222222222222222222222222222222 100644
3+
--- a/build/esm/client.js
4+
+++ b/build/esm/client.js
5+
@@ -643,11 +643,16 @@ class Client {
6+
* `false` otherwise
7+
*/
8+
async _isClientDoneProcessing(timeout) {
9+
+ // Check immediately first - no need to wait if nothing is processing
10+
+ if (!this._numProcessing) {
11+
+ return true;
12+
+ }
13+
+
14+
let ticked = 0;
15+
16+
// if no timeout is provided, we wait "forever" until everything is processed
17+
while (!timeout || ticked < timeout) {
18+
- await new Promise(resolve => setTimeout(resolve, 1));
19+
+ await new Promise(resolve => { const t = setTimeout(resolve, 1); if (typeof t !== 'number' && t.unref) t.unref(); });
20+
21+
if (!this._numProcessing) {
22+
return true;
23+
diff --git a/build/cjs/client.js b/build/cjs/client.js
24+
index 3333333333333333333333333333333333333333..4444444444444444444444444444444444444444 100644
25+
--- a/build/cjs/client.js
26+
+++ b/build/cjs/client.js
27+
@@ -645,11 +645,16 @@ class Client {
28+
* `false` otherwise
29+
*/
30+
async _isClientDoneProcessing(timeout) {
31+
+ // Check immediately first - no need to wait if nothing is processing
32+
+ if (!this._numProcessing) {
33+
+ return true;
34+
+ }
35+
+
36+
let ticked = 0;
37+
38+
// if no timeout is provided, we wait "forever" until everything is processed
39+
while (!timeout || ticked < timeout) {
40+
- await new Promise(resolve => setTimeout(resolve, 1));
41+
+ await new Promise(resolve => { const t = setTimeout(resolve, 1); if (typeof t !== 'number' && t.unref) t.unref(); });
42+
43+
if (!this._numProcessing) {
44+
return true;
45+
diff --git a/build/esm/utils/promisebuffer.js b/build/esm/utils/promisebuffer.js
46+
index 5555555555555555555555555555555555555555..6666666666666666666666666666666666666666 100644
47+
--- a/build/esm/utils/promisebuffer.js
48+
+++ b/build/esm/utils/promisebuffer.js
49+
@@ -69,7 +69,7 @@ function makePromiseBuffer(limit = 100) {
50+
return drainPromise;
51+
}
52+
53+
- const promises = [drainPromise, new Promise(resolve => setTimeout(() => resolve(false), timeout))];
54+
+ const promises = [drainPromise, new Promise(resolve => { const t = setTimeout(() => resolve(false), timeout); if (typeof t !== 'number' && t.unref) t.unref(); })];
55+
56+
// Promise.race will resolve to the first promise that resolves or rejects
57+
// So if the drainPromise resolves, the timeout promise will be ignored
58+
diff --git a/build/cjs/utils/promisebuffer.js b/build/cjs/utils/promisebuffer.js
59+
index 7777777777777777777777777777777777777777..8888888888888888888888888888888888888888 100644
60+
--- a/build/cjs/utils/promisebuffer.js
61+
+++ b/build/cjs/utils/promisebuffer.js
62+
@@ -71,7 +71,7 @@ function makePromiseBuffer(limit = 100) {
63+
return drainPromise;
64+
}
65+
66+
- const promises = [drainPromise, new Promise(resolve => setTimeout(() => resolve(false), timeout))];
67+
+ const promises = [drainPromise, new Promise(resolve => { const t = setTimeout(() => resolve(false), timeout); if (typeof t !== 'number' && t.unref) t.unref(); })];
68+
69+
// Promise.race will resolve to the first promise that resolves or rejects
70+
// So if the drainPromise resolves, the timeout promise will be ignored

src/commands/issue/explain.ts

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,25 @@ import {
1111
triggerRootCauseAnalysis,
1212
} from "../../lib/api-client.js";
1313
import { ApiError } from "../../lib/errors.js";
14-
import { writeJson } from "../../lib/formatters/index.js";
14+
import { writeFooter, writeJson } from "../../lib/formatters/index.js";
1515
import {
1616
formatAutofixError,
1717
formatRootCauseList,
1818
} from "../../lib/formatters/seer.js";
1919
import { extractRootCauses } from "../../types/seer.js";
20-
import { pollAutofixState, resolveOrgAndIssueId } from "./utils.js";
20+
import {
21+
buildCommandHint,
22+
type IssueIdFlags,
23+
issueIdFlags,
24+
issueIdPositional,
25+
pollAutofixState,
26+
resolveOrgAndIssueId,
27+
} from "./utils.js";
2128

22-
type ExplainFlags = {
23-
readonly org?: string;
29+
interface ExplainFlags extends IssueIdFlags {
2430
readonly json: boolean;
2531
readonly force: boolean;
26-
};
32+
}
2733

2834
export const explainCommand = buildCommand({
2935
docs: {
@@ -39,27 +45,14 @@ export const explainCommand = buildCommand({
3945
"Examples:\n" +
4046
" sentry issue explain 123456789\n" +
4147
" sentry issue explain MYPROJECT-ABC --org my-org\n" +
48+
" sentry issue explain G --org my-org --project my-project\n" +
4249
" sentry issue explain 123456789 --json\n" +
4350
" sentry issue explain 123456789 --force",
4451
},
4552
parameters: {
46-
positional: {
47-
kind: "tuple",
48-
parameters: [
49-
{
50-
brief: "Issue ID or short ID (e.g., MYPROJECT-ABC or 123456789)",
51-
parse: String,
52-
},
53-
],
54-
},
53+
positional: issueIdPositional,
5554
flags: {
56-
org: {
57-
kind: "parsed",
58-
parse: String,
59-
brief:
60-
"Organization slug (required for short IDs if not auto-detected)",
61-
optional: true,
62-
},
55+
...issueIdFlags,
6356
json: {
6457
kind: "boolean",
6558
brief: "Output as JSON",
@@ -81,12 +74,13 @@ export const explainCommand = buildCommand({
8174

8275
try {
8376
// Resolve org and issue ID
84-
const { org, issueId: numericId } = await resolveOrgAndIssueId(
77+
const { org, issueId: numericId } = await resolveOrgAndIssueId({
8578
issueId,
86-
flags.org,
79+
org: flags.org,
80+
project: flags.project,
8781
cwd,
88-
`sentry issue explain ${issueId} --org <org-slug>`
89-
);
82+
commandHint: buildCommandHint("explain", issueId),
83+
});
9084

9185
// 1. Check for existing analysis (skip if --force)
9286
let state = flags.force ? null : await getAutofixState(org, numericId);
@@ -135,8 +129,12 @@ export const explainCommand = buildCommand({
135129
}
136130

137131
// Human-readable output
138-
const lines = formatRootCauseList(causes, issueId);
132+
const lines = formatRootCauseList(causes);
139133
stdout.write(`${lines.join("\n")}\n`);
134+
writeFooter(
135+
stdout,
136+
`To create a plan, run: sentry issue plan ${issueId}`
137+
);
140138
} catch (error) {
141139
// Handle API errors with friendly messages
142140
if (error instanceof ApiError) {

src/commands/issue/plan.ts

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,19 @@ import {
2424
extractSolution,
2525
type RootCause,
2626
} from "../../types/seer.js";
27-
import { pollAutofixState, resolveOrgAndIssueId } from "./utils.js";
27+
import {
28+
buildCommandHint,
29+
type IssueIdFlags,
30+
issueIdFlags,
31+
issueIdPositional,
32+
pollAutofixState,
33+
resolveOrgAndIssueId,
34+
} from "./utils.js";
2835

29-
type PlanFlags = {
30-
readonly org?: string;
36+
interface PlanFlags extends IssueIdFlags {
3137
readonly cause?: number;
3238
readonly json: boolean;
33-
};
39+
}
3440

3541
/**
3642
* Validate that an autofix run exists and has completed root cause analysis.
@@ -140,26 +146,13 @@ export const planCommand = buildCommand({
140146
" - Code mappings set up for your project\n\n" +
141147
"Examples:\n" +
142148
" sentry issue plan 123456789 --cause 0\n" +
143-
" sentry issue plan MYPROJECT-ABC --org my-org --cause 1",
149+
" sentry issue plan MYPROJECT-ABC --org my-org --cause 1\n" +
150+
" sentry issue plan G --org my-org --project my-project --cause 0",
144151
},
145152
parameters: {
146-
positional: {
147-
kind: "tuple",
148-
parameters: [
149-
{
150-
brief: "Issue ID or short ID (e.g., MYPROJECT-ABC or 123456789)",
151-
parse: String,
152-
},
153-
],
154-
},
153+
positional: issueIdPositional,
155154
flags: {
156-
org: {
157-
kind: "parsed",
158-
parse: String,
159-
brief:
160-
"Organization slug (required for short IDs if not auto-detected)",
161-
optional: true,
162-
},
155+
...issueIdFlags,
163156
cause: {
164157
kind: "parsed",
165158
parse: numberParser,
@@ -182,12 +175,13 @@ export const planCommand = buildCommand({
182175

183176
try {
184177
// Resolve org and issue ID
185-
const { org, issueId: numericId } = await resolveOrgAndIssueId(
178+
const { org, issueId: numericId } = await resolveOrgAndIssueId({
186179
issueId,
187-
flags.org,
180+
org: flags.org,
181+
project: flags.project,
188182
cwd,
189-
`sentry issue plan ${issueId} --org <org-slug>`
190-
);
183+
commandHint: buildCommandHint("plan", issueId),
184+
});
191185

192186
// Get current autofix state
193187
const currentState = await getAutofixState(org, numericId);

0 commit comments

Comments
 (0)