@humanbased/crosscheck 1.1.1-beta.0 → 1.2.0-beta.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/crosscheck.config.example.yml +42 -0
- package/dist/__tests__/can-write-verdict.test.d.ts +2 -0
- package/dist/__tests__/can-write-verdict.test.d.ts.map +1 -0
- package/dist/__tests__/can-write-verdict.test.js +31 -0
- package/dist/__tests__/can-write-verdict.test.js.map +1 -0
- package/dist/__tests__/linear-client.test.d.ts +2 -0
- package/dist/__tests__/linear-client.test.d.ts.map +1 -0
- package/dist/__tests__/linear-client.test.js +120 -0
- package/dist/__tests__/linear-client.test.js.map +1 -0
- package/dist/__tests__/linear-comment.test.d.ts +2 -0
- package/dist/__tests__/linear-comment.test.d.ts.map +1 -0
- package/dist/__tests__/linear-comment.test.js +151 -0
- package/dist/__tests__/linear-comment.test.js.map +1 -0
- package/dist/__tests__/linear-identity.test.d.ts +2 -0
- package/dist/__tests__/linear-identity.test.d.ts.map +1 -0
- package/dist/__tests__/linear-identity.test.js +253 -0
- package/dist/__tests__/linear-identity.test.js.map +1 -0
- package/dist/__tests__/linear-notify.test.d.ts +2 -0
- package/dist/__tests__/linear-notify.test.d.ts.map +1 -0
- package/dist/__tests__/linear-notify.test.js +144 -0
- package/dist/__tests__/linear-notify.test.js.map +1 -0
- package/dist/__tests__/linear-ref.test.d.ts +2 -0
- package/dist/__tests__/linear-ref.test.d.ts.map +1 -0
- package/dist/__tests__/linear-ref.test.js +261 -0
- package/dist/__tests__/linear-ref.test.js.map +1 -0
- package/dist/__tests__/linear-verify.test.d.ts +2 -0
- package/dist/__tests__/linear-verify.test.d.ts.map +1 -0
- package/dist/__tests__/linear-verify.test.js +113 -0
- package/dist/__tests__/linear-verify.test.js.map +1 -0
- package/dist/__tests__/linear-worker.test.d.ts +2 -0
- package/dist/__tests__/linear-worker.test.d.ts.map +1 -0
- package/dist/__tests__/linear-worker.test.js +83 -0
- package/dist/__tests__/linear-worker.test.js.map +1 -0
- package/dist/__tests__/linear-write-possible.test.d.ts +2 -0
- package/dist/__tests__/linear-write-possible.test.d.ts.map +1 -0
- package/dist/__tests__/linear-write-possible.test.js +30 -0
- package/dist/__tests__/linear-write-possible.test.js.map +1 -0
- package/dist/__tests__/optimize.test.js +1 -0
- package/dist/__tests__/optimize.test.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +49 -1
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +23 -3
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +40 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +52 -6
- package/dist/commands/watch.js.map +1 -1
- package/dist/config/loader.d.ts +3 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +13 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +208 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +39 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/issues/ticket-ref.d.ts.map +1 -1
- package/dist/issues/ticket-ref.js +6 -5
- package/dist/issues/ticket-ref.js.map +1 -1
- package/dist/lib/clone.d.ts +1 -0
- package/dist/lib/clone.d.ts.map +1 -1
- package/dist/lib/clone.js +28 -10
- package/dist/lib/clone.js.map +1 -1
- package/dist/lib/runner.d.ts +2 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +58 -1
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/workflow.d.ts +9 -0
- package/dist/lib/workflow.d.ts.map +1 -1
- package/dist/lib/workflow.js +20 -0
- package/dist/lib/workflow.js.map +1 -1
- package/dist/linear/client.d.ts +18 -0
- package/dist/linear/client.d.ts.map +1 -0
- package/dist/linear/client.js +67 -0
- package/dist/linear/client.js.map +1 -0
- package/dist/linear/comment.d.ts +20 -0
- package/dist/linear/comment.d.ts.map +1 -0
- package/dist/linear/comment.js +53 -0
- package/dist/linear/comment.js.map +1 -0
- package/dist/linear/identity.d.ts +47 -0
- package/dist/linear/identity.d.ts.map +1 -0
- package/dist/linear/identity.js +151 -0
- package/dist/linear/identity.js.map +1 -0
- package/dist/linear/notify.d.ts +35 -0
- package/dist/linear/notify.d.ts.map +1 -0
- package/dist/linear/notify.js +65 -0
- package/dist/linear/notify.js.map +1 -0
- package/dist/linear/ref.d.ts +13 -0
- package/dist/linear/ref.d.ts.map +1 -0
- package/dist/linear/ref.js +90 -0
- package/dist/linear/ref.js.map +1 -0
- package/dist/linear/verify.d.ts +21 -0
- package/dist/linear/verify.d.ts.map +1 -0
- package/dist/linear/verify.js +65 -0
- package/dist/linear/verify.js.map +1 -0
- package/docs/linear-identity-contract.md +133 -0
- package/docs/linear-identity.md +224 -0
- package/get-started.md +39 -0
- package/package.json +1 -1
|
@@ -233,6 +233,48 @@ logs:
|
|
|
233
233
|
# fix_fill: cyan # filled segments on the FIX bar
|
|
234
234
|
|
|
235
235
|
|
|
236
|
+
# ─── Linear ──────────────────────────────────────────────────────────────────
|
|
237
|
+
# Mirror review verdicts onto the Linear issue a PR belongs to.
|
|
238
|
+
# Disabled by default. See docs/linear-identity.md for the full setup guide.
|
|
239
|
+
#
|
|
240
|
+
# Two identity tiers:
|
|
241
|
+
# api_key (T0) — your own Linear key. Writes carry a signature line
|
|
242
|
+
# but Linear still attributes them to your account.
|
|
243
|
+
# client_credentials (T1) — an OAuth app in your workspace. crosscheck mints a
|
|
244
|
+
# token per run and writes render as the app itself
|
|
245
|
+
# (botActor), not as a human. Recommended.
|
|
246
|
+
#
|
|
247
|
+
# A failed T1 token mint ABORTS the run — crosscheck never silently falls back to
|
|
248
|
+
# an API key, because that would re-attribute agent writes to a person.
|
|
249
|
+
# linear:
|
|
250
|
+
# enabled: true
|
|
251
|
+
# auth:
|
|
252
|
+
# mode: client_credentials # api_key | client_credentials
|
|
253
|
+
# # Env var NAMES — never put secrets in this file.
|
|
254
|
+
# api_key_env: LINEAR_API_KEY # used when mode: api_key
|
|
255
|
+
# client_id_env: LINEAR_CLIENT_ID
|
|
256
|
+
# client_secret_env: LINEAR_CLIENT_SECRET
|
|
257
|
+
# # NOTE: "read,write" does NOT cover initiatives — initiative:read and
|
|
258
|
+
# # initiative:write are separate scopes you must request explicitly.
|
|
259
|
+
# scopes: "read,write"
|
|
260
|
+
# identity:
|
|
261
|
+
# actor: crosscheck # createAsUser name in T1
|
|
262
|
+
# signature: "🤖 {actor} · {product}" # first line of every Linear write
|
|
263
|
+
# # Suffix the actor with the step that produced the write, so a review, a fix
|
|
264
|
+
# # and a recheck are distinguishable: crosscheck/review, crosscheck/fix, ...
|
|
265
|
+
# # Set false for one flat actor name.
|
|
266
|
+
# per_step_actor: true
|
|
267
|
+
# comment_on: # which verdicts get mirrored
|
|
268
|
+
# - APPROVE
|
|
269
|
+
# - NEEDS_WORK
|
|
270
|
+
# - BLOCK
|
|
271
|
+
# # Team key prefixes that may be matched as bare refs (IN-2269) in a branch,
|
|
272
|
+
# # title, or body. Leave empty to only follow explicit linear.app issue URLs —
|
|
273
|
+
# # without this, tokens like UTF-8 and SHA-256 have the same shape as a ref.
|
|
274
|
+
# team_keys:
|
|
275
|
+
# - IN
|
|
276
|
+
|
|
277
|
+
|
|
236
278
|
# ─── Branding ────────────────────────────────────────────────────────────────
|
|
237
279
|
# Customize how crosscheck identifies itself in review comments.
|
|
238
280
|
# Useful for teams, shared services, or personal branding.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can-write-verdict.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/can-write-verdict.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { canWriteVerdict } from '../lib/workflow.js';
|
|
3
|
+
describe('canWriteVerdict', () => {
|
|
4
|
+
// Only review and recheck post a verdict, and only a verdict reaches Linear.
|
|
5
|
+
// `crosscheck fix` and `crosscheck resolve` must not abort on a Linear outage.
|
|
6
|
+
it('is true for a workflow containing a review', () => {
|
|
7
|
+
expect(canWriteVerdict([{ type: 'review' }])).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it('is true for a workflow containing a recheck', () => {
|
|
10
|
+
expect(canWriteVerdict([{ type: 'recheck' }])).toBe(true);
|
|
11
|
+
});
|
|
12
|
+
it('is true for the full loop', () => {
|
|
13
|
+
expect(canWriteVerdict([{ type: 'review' }, { type: 'fix' }, { type: 'recheck' }])).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it('is false for a fix-only workflow — the `crosscheck fix` alias', () => {
|
|
16
|
+
expect(canWriteVerdict([{ type: 'fix' }])).toBe(false);
|
|
17
|
+
});
|
|
18
|
+
it('is false for conflict-resolve only — the `crosscheck resolve` alias', () => {
|
|
19
|
+
expect(canWriteVerdict([{ type: 'conflict-resolve' }])).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
it('is false for fix plus conflict-resolve', () => {
|
|
22
|
+
expect(canWriteVerdict([{ type: 'conflict-resolve' }, { type: 'fix' }])).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
it('is false for an empty workflow', () => {
|
|
25
|
+
expect(canWriteVerdict([])).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
it('fails open when the steps are unknown, so a real workflow is never starved', () => {
|
|
28
|
+
expect(canWriteVerdict(undefined)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=can-write-verdict.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"can-write-verdict.test.js","sourceRoot":"","sources":["../../src/__tests__/can-write-verdict.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,6EAA6E;IAC7E,+EAA+E;IAC/E,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC1D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChG,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACxD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACtF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gCAAgC,EAAE,GAAG,EAAE;QACxC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { linearGraphQL, findIssueByIdentifier, postLinearComment } from '../linear/client.js';
|
|
3
|
+
const API_KEY_AUTH = {
|
|
4
|
+
mode: 'api_key',
|
|
5
|
+
token: 'lin_api_secret',
|
|
6
|
+
bearer: false,
|
|
7
|
+
actor: 'crosscheck',
|
|
8
|
+
signature: '🤖 crosscheck · crosscheck',
|
|
9
|
+
signatureTemplate: '🤖 {actor} · {product}',
|
|
10
|
+
product: 'crosscheck',
|
|
11
|
+
};
|
|
12
|
+
const APP_AUTH = {
|
|
13
|
+
mode: 'client_credentials',
|
|
14
|
+
token: 'minted-token',
|
|
15
|
+
bearer: true,
|
|
16
|
+
actor: 'crosscheck',
|
|
17
|
+
signature: '🤖 crosscheck · crosscheck',
|
|
18
|
+
signatureTemplate: '🤖 {actor} · {product}',
|
|
19
|
+
product: 'crosscheck',
|
|
20
|
+
createAsUser: 'crosscheck',
|
|
21
|
+
};
|
|
22
|
+
function jsonFetch(payload, status = 200) {
|
|
23
|
+
return vi.fn(async () => new Response(JSON.stringify(payload), { status }));
|
|
24
|
+
}
|
|
25
|
+
function lastCall(fetchImpl) {
|
|
26
|
+
const calls = fetchImpl.mock.calls;
|
|
27
|
+
return calls[calls.length - 1];
|
|
28
|
+
}
|
|
29
|
+
describe('linearGraphQL', () => {
|
|
30
|
+
it('sends an api_key as a bare Authorization header', async () => {
|
|
31
|
+
const fetchImpl = jsonFetch({ data: { ok: true } });
|
|
32
|
+
await linearGraphQL(API_KEY_AUTH, 'query { ok }', {}, { fetchImpl });
|
|
33
|
+
const [url, init] = lastCall(fetchImpl);
|
|
34
|
+
expect(url).toBe('https://api.linear.app/graphql');
|
|
35
|
+
expect(init.headers.Authorization).toBe('lin_api_secret');
|
|
36
|
+
});
|
|
37
|
+
it('sends a minted app token as a Bearer header', async () => {
|
|
38
|
+
const fetchImpl = jsonFetch({ data: { ok: true } });
|
|
39
|
+
await linearGraphQL(APP_AUTH, 'query { ok }', {}, { fetchImpl });
|
|
40
|
+
expect(lastCall(fetchImpl)[1].headers.Authorization).toBe('Bearer minted-token');
|
|
41
|
+
});
|
|
42
|
+
it('never puts the token in the URL', async () => {
|
|
43
|
+
const fetchImpl = jsonFetch({ data: { ok: true } });
|
|
44
|
+
await linearGraphQL(API_KEY_AUTH, 'query { ok }', {}, { fetchImpl });
|
|
45
|
+
expect(lastCall(fetchImpl)[0]).not.toContain('lin_api_secret');
|
|
46
|
+
});
|
|
47
|
+
it('sends the query and variables as a JSON body', async () => {
|
|
48
|
+
const fetchImpl = jsonFetch({ data: { ok: true } });
|
|
49
|
+
await linearGraphQL(API_KEY_AUTH, 'query($a: String) { ok }', { a: 'b' }, { fetchImpl });
|
|
50
|
+
const body = JSON.parse(lastCall(fetchImpl)[1].body);
|
|
51
|
+
expect(body.query).toBe('query($a: String) { ok }');
|
|
52
|
+
expect(body.variables).toEqual({ a: 'b' });
|
|
53
|
+
});
|
|
54
|
+
it('throws on a GraphQL error without leaking the token', async () => {
|
|
55
|
+
const fetchImpl = jsonFetch({ errors: [{ message: 'Access denied' }] });
|
|
56
|
+
const err = await linearGraphQL(API_KEY_AUTH, 'query { ok }', {}, { fetchImpl })
|
|
57
|
+
.then(() => null, (e) => e);
|
|
58
|
+
expect(err.message).toContain('Access denied');
|
|
59
|
+
expect(err.message).not.toContain('lin_api_secret');
|
|
60
|
+
});
|
|
61
|
+
it('throws on a non-2xx response without leaking the token', async () => {
|
|
62
|
+
const fetchImpl = jsonFetch({ error: 'unauthorized' }, 401);
|
|
63
|
+
const err = await linearGraphQL(API_KEY_AUTH, 'query { ok }', {}, { fetchImpl })
|
|
64
|
+
.then(() => null, (e) => e);
|
|
65
|
+
expect(err.message).toContain('401');
|
|
66
|
+
expect(err.message).not.toContain('lin_api_secret');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe('findIssueByIdentifier', () => {
|
|
70
|
+
it('returns the issue id and url when found', async () => {
|
|
71
|
+
const fetchImpl = jsonFetch({
|
|
72
|
+
data: { issues: { nodes: [{ id: 'uuid-1', identifier: 'IN-2269', url: 'https://linear.app/x/issue/IN-2269' }] } },
|
|
73
|
+
});
|
|
74
|
+
const issue = await findIssueByIdentifier(API_KEY_AUTH, 'IN-2269', { fetchImpl });
|
|
75
|
+
expect(issue).toEqual({ id: 'uuid-1', identifier: 'IN-2269', url: 'https://linear.app/x/issue/IN-2269' });
|
|
76
|
+
});
|
|
77
|
+
it('returns null when the issue does not exist', async () => {
|
|
78
|
+
const fetchImpl = jsonFetch({ data: { issues: { nodes: [] } } });
|
|
79
|
+
expect(await findIssueByIdentifier(API_KEY_AUTH, 'IN-9999', { fetchImpl })).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
it('queries by team key and number parsed from the identifier', async () => {
|
|
82
|
+
const fetchImpl = jsonFetch({ data: { issues: { nodes: [] } } });
|
|
83
|
+
await findIssueByIdentifier(API_KEY_AUTH, 'IN-2269', { fetchImpl });
|
|
84
|
+
const body = JSON.parse(lastCall(fetchImpl)[1].body);
|
|
85
|
+
expect(body.variables).toEqual({ teamKey: 'IN', number: 2269 });
|
|
86
|
+
});
|
|
87
|
+
it('rejects a malformed identifier', async () => {
|
|
88
|
+
const fetchImpl = jsonFetch({ data: { issues: { nodes: [] } } });
|
|
89
|
+
await expect(findIssueByIdentifier(API_KEY_AUTH, 'not-an-id', { fetchImpl })).rejects.toThrow(/identifier/i);
|
|
90
|
+
expect(fetchImpl).not.toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
describe('postLinearComment', () => {
|
|
94
|
+
const ok = () => jsonFetch({
|
|
95
|
+
data: { commentCreate: { success: true, comment: { id: 'c1', url: 'https://linear.app/x/issue/IN-1#comment-c1' } } },
|
|
96
|
+
});
|
|
97
|
+
it('passes createAsUser in client_credentials mode', async () => {
|
|
98
|
+
const fetchImpl = ok();
|
|
99
|
+
await postLinearComment(APP_AUTH, 'uuid-1', 'hello', { fetchImpl });
|
|
100
|
+
const body = JSON.parse(lastCall(fetchImpl)[1].body);
|
|
101
|
+
expect(body.variables.createAsUser).toBe('crosscheck');
|
|
102
|
+
expect(body.variables.issueId).toBe('uuid-1');
|
|
103
|
+
expect(body.variables.body).toBe('hello');
|
|
104
|
+
});
|
|
105
|
+
it('omits createAsUser in api_key mode', async () => {
|
|
106
|
+
const fetchImpl = ok();
|
|
107
|
+
await postLinearComment(API_KEY_AUTH, 'uuid-1', 'hello', { fetchImpl });
|
|
108
|
+
const body = JSON.parse(lastCall(fetchImpl)[1].body);
|
|
109
|
+
expect(body.variables.createAsUser).toBeUndefined();
|
|
110
|
+
});
|
|
111
|
+
it('returns the created comment', async () => {
|
|
112
|
+
const comment = await postLinearComment(APP_AUTH, 'uuid-1', 'hello', { fetchImpl: ok() });
|
|
113
|
+
expect(comment).toEqual({ id: 'c1', url: 'https://linear.app/x/issue/IN-1#comment-c1' });
|
|
114
|
+
});
|
|
115
|
+
it('throws when the mutation reports success: false', async () => {
|
|
116
|
+
const fetchImpl = jsonFetch({ data: { commentCreate: { success: false, comment: null } } });
|
|
117
|
+
await expect(postLinearComment(APP_AUTH, 'uuid-1', 'hello', { fetchImpl })).rejects.toThrow(/rejected/i);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=linear-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-comment.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-comment.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildLinearCommentBody, shouldPostToLinear } from '../linear/comment.js';
|
|
3
|
+
import { parseAnnotation } from '../lib/annotation.js';
|
|
4
|
+
const BASE = {
|
|
5
|
+
signature: '🤖 crosscheck · crosscheck',
|
|
6
|
+
verdict: 'NEEDS_WORK',
|
|
7
|
+
reviewer: 'codex',
|
|
8
|
+
origin: 'claude',
|
|
9
|
+
model: 'gpt-5',
|
|
10
|
+
prUrl: 'https://github.com/acme/app/pull/12',
|
|
11
|
+
prTitle: 'feat: add widget',
|
|
12
|
+
sha: 'abc1234',
|
|
13
|
+
};
|
|
14
|
+
describe('buildLinearCommentBody', () => {
|
|
15
|
+
it('puts the signature on the first line', () => {
|
|
16
|
+
// IN-2260 step 3 requires the signature to lead every Linear write.
|
|
17
|
+
const body = buildLinearCommentBody(BASE);
|
|
18
|
+
expect(body.split('\n')[0]).toBe('🤖 crosscheck · crosscheck');
|
|
19
|
+
});
|
|
20
|
+
it('states the verdict and links back to the PR', () => {
|
|
21
|
+
const body = buildLinearCommentBody(BASE);
|
|
22
|
+
expect(body).toContain('NEEDS_WORK');
|
|
23
|
+
expect(body).toContain('[feat: add widget](https://github.com/acme/app/pull/12)');
|
|
24
|
+
expect(body).toContain('codex');
|
|
25
|
+
});
|
|
26
|
+
it('embeds a parseable crosscheck annotation', () => {
|
|
27
|
+
const parsed = parseAnnotation(buildLinearCommentBody(BASE));
|
|
28
|
+
expect(parsed).toMatchObject({
|
|
29
|
+
origin: 'claude',
|
|
30
|
+
reviewer: 'codex',
|
|
31
|
+
model: 'gpt-5',
|
|
32
|
+
verdict: 'NEEDS_WORK',
|
|
33
|
+
type: 'review',
|
|
34
|
+
service: 'crosscheck',
|
|
35
|
+
sha: 'abc1234',
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
it('includes the summary when provided', () => {
|
|
39
|
+
const body = buildLinearCommentBody({ ...BASE, summary: 'Two blocking issues in auth.ts' });
|
|
40
|
+
expect(body).toContain('Two blocking issues in auth.ts');
|
|
41
|
+
});
|
|
42
|
+
it('omits the summary section when absent', () => {
|
|
43
|
+
const body = buildLinearCommentBody(BASE);
|
|
44
|
+
expect(body).not.toContain('undefined');
|
|
45
|
+
});
|
|
46
|
+
it('renders UNKNOWN for a null verdict', () => {
|
|
47
|
+
const body = buildLinearCommentBody({ ...BASE, verdict: null });
|
|
48
|
+
expect(body).toContain('UNKNOWN');
|
|
49
|
+
expect(parseAnnotation(body)?.verdict).toBe('UNKNOWN');
|
|
50
|
+
});
|
|
51
|
+
it('escapes brackets in the PR title so the markdown link survives', () => {
|
|
52
|
+
const body = buildLinearCommentBody({ ...BASE, prTitle: 'feat: handle [x] and [y]' });
|
|
53
|
+
expect(body).toContain('[feat: handle \\[x\\] and \\[y\\]](https://github.com/acme/app/pull/12)');
|
|
54
|
+
});
|
|
55
|
+
it('escapes nothing that would break the annotation parse', () => {
|
|
56
|
+
const body = buildLinearCommentBody({ ...BASE, prTitle: 'feat: add <!-- crosscheck: fake --> widget' });
|
|
57
|
+
// The real annotation is last, so it still wins.
|
|
58
|
+
expect(parseAnnotation(body)?.reviewer).toBe('codex');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('shouldPostToLinear', () => {
|
|
62
|
+
const DEFAULTS = ['APPROVE', 'NEEDS_WORK', 'BLOCK'];
|
|
63
|
+
it('posts for each configured verdict', () => {
|
|
64
|
+
for (const v of DEFAULTS)
|
|
65
|
+
expect(shouldPostToLinear(v, DEFAULTS)).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it('skips a verdict that is not configured', () => {
|
|
68
|
+
expect(shouldPostToLinear('APPROVE', ['BLOCK'])).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('skips a null verdict by default', () => {
|
|
71
|
+
expect(shouldPostToLinear(null, DEFAULTS)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
it('posts a null verdict when UNKNOWN is opted in', () => {
|
|
74
|
+
expect(shouldPostToLinear(null, ['BLOCK', 'UNKNOWN'])).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
it('skips everything when the list is empty', () => {
|
|
77
|
+
expect(shouldPostToLinear('BLOCK', [])).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe('workflow step metadata', () => {
|
|
81
|
+
// A recheck posted by runWorkflow must not claim to be a round-1 review — the
|
|
82
|
+
// Linear annotation has to agree with the GitHub one or parsers see two truths.
|
|
83
|
+
it('defaults to a round-1 review', () => {
|
|
84
|
+
const parsed = parseAnnotation(buildLinearCommentBody(BASE));
|
|
85
|
+
expect(parsed).toMatchObject({ type: 'review', round: 1 });
|
|
86
|
+
});
|
|
87
|
+
it('carries an explicit step type', () => {
|
|
88
|
+
const body = buildLinearCommentBody({ ...BASE, stepType: 'recheck' });
|
|
89
|
+
expect(parseAnnotation(body)).toMatchObject({ type: 'recheck' });
|
|
90
|
+
});
|
|
91
|
+
it('carries an explicit round', () => {
|
|
92
|
+
const body = buildLinearCommentBody({ ...BASE, stepType: 'recheck', round: 3 });
|
|
93
|
+
expect(parseAnnotation(body)).toMatchObject({ type: 'recheck', round: 3 });
|
|
94
|
+
});
|
|
95
|
+
it('matches what the GitHub annotation would carry for the same step', () => {
|
|
96
|
+
const body = buildLinearCommentBody({ ...BASE, stepType: 'fix', round: 2 });
|
|
97
|
+
const parsed = parseAnnotation(body);
|
|
98
|
+
expect(parsed?.type).toBe('fix');
|
|
99
|
+
expect(parsed?.round).toBe(2);
|
|
100
|
+
expect(parsed?.sha).toBe(BASE.sha);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
describe('verdict normalisation', () => {
|
|
104
|
+
// parseVerdict yields "NEEDS WORK"; config and the annotation use "NEEDS_WORK".
|
|
105
|
+
// Comparing raw dropped every NEEDS WORK verdict on the default config.
|
|
106
|
+
it('matches the spaced verdict against the underscored filter', () => {
|
|
107
|
+
expect(shouldPostToLinear('NEEDS WORK', ['NEEDS_WORK', 'BLOCK'])).toBe(true);
|
|
108
|
+
});
|
|
109
|
+
it('matches the underscored verdict too', () => {
|
|
110
|
+
expect(shouldPostToLinear('NEEDS_WORK', ['NEEDS_WORK', 'BLOCK'])).toBe(true);
|
|
111
|
+
});
|
|
112
|
+
it('tolerates a spaced filter entry', () => {
|
|
113
|
+
expect(shouldPostToLinear('NEEDS_WORK', ['NEEDS WORK'])).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
it('is case insensitive', () => {
|
|
116
|
+
expect(shouldPostToLinear('needs work', ['NEEDS_WORK'])).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
it('still excludes a verdict that is genuinely not configured', () => {
|
|
119
|
+
expect(shouldPostToLinear('NEEDS WORK', ['BLOCK'])).toBe(false);
|
|
120
|
+
expect(shouldPostToLinear('APPROVE', ['NEEDS_WORK', 'BLOCK'])).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
it('still maps null to UNKNOWN', () => {
|
|
123
|
+
expect(shouldPostToLinear(null, ['UNKNOWN'])).toBe(true);
|
|
124
|
+
expect(shouldPostToLinear(null, ['NEEDS_WORK', 'BLOCK'])).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
describe('the visible line names the step', () => {
|
|
128
|
+
// The annotation carries type=recheck; the human-readable line saying "review"
|
|
129
|
+
// contradicted it two lines above.
|
|
130
|
+
it('says review by default', () => {
|
|
131
|
+
expect(buildLinearCommentBody(BASE)).toContain('review of [');
|
|
132
|
+
});
|
|
133
|
+
it('says recheck for a recheck', () => {
|
|
134
|
+
const body = buildLinearCommentBody({ ...BASE, stepType: 'recheck' });
|
|
135
|
+
expect(body).toContain('recheck of [');
|
|
136
|
+
expect(body).not.toContain(' review of [');
|
|
137
|
+
});
|
|
138
|
+
it('says fix for a fix step', () => {
|
|
139
|
+
expect(buildLinearCommentBody({ ...BASE, stepType: 'fix' })).toContain('fix for [');
|
|
140
|
+
});
|
|
141
|
+
it('says conflict resolution for a conflict-resolve step', () => {
|
|
142
|
+
expect(buildLinearCommentBody({ ...BASE, stepType: 'conflict-resolve' }))
|
|
143
|
+
.toContain('conflict resolution for [');
|
|
144
|
+
});
|
|
145
|
+
it('agrees with the annotation it embeds', () => {
|
|
146
|
+
const body = buildLinearCommentBody({ ...BASE, stepType: 'recheck', round: 2 });
|
|
147
|
+
expect(body).toContain('recheck of [');
|
|
148
|
+
expect(parseAnnotation(body)).toMatchObject({ type: 'recheck', round: 2 });
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
//# sourceMappingURL=linear-comment.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-comment.test.js","sourceRoot":"","sources":["../../src/__tests__/linear-comment.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-identity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/linear-identity.test.ts"],"names":[],"mappings":""}
|