thurview 0.19.0 → 0.21.0
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/README.md +3 -5
- package/dist/cli.js +149 -42
- package/dist/cli.js.map +1 -1
- package/dist/diff.js +3 -3
- package/dist/diff.js.map +1 -1
- package/dist/document/compile.js +1 -1
- package/dist/document/compile.js.map +1 -1
- package/dist/export.js +10 -25
- package/dist/export.js.map +1 -1
- package/dist/forge/github.js +41 -0
- package/dist/forge/github.js.map +1 -1
- package/dist/forge/gitlab.js +84 -23
- package/dist/forge/gitlab.js.map +1 -1
- package/dist/forge/types.js +4 -0
- package/dist/forge/types.js.map +1 -1
- package/dist/git.js +0 -9
- package/dist/git.js.map +1 -1
- package/dist/highlight-theme.js +37 -14
- package/dist/highlight-theme.js.map +1 -1
- package/dist/highlight.js +3 -15
- package/dist/highlight.js.map +1 -1
- package/dist/pr-review/categories.js +51 -0
- package/dist/pr-review/categories.js.map +1 -0
- package/dist/pr-review/follow.js +208 -0
- package/dist/pr-review/follow.js.map +1 -0
- package/dist/pr-review/format.js +227 -0
- package/dist/pr-review/format.js.map +1 -0
- package/dist/server/server.js +32 -47
- package/dist/server/server.js.map +1 -1
- package/dist/ui/app.css +65 -30
- package/dist/ui/app.js +15 -16
- package/dist/ui/app.js.map +2 -2
- package/dist/ui/index.html +21 -0
- package/package.json +1 -1
- package/skills/thurview/SKILL.md +10 -20
- package/skills/thurview/references/document-authoring.md +1 -2
- package/skills/thurview/references/lifecycle.md +0 -1
- package/skills/thurview-design/SKILL.md +4 -7
- package/skills/thurview-explain/SKILL.md +6 -7
- package/skills/thurview-pr-review/SKILL.md +181 -0
- package/skills/thurview-pr-review/references/forges.md +47 -0
- package/dist/theme.js +0 -290
- package/dist/theme.js.map +0 -1
- package/skills/thurview/references/theme.md +0 -89
package/dist/highlight.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createHighlighter, bundledLanguages
|
|
1
|
+
import { createHighlighter, bundledLanguages } from "shiki";
|
|
2
2
|
import { theme } from "./highlight-theme.js";
|
|
3
3
|
const EXT = {
|
|
4
4
|
ts: "typescript",
|
|
@@ -84,21 +84,11 @@ export function languageFor(path) {
|
|
|
84
84
|
return EXT[ext] ?? "text";
|
|
85
85
|
}
|
|
86
86
|
let hl = null;
|
|
87
|
-
const registered = new Set(["thurview"]);
|
|
88
87
|
async function highlighter() {
|
|
89
88
|
if (!hl)
|
|
90
89
|
hl = createHighlighter({ themes: [theme], langs: [] });
|
|
91
90
|
return hl;
|
|
92
91
|
}
|
|
93
|
-
/** Register a per-review highlighter theme; repeated registrations are no-ops. */
|
|
94
|
-
export async function registerTheme(reg) {
|
|
95
|
-
const name = reg.name ?? "thurview";
|
|
96
|
-
if (!registered.has(name)) {
|
|
97
|
-
await (await highlighter()).loadTheme(reg);
|
|
98
|
-
registered.add(name);
|
|
99
|
-
}
|
|
100
|
-
return name;
|
|
101
|
-
}
|
|
102
92
|
function escapeHtml(s) {
|
|
103
93
|
return s
|
|
104
94
|
.replace(/&/g, "&")
|
|
@@ -108,9 +98,7 @@ function escapeHtml(s) {
|
|
|
108
98
|
}
|
|
109
99
|
const cache = new Map();
|
|
110
100
|
/** Highlight a whole file; returns one HTML fragment per line (no trailing newline line). */
|
|
111
|
-
export async function highlightLines(code, lang, cacheKey
|
|
112
|
-
if (cacheKey)
|
|
113
|
-
cacheKey = `${themeName}|${cacheKey}`;
|
|
101
|
+
export async function highlightLines(code, lang, cacheKey) {
|
|
114
102
|
if (cacheKey && cache.has(cacheKey))
|
|
115
103
|
return cache.get(cacheKey);
|
|
116
104
|
const lines = code.endsWith("\n") ? code.slice(0, -1).split("\n") : code.split("\n");
|
|
@@ -129,7 +117,7 @@ export async function highlightLines(code, lang, cacheKey, themeName = "thurview
|
|
|
129
117
|
return remember(cacheKey, out);
|
|
130
118
|
}
|
|
131
119
|
}
|
|
132
|
-
const result = h.codeToTokens(code, { lang: lang, theme:
|
|
120
|
+
const result = h.codeToTokens(code, { lang: lang, theme: "thurview" });
|
|
133
121
|
out = result.tokens.map((toks) => toks
|
|
134
122
|
.map((t) => {
|
|
135
123
|
const style = [
|
package/dist/highlight.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"highlight.js","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"highlight.js","sourceRoot":"","sources":["../src/highlight.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAoB,MAAM,OAAO,CAAC;AAC9E,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C,MAAM,GAAG,GAA2B;IAClC,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,MAAM;IACV,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,EAAE,EAAE,KAAK;IACT,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,OAAO;IACd,CAAC,EAAE,aAAa;IAChB,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,YAAY;IACjB,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,MAAM;IACX,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,UAAU;IACd,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,OAAO,EAAE,SAAS;IAClB,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,KAAK;IACT,GAAG,EAAE,KAAK;IACV,UAAU,EAAE,YAAY;IACxB,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,QAAQ;IAChB,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,QAAQ;IACb,EAAE,EAAE,SAAS;IACb,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,KAAK;IACV,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,UAAU;IAChB,EAAE,EAAE,UAAU;IACd,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,GAAG,EAAE,MAAM;IACX,GAAG,EAAE,KAAK;IACV,GAAG,EAAE,KAAK;CACX,CAAC;AAEF,MAAM,IAAI,GAA2B;IACnC,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;IACpB,UAAU,EAAE,OAAO;CACpB,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACzC,IAAI,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAW,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IAC3F,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC;AAC5B,CAAC;AAED,IAAI,EAAE,GAAgC,IAAI,CAAC;AAE3C,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC,EAAE;QAAE,EAAE,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IAChE,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC;SACL,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;AAE1C,6FAA6F;AAC7F,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,IAAY,EACZ,QAAiB;IAEjB,IAAI,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;IACjE,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrF,IAAI,GAAa,CAAC;IAClB,IAAI,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,gBAAgB,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC9E,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,GAAG,MAAM,WAAW,EAAE,CAAC;QAC9B,IAAI,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC;gBACH,MAAM,CAAC,CAAC,YAAY,CAAC,IAAa,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAC5B,OAAO,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACjC,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAa,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAChF,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,IAAI;aACD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACT,MAAM,KAAK,GAAG;gBACZ,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;gBACjC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE;aACrF;iBACE,MAAM,CAAC,OAAO,CAAC;iBACf,IAAI,CAAC,GAAG,CAAC,CAAC;YACb,OAAO,KAAK;gBACV,CAAC,CAAC,gBAAgB,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS;gBAC1D,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC;aACD,IAAI,CAAC,EAAE,CAAC,CACZ,CAAC;QACF,gFAAgF;QAChF,OAAO,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;YAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/C,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM;YAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3D,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,QAAQ,CAAC,GAAuB,EAAE,CAAW;IACpD,IAAI,GAAG,EAAE,CAAC;QACR,IAAI,KAAK,CAAC,IAAI,GAAG,GAAG;YAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC,CAAC;QACxE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The closed sets a change request review speaks in. Each is an enum in code
|
|
3
|
+
* and a table in `skills/thurview-pr-review/SKILL.md`, and a test holds the
|
|
4
|
+
* two to the same words: a category the docs do not define is one an author
|
|
5
|
+
* cannot act on, and one the code does not know is one the summary cannot
|
|
6
|
+
* count.
|
|
7
|
+
*/
|
|
8
|
+
/** Every finding is exactly one of these. Order is the summary table's order. */
|
|
9
|
+
export const CATEGORIES = {
|
|
10
|
+
bug: { label: "Bug", definition: "The code does the wrong thing for an input it accepts." },
|
|
11
|
+
security: {
|
|
12
|
+
label: "Security",
|
|
13
|
+
definition: "An attacker gains access, data or execution they should not have.",
|
|
14
|
+
},
|
|
15
|
+
performance: {
|
|
16
|
+
label: "Performance",
|
|
17
|
+
definition: "Time, memory or calls grow worse than the change needs.",
|
|
18
|
+
},
|
|
19
|
+
reliability: {
|
|
20
|
+
label: "Reliability",
|
|
21
|
+
definition: "A failure, retry, timeout or race is handled wrongly or not at all.",
|
|
22
|
+
},
|
|
23
|
+
compatibility: {
|
|
24
|
+
label: "Compatibility",
|
|
25
|
+
definition: "A public API, schema, config, flag or data format breaks its callers.",
|
|
26
|
+
},
|
|
27
|
+
maintainability: {
|
|
28
|
+
label: "Maintainability",
|
|
29
|
+
definition: "The next change here is harder: duplication, dead code, a misleading name.",
|
|
30
|
+
},
|
|
31
|
+
tests: {
|
|
32
|
+
label: "Tests",
|
|
33
|
+
definition: "A behaviour the change adds or alters is not tested, or a test proves nothing.",
|
|
34
|
+
},
|
|
35
|
+
docs: {
|
|
36
|
+
label: "Docs",
|
|
37
|
+
definition: "A comment, README or doc now says something the code does not do.",
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export const CATEGORY_IDS = Object.keys(CATEGORIES);
|
|
41
|
+
/** `blocking` must be fixed before merge; `nit` is taste, and says so. */
|
|
42
|
+
export const SEVERITIES = ["blocking", "non-blocking", "nit"];
|
|
43
|
+
/** How safe the change is to merge, as the summary's first number. */
|
|
44
|
+
export const CONFIDENCE = {
|
|
45
|
+
1: "Do not merge: it breaks something that works today.",
|
|
46
|
+
2: "Not yet: a blocking finding is open.",
|
|
47
|
+
3: "Unsure: a risk is named that the review could not rule out.",
|
|
48
|
+
4: "Safe to merge; non-blocking findings are worth a look.",
|
|
49
|
+
5: "Safe to merge; nothing open beyond nits.",
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=categories.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"categories.js","sourceRoot":"","sources":["../../src/pr-review/categories.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,GAAG,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,wDAAwD,EAAE;IAC3F,QAAQ,EAAE;QACR,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,mEAAmE;KAChF;IACD,WAAW,EAAE;QACX,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,yDAAyD;KACtE;IACD,WAAW,EAAE;QACX,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,qEAAqE;KAClF;IACD,aAAa,EAAE;QACb,KAAK,EAAE,eAAe;QACtB,UAAU,EAAE,uEAAuE;KACpF;IACD,eAAe,EAAE;QACf,KAAK,EAAE,iBAAiB;QACxB,UAAU,EAAE,4EAA4E;KACzF;IACD,KAAK,EAAE;QACL,KAAK,EAAE,OAAO;QACd,UAAU,EAAE,gFAAgF;KAC7F;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,mEAAmE;KAChF;CACO,CAAC;AAGX,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAA8B,CAAC;AAEjF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,UAAU,EAAE,cAAc,EAAE,KAAK,CAAU,CAAC;AAGvE,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,CAAC,EAAE,qDAAqD;IACxD,CAAC,EAAE,sCAAsC;IACzC,CAAC,EAAE,6DAA6D;IAChE,CAAC,EAAE,wDAAwD;IAC3D,CAAC,EAAE,0CAA0C;CACrC,CAAC"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { AxiError } from "axi-sdk-js";
|
|
2
|
+
import { bareSummary, checkPass, findingId, readFindingMarker, readSummaryMarker, renderFinding, renderSummary, restate, statusLine, } from "./format.js";
|
|
3
|
+
export const STOP_LABEL = "thurview:stop";
|
|
4
|
+
const STOP_COMMAND = /^\s*\/thurview\s+stop\b/i;
|
|
5
|
+
function maxId(...ids) {
|
|
6
|
+
return ids.reduce((a, b) => (Number(b) > Number(a) ? b : a), "0");
|
|
7
|
+
}
|
|
8
|
+
export async function readState(ctx) {
|
|
9
|
+
const [notes, prior, me] = await Promise.all([
|
|
10
|
+
ctx.forge.notes(ctx.repo, ctx.cr),
|
|
11
|
+
ctx.forge.prior(ctx.repo, ctx.cr),
|
|
12
|
+
ctx.forge.whoami(ctx.repo),
|
|
13
|
+
]);
|
|
14
|
+
// Only this account's own marker counts: anyone can paste one, and
|
|
15
|
+
// trusting it would let a commenter silence the loop or have it edit
|
|
16
|
+
// their comment.
|
|
17
|
+
let summary = null;
|
|
18
|
+
for (const note of notes) {
|
|
19
|
+
const marker = note.author === me ? readSummaryMarker(note.body) : null;
|
|
20
|
+
if (marker) {
|
|
21
|
+
// A change request reopened after it closed is followed again.
|
|
22
|
+
const ended = marker.state === "merged" || marker.state === "closed";
|
|
23
|
+
summary = {
|
|
24
|
+
note,
|
|
25
|
+
marker: ended && ctx.cr.state === "open" ? { ...marker, state: "active" } : marker,
|
|
26
|
+
};
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const findings = [];
|
|
31
|
+
for (const t of prior.threads) {
|
|
32
|
+
const m = t.author === me ? readFindingMarker(t.messages[0]?.body ?? "") : null;
|
|
33
|
+
if (!m)
|
|
34
|
+
continue;
|
|
35
|
+
findings.push({
|
|
36
|
+
id: m.id,
|
|
37
|
+
category: m.category,
|
|
38
|
+
severity: m.severity,
|
|
39
|
+
title: m.title,
|
|
40
|
+
...(t.path ? { path: t.path } : {}),
|
|
41
|
+
...(t.line ? { line: t.line } : {}),
|
|
42
|
+
open: !t.resolved,
|
|
43
|
+
thread: t.id,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const seen = summary?.marker.seen ?? "0";
|
|
47
|
+
const command = notes.find((n) => n !== summary?.note && STOP_COMMAND.test(n.body) && Number(n.id) > Number(seen));
|
|
48
|
+
const stop = ctx.cr.labels.includes(STOP_LABEL)
|
|
49
|
+
? `the ${STOP_LABEL} label`
|
|
50
|
+
: command
|
|
51
|
+
? `a /thurview stop comment by @${command.author}`
|
|
52
|
+
: null;
|
|
53
|
+
return { summary, findings, stop, newest: maxId(...notes.map((n) => n.id)) };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One pass on the current head. Everything is checked before anything is
|
|
57
|
+
* posted; then the new findings, then the fixed threads, then the summary
|
|
58
|
+
* last, so a pass that fails part-way leaves a summary that still describes
|
|
59
|
+
* the head before it, and running it again posts only what is missing.
|
|
60
|
+
*/
|
|
61
|
+
export async function sync(ctx, pass, opts = {}) {
|
|
62
|
+
const { forge, repo, cr } = ctx;
|
|
63
|
+
if (cr.state !== "open")
|
|
64
|
+
throw new AxiError(`the change request is ${cr.state}`, "CONFLICT", [
|
|
65
|
+
"There is nothing left to review; the follow loop ends here",
|
|
66
|
+
]);
|
|
67
|
+
checkPass(pass);
|
|
68
|
+
if (!cr.head.startsWith(pass.head))
|
|
69
|
+
throw new AxiError(`the pass reviewed ${pass.head.slice(0, 12)}, and the head is now ${cr.head.slice(0, 12)}`, "CONFLICT", [
|
|
70
|
+
`Review \`git diff ${pass.head.slice(0, 12)} ${cr.head.slice(0, 12)}\` as well, then sync with head ${cr.head}`,
|
|
71
|
+
"Its line numbers are the old head's, so posting it would land comments on the wrong lines",
|
|
72
|
+
]);
|
|
73
|
+
const st = await readState(ctx);
|
|
74
|
+
const status = st.summary?.marker.state;
|
|
75
|
+
if (st.stop || (status && status !== "active"))
|
|
76
|
+
throw new AxiError(`the review of this change request is stopped (${st.stop ?? `the summary says ${status}`})`, "CONFLICT", [`Run \`thurview pr-review start --change ${cr.number}\` if the reader asked to resume`]);
|
|
77
|
+
// A finding resolved already - by an earlier run of this same pass, or by
|
|
78
|
+
// hand - is done; only an id never posted is a mistake.
|
|
79
|
+
const fixed = (pass.fixed ?? []).filter((id) => st.findings.some((x) => x.id === id && x.open));
|
|
80
|
+
for (const id of pass.fixed ?? []) {
|
|
81
|
+
if (!st.findings.some((x) => x.id === id))
|
|
82
|
+
throw new AxiError(`no open finding ${id} on this change request`, "NOT_FOUND", [
|
|
83
|
+
`The open ones: ${st.findings
|
|
84
|
+
.filter((x) => x.open)
|
|
85
|
+
.map((x) => x.id)
|
|
86
|
+
.join(", ") || "none"}`,
|
|
87
|
+
"Run `thurview pr-review status` for their ids",
|
|
88
|
+
]);
|
|
89
|
+
}
|
|
90
|
+
const fresh = [];
|
|
91
|
+
const duplicates = [];
|
|
92
|
+
for (const f of pass.findings ?? []) {
|
|
93
|
+
const id = findingId(f);
|
|
94
|
+
if (st.findings.some((x) => x.id === id && x.open) || fresh.some((x) => x.id === id))
|
|
95
|
+
duplicates.push(id);
|
|
96
|
+
else
|
|
97
|
+
fresh.push({ id, f });
|
|
98
|
+
}
|
|
99
|
+
const open = [
|
|
100
|
+
...st.findings.filter((x) => x.open && !fixed.includes(x.id)),
|
|
101
|
+
...fresh.map(({ f }) => ({ category: f.category, severity: f.severity })),
|
|
102
|
+
];
|
|
103
|
+
const blocking = open.filter((x) => x.severity === "blocking").length;
|
|
104
|
+
if (blocking && pass.confidence >= 3)
|
|
105
|
+
throw new AxiError(`confidence ${pass.confidence} beside ${blocking} open blocking finding${blocking === 1 ? "" : "s"}`, "VALIDATION_ERROR", [
|
|
106
|
+
"A blocking finding means the change is not safe to merge: confidence is 1 or 2",
|
|
107
|
+
"Or the finding is not blocking: say so with its severity",
|
|
108
|
+
]);
|
|
109
|
+
const marker = {
|
|
110
|
+
head: cr.head,
|
|
111
|
+
state: "active",
|
|
112
|
+
seen: maxId(st.newest, st.summary?.marker.seen ?? "0"),
|
|
113
|
+
};
|
|
114
|
+
const body = renderSummary(pass, marker, cr.author, open);
|
|
115
|
+
const result = {
|
|
116
|
+
summary: opts.dryRun ? "dry-run" : st.summary ? "edited" : "created",
|
|
117
|
+
head: cr.head,
|
|
118
|
+
posted: fresh.map(({ id, f }) => ({ id, at: `${f.path}:${f.line}` })),
|
|
119
|
+
resolved: fixed,
|
|
120
|
+
duplicates,
|
|
121
|
+
open: open.length,
|
|
122
|
+
blocking,
|
|
123
|
+
body,
|
|
124
|
+
};
|
|
125
|
+
if (opts.dryRun)
|
|
126
|
+
return result;
|
|
127
|
+
for (const { f } of fresh)
|
|
128
|
+
await forge.comment(repo, cr, {
|
|
129
|
+
path: f.path,
|
|
130
|
+
line: f.line,
|
|
131
|
+
...(f.startLine && f.startLine < f.line ? { startLine: f.startLine } : {}),
|
|
132
|
+
...(f.side ? { side: f.side } : {}),
|
|
133
|
+
body: renderFinding(f, pass.signoff, forge.id),
|
|
134
|
+
});
|
|
135
|
+
const reply = `Fixed in ${cr.head.slice(0, 7)}.${pass.signoff ? `\n\n${pass.signoff}` : ""}`;
|
|
136
|
+
for (const id of fixed)
|
|
137
|
+
await forge.reply(repo, cr, st.findings.find((x) => x.id === id && x.open).thread, reply, true);
|
|
138
|
+
if (st.summary)
|
|
139
|
+
await forge.editNote(repo, cr, st.summary.note.id, body);
|
|
140
|
+
else
|
|
141
|
+
await forge.postNote(repo, cr, body);
|
|
142
|
+
return result;
|
|
143
|
+
}
|
|
144
|
+
/** Rewrite the summary's state and opening line, or post a bare one when there is none. */
|
|
145
|
+
async function mark(ctx, st, state, opening) {
|
|
146
|
+
const m = {
|
|
147
|
+
head: st.summary?.marker.head ?? ctx.cr.head,
|
|
148
|
+
state,
|
|
149
|
+
seen: maxId(st.newest, st.summary?.marker.seen ?? "0"),
|
|
150
|
+
};
|
|
151
|
+
const line = opening ?? statusLine(m);
|
|
152
|
+
if (st.summary)
|
|
153
|
+
await ctx.forge.editNote(ctx.repo, ctx.cr, st.summary.note.id, restate(st.summary.note.body, m, line));
|
|
154
|
+
else
|
|
155
|
+
await ctx.forge.postNote(ctx.repo, ctx.cr, bareSummary(m, line));
|
|
156
|
+
}
|
|
157
|
+
/** Stop following: the summary says so, and every later wait answers `stopped`. */
|
|
158
|
+
export async function stopReview(ctx) {
|
|
159
|
+
await mark(ctx, await readState(ctx), "stopped");
|
|
160
|
+
}
|
|
161
|
+
/** Resume after a stop. Stop commands posted before now are spent. */
|
|
162
|
+
export async function startReview(ctx) {
|
|
163
|
+
if (ctx.cr.labels.includes(STOP_LABEL))
|
|
164
|
+
throw new AxiError(`the change request still carries ${STOP_LABEL}`, "CONFLICT", [
|
|
165
|
+
"Remove the label first; it is the reader's stop, and only they lift it",
|
|
166
|
+
]);
|
|
167
|
+
const st = await readState(ctx);
|
|
168
|
+
if (!st.summary)
|
|
169
|
+
return;
|
|
170
|
+
await mark(ctx, st, "active", "Next: me — review resumed; the next push gets a pass.");
|
|
171
|
+
}
|
|
172
|
+
const realSleep = (s) => new Promise((r) => setTimeout(r, s * 1000));
|
|
173
|
+
const realNow = () => Date.now() / 1000;
|
|
174
|
+
/**
|
|
175
|
+
* Block until there is something to do: a head the last pass did not see, or
|
|
176
|
+
* an end. An end is written to the summary here, so a loop that is killed
|
|
177
|
+
* right after still left the change request saying why it stopped.
|
|
178
|
+
*/
|
|
179
|
+
export async function waitForEvent(forge, repo, ref, opts) {
|
|
180
|
+
const sleep = opts.sleep ?? realSleep;
|
|
181
|
+
const now = opts.now ?? realNow;
|
|
182
|
+
const start = now();
|
|
183
|
+
for (;;) {
|
|
184
|
+
const cr = await forge.get(repo, ref);
|
|
185
|
+
const ctx = { forge, repo, cr };
|
|
186
|
+
const st = await readState(ctx);
|
|
187
|
+
const status = st.summary?.marker.state;
|
|
188
|
+
if (cr.state === "merged" || cr.state === "closed") {
|
|
189
|
+
if (st.summary && status !== cr.state)
|
|
190
|
+
await mark(ctx, st, cr.state);
|
|
191
|
+
return { event: cr.state, head: cr.head };
|
|
192
|
+
}
|
|
193
|
+
if (status === "stopped")
|
|
194
|
+
return { event: "stopped", head: cr.head };
|
|
195
|
+
if (st.stop) {
|
|
196
|
+
await mark(ctx, st, "stopped");
|
|
197
|
+
return { event: "stopped", head: cr.head, why: st.stop };
|
|
198
|
+
}
|
|
199
|
+
if (!st.summary)
|
|
200
|
+
return { event: "push", since: null, head: cr.head };
|
|
201
|
+
if (st.summary.marker.head !== cr.head)
|
|
202
|
+
return { event: "push", since: st.summary.marker.head, head: cr.head };
|
|
203
|
+
if (now() - start + opts.interval > opts.timeout)
|
|
204
|
+
return { event: "none", head: cr.head };
|
|
205
|
+
await sleep(opts.interval);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=follow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"follow.js","sourceRoot":"","sources":["../../src/pr-review/follow.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,OAAO,EACL,WAAW,EACX,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,OAAO,EACP,UAAU,GAKX,MAAM,aAAa,CAAC;AAyBrB,MAAM,CAAC,MAAM,UAAU,GAAG,eAAe,CAAC;AAC1C,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAsBhD,SAAS,KAAK,CAAC,GAAG,GAAa;IAC7B,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAQ;IACtC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC3C,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;QACjC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IACH,mEAAmE;IACnE,qEAAqE;IACrE,iBAAiB;IACjB,IAAI,OAAO,GAA2B,IAAI,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxE,IAAI,MAAM,EAAE,CAAC;YACX,+DAA+D;YAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC;YACrE,OAAO,GAAG;gBACR,IAAI;gBACJ,MAAM,EAAE,KAAK,IAAI,GAAG,CAAC,EAAE,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM;aACnF,CAAC;YACF,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,QAAQ,GAAmB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChF,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,QAAQ,CAAC,IAAI,CAAC;YACZ,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ;YACjB,MAAM,EAAE,CAAC,CAAC,EAAE;SACb,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,OAAO,EAAE,IAAI,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CACvF,CAAC;IACF,MAAM,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC7C,CAAC,CAAC,OAAO,UAAU,QAAQ;QAC3B,CAAC,CAAC,OAAO;YACP,CAAC,CAAC,gCAAgC,OAAO,CAAC,MAAM,EAAE;YAClD,CAAC,CAAC,IAAI,CAAC;IACX,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;AAC/E,CAAC;AAaD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,GAAQ,EACR,IAAU,EACV,IAAI,GAAyB,EAAE;IAE/B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAChC,IAAI,EAAE,CAAC,KAAK,KAAK,MAAM;QACrB,MAAM,IAAI,QAAQ,CAAC,yBAAyB,EAAE,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE;YAClE,4DAA4D;SAC7D,CAAC,CAAC;IACL,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAChC,MAAM,IAAI,QAAQ,CAChB,qBAAqB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,yBAAyB,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAC1F,UAAU,EACV;YACE,qBAAqB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,mCAAmC,EAAE,CAAC,IAAI,EAAE;YAC/G,2FAA2F;SAC5F,CACF,CAAC;IACJ,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;IACxC,IAAI,EAAE,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,QAAQ,CAAC;QAC5C,MAAM,IAAI,QAAQ,CAChB,iDAAiD,EAAE,CAAC,IAAI,IAAI,oBAAoB,MAAM,EAAE,GAAG,EAC3F,UAAU,EACV,CAAC,2CAA2C,EAAE,CAAC,MAAM,kCAAkC,CAAC,CACzF,CAAC;IACJ,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChG,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,yBAAyB,EAAE,WAAW,EAAE;gBAC9E,kBACE,EAAE,CAAC,QAAQ;qBACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;qBACrB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;qBAChB,IAAI,CAAC,IAAI,CAAC,IAAI,MACnB,EAAE;gBACF,+CAA+C;aAChD,CAAC,CAAC;IACP,CAAC;IACD,MAAM,KAAK,GAAiC,EAAE,CAAC;IAC/C,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;YAClF,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;YACjB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,MAAM,IAAI,GAAG;QACX,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7D,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;KAC1E,CAAC;IACF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM,CAAC;IACtE,IAAI,QAAQ,IAAI,IAAI,CAAC,UAAU,IAAI,CAAC;QAClC,MAAM,IAAI,QAAQ,CAChB,cAAc,IAAI,CAAC,UAAU,WAAW,QAAQ,yBAAyB,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,EACpG,kBAAkB,EAClB;YACE,gFAAgF;YAChF,0DAA0D;SAC3D,CACF,CAAC;IACJ,MAAM,MAAM,GAAkB;QAC5B,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;KACvD,CAAC;IACF,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAe;QACzB,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;QACpE,IAAI,EAAE,EAAE,CAAC,IAAI;QACb,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACrE,QAAQ,EAAE,KAAK;QACf,UAAU;QACV,IAAI,EAAE,IAAI,CAAC,MAAM;QACjB,QAAQ;QACR,IAAI;KACL,CAAC;IACF,IAAI,IAAI,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAE/B,KAAK,MAAM,EAAE,CAAC,EAAE,IAAI,KAAK;QACvB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE;YAC5B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,IAAI,EAAE,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;SAC/C,CAAC,CAAC;IACL,MAAM,KAAK,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IAC7F,KAAK,MAAM,EAAE,IAAI,KAAK;QACpB,MAAM,KAAK,CAAC,KAAK,CACf,IAAI,EACJ,EAAE,EACF,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAE,CAAC,MAAM,EACtD,KAAK,EACL,IAAI,CACL,CAAC;IACJ,IAAI,EAAE,CAAC,OAAO;QAAE,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;;QACpE,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,2FAA2F;AAC3F,KAAK,UAAU,IAAI,CAAC,GAAQ,EAAE,EAAe,EAAE,KAAmB,EAAE,OAAgB;IAClF,MAAM,CAAC,GAAkB;QACvB,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI;QAC5C,KAAK;QACL,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;KACvD,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAE,CAAC;IACvC,IAAI,EAAE,CAAC,OAAO;QACZ,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CACtB,GAAG,CAAC,IAAI,EACR,GAAG,CAAC,EAAE,EACN,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAClB,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CACvC,CAAC;;QACC,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,mFAAmF;AACnF,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAQ;IACvC,MAAM,IAAI,CAAC,GAAG,EAAE,MAAM,SAAS,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;AACnD,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAQ;IACxC,IAAI,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QACpC,MAAM,IAAI,QAAQ,CAAC,oCAAoC,UAAU,EAAE,EAAE,UAAU,EAAE;YAC/E,wEAAwE;SACzE,CAAC,CAAC;IACL,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,OAAO;QAAE,OAAO;IACxB,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,uDAAuD,CAAC,CAAC;AACzF,CAAC;AAgBD,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACnF,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAkB,EAClB,IAAY,EACZ,GAAW,EACX,IAAiB;IAEjB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC;IACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;IACpB,SAAS,CAAC;QACR,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,GAAG,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;QACxC,IAAI,EAAE,CAAC,KAAK,KAAK,QAAQ,IAAI,EAAE,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACnD,IAAI,EAAE,CAAC,OAAO,IAAI,MAAM,KAAK,EAAE,CAAC,KAAK;gBAAE,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;YACrE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC;QACD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QACrE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,SAAS,CAAC,CAAC;YAC/B,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,EAAE,CAAC,OAAO;YAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QACtE,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,IAAI;YACpC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QACzE,IAAI,GAAG,EAAE,GAAG,KAAK,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1F,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { AxiError } from "axi-sdk-js";
|
|
4
|
+
import { CATEGORIES, CATEGORY_IDS, SEVERITIES, } from "./categories.js";
|
|
5
|
+
/**
|
|
6
|
+
* What a change request review posts, and how it reads it back. Everything a
|
|
7
|
+
* follow loop needs to resume lives in two hidden markers on the forge - one
|
|
8
|
+
* on the summary, one on each finding's thread - so a restart reads the state
|
|
9
|
+
* back from the change request and never from this machine.
|
|
10
|
+
*/
|
|
11
|
+
/** The summary's word budget, the counts table and the marker excluded. */
|
|
12
|
+
export const SUMMARY_WORDS = 120;
|
|
13
|
+
/** A finding's visible lines, its suggestion block excluded and its sign-off included. */
|
|
14
|
+
export const FINDING_LINES = 5;
|
|
15
|
+
const SUMMARY_TAG = "thurview-pr-review";
|
|
16
|
+
const FINDING_TAG = "thurview-finding";
|
|
17
|
+
const Finding = z.object({
|
|
18
|
+
id: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(/^[a-z0-9-]+$/)
|
|
21
|
+
.optional(),
|
|
22
|
+
category: z.enum(CATEGORY_IDS),
|
|
23
|
+
severity: z.enum(SEVERITIES),
|
|
24
|
+
path: z.string().min(1),
|
|
25
|
+
line: z.number().int().positive(),
|
|
26
|
+
startLine: z.number().int().positive().optional(),
|
|
27
|
+
side: z.enum(["head", "base"]).optional(),
|
|
28
|
+
title: z.string().min(1),
|
|
29
|
+
body: z.string().default(""),
|
|
30
|
+
suggestion: z.string().optional(),
|
|
31
|
+
});
|
|
32
|
+
const PassSchema = z.object({
|
|
33
|
+
/** The head this pass reviewed; `sync` refuses it once the change request moved on. */
|
|
34
|
+
head: z.string().min(7),
|
|
35
|
+
confidence: z.number().int().min(1).max(5),
|
|
36
|
+
reason: z.string().min(1),
|
|
37
|
+
risk: z.array(z.string().min(1)).min(1).max(5),
|
|
38
|
+
change: z.string().min(1),
|
|
39
|
+
reviewUrl: z.string().url().optional(),
|
|
40
|
+
signoff: z.string().optional(),
|
|
41
|
+
findings: z.array(Finding).default([]),
|
|
42
|
+
fixed: z.array(z.string()).default([]),
|
|
43
|
+
});
|
|
44
|
+
export function parsePass(text, file) {
|
|
45
|
+
let raw;
|
|
46
|
+
try {
|
|
47
|
+
raw = JSON.parse(text);
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
throw new AxiError(`${file} is not valid JSON: ${e.message}`, "VALIDATION_ERROR", [
|
|
51
|
+
"Read `thurview pr-review --help` for the pass file's shape",
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
const parsed = PassSchema.safeParse(raw);
|
|
55
|
+
if (!parsed.success)
|
|
56
|
+
throw new AxiError(`${file} is not a pass: ${parsed.error.issues
|
|
57
|
+
.map((i) => `${i.path.join(".") || "(root)"} ${i.message}`)
|
|
58
|
+
.join("; ")}`, "VALIDATION_ERROR", [
|
|
59
|
+
`category is one of ${CATEGORY_IDS.join(", ")}; severity is one of ${SEVERITIES.join(", ")}`,
|
|
60
|
+
"confidence is 1-5; risk holds 1 to 5 bullets",
|
|
61
|
+
]);
|
|
62
|
+
checkPass(parsed.data);
|
|
63
|
+
return parsed.data;
|
|
64
|
+
}
|
|
65
|
+
/** The rules no schema says: one line per title, and the comment budget. */
|
|
66
|
+
export function checkPass(p) {
|
|
67
|
+
// A newline would let a line pass as a table row or the marker, and so
|
|
68
|
+
// slip past the word budget; every one of these is one line by design.
|
|
69
|
+
const oneLine = [
|
|
70
|
+
["reason", p.reason],
|
|
71
|
+
["change", p.change],
|
|
72
|
+
["signoff", p.signoff],
|
|
73
|
+
...p.risk.map((r, i) => [`risk[${i}]`, r]),
|
|
74
|
+
];
|
|
75
|
+
for (const [field, text] of oneLine)
|
|
76
|
+
if (text && /[\r\n]/.test(text.trim()))
|
|
77
|
+
throw new AxiError(`${field} is more than one line`, "VALIDATION_ERROR", [
|
|
78
|
+
"reason, each risk bullet, change and signoff are one line each",
|
|
79
|
+
]);
|
|
80
|
+
for (const f of p.findings ?? []) {
|
|
81
|
+
const at = `${f.path}:${f.line}`;
|
|
82
|
+
if (f.title.includes("\n"))
|
|
83
|
+
throw new AxiError(`${at}: the title is more than one line`, "VALIDATION_ERROR", [
|
|
84
|
+
"The title is the claim, in one line; the fix goes in body",
|
|
85
|
+
]);
|
|
86
|
+
if (f.startLine && f.startLine > f.line)
|
|
87
|
+
throw new AxiError(`${at}: startLine ${f.startLine} is after line`, "VALIDATION_ERROR", [
|
|
88
|
+
"line is the LAST line of the range and startLine the first",
|
|
89
|
+
]);
|
|
90
|
+
const lines = visibleLines(f, p.signoff);
|
|
91
|
+
if (lines > FINDING_LINES)
|
|
92
|
+
throw new AxiError(`${at}: the comment is ${lines} lines, over the ${FINDING_LINES}-line budget`, "VALIDATION_ERROR", ["One point per comment: cut it to the claim and one fix, or split it in two"]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function visibleLines(f, signoff) {
|
|
96
|
+
const body = f.body?.trim() ? f.body.trim().split("\n").length : 0;
|
|
97
|
+
return 1 + body + (signoff ? 1 : 0);
|
|
98
|
+
}
|
|
99
|
+
/** Stable across passes, so the same finding found twice is recognised as one. */
|
|
100
|
+
export function findingId(f) {
|
|
101
|
+
if (f.id)
|
|
102
|
+
return f.id;
|
|
103
|
+
const h = createHash("sha1").update(`${f.category}\n${f.path}\n${f.title.trim()}`);
|
|
104
|
+
return h.digest("hex").slice(0, 10);
|
|
105
|
+
}
|
|
106
|
+
function marker(tag, data) {
|
|
107
|
+
return `<!-- ${tag} ${JSON.stringify(data)} -->`;
|
|
108
|
+
}
|
|
109
|
+
function readMarker(tag, body) {
|
|
110
|
+
const m = new RegExp(`^<!-- ${tag} (\\{.*?\\}) -->`).exec(body);
|
|
111
|
+
if (!m)
|
|
112
|
+
return null;
|
|
113
|
+
try {
|
|
114
|
+
return JSON.parse(m[1]);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export function readSummaryMarker(body) {
|
|
121
|
+
return readMarker(SUMMARY_TAG, body);
|
|
122
|
+
}
|
|
123
|
+
export function readFindingMarker(body) {
|
|
124
|
+
const m = readMarker(FINDING_TAG, body);
|
|
125
|
+
if (!m || !(m.category in CATEGORIES) || !SEVERITIES.includes(m.severity))
|
|
126
|
+
return null;
|
|
127
|
+
const heading = body.split("\n")[1] ?? "";
|
|
128
|
+
return { ...m, title: heading.replace(/^.*?:\*\* /, "") };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* One finding as an inline comment: the claim, the fix, a suggestion the
|
|
132
|
+
* author can apply. GitHub anchors a range and replaces all of it; GitLab
|
|
133
|
+
* anchors the range's last line, so its suggestion reaches back over the rest.
|
|
134
|
+
*/
|
|
135
|
+
export function renderFinding(f, signoff, forge) {
|
|
136
|
+
const label = CATEGORIES[f.category].label;
|
|
137
|
+
const head = f.severity === "nit"
|
|
138
|
+
? `nit: **${label}:** ${f.title.trim()}`
|
|
139
|
+
: `**${label} · ${f.severity}:** ${f.title.trim()}`;
|
|
140
|
+
const parts = [
|
|
141
|
+
marker(FINDING_TAG, { id: findingId(f), category: f.category, severity: f.severity }),
|
|
142
|
+
head,
|
|
143
|
+
];
|
|
144
|
+
if (f.body?.trim())
|
|
145
|
+
parts.push(f.body.trim());
|
|
146
|
+
if (f.suggestion !== undefined) {
|
|
147
|
+
const above = f.startLine && f.startLine < f.line ? f.line - f.startLine : 0;
|
|
148
|
+
const fence = forge === "gitlab" ? `\`\`\`suggestion:-${above}+0` : "```suggestion";
|
|
149
|
+
parts.push(`${fence}\n${f.suggestion.replace(/\n$/, "")}\n\`\`\``);
|
|
150
|
+
}
|
|
151
|
+
if (signoff)
|
|
152
|
+
parts.push(signoff);
|
|
153
|
+
return parts.join("\n");
|
|
154
|
+
}
|
|
155
|
+
function table(open) {
|
|
156
|
+
if (!open.length)
|
|
157
|
+
return "No open findings.";
|
|
158
|
+
const rows = CATEGORY_IDS.filter((c) => open.some((f) => f.category === c)).map((c) => {
|
|
159
|
+
const n = (s) => open.filter((f) => f.category === c && f.severity === s).length;
|
|
160
|
+
return `| ${CATEGORIES[c].label} | ${SEVERITIES.map(n).join(" | ")} |`;
|
|
161
|
+
});
|
|
162
|
+
return [
|
|
163
|
+
`| Open findings | ${SEVERITIES.join(" | ")} |`,
|
|
164
|
+
`| --- | ${SEVERITIES.map(() => "---").join(" | ")} |`,
|
|
165
|
+
...rows,
|
|
166
|
+
].join("\n");
|
|
167
|
+
}
|
|
168
|
+
function headline(p, author, open) {
|
|
169
|
+
const blocking = open.filter((f) => f.severity === "blocking").length;
|
|
170
|
+
if (blocking)
|
|
171
|
+
return `Next: @${author} — fix the ${blocking} blocking finding${blocking === 1 ? "" : "s"}.`;
|
|
172
|
+
if (p.confidence >= 4)
|
|
173
|
+
return "Next: merge";
|
|
174
|
+
return `Next: @${author} — answer the risk below.`;
|
|
175
|
+
}
|
|
176
|
+
/** The line a stopped or finished review opens with instead of a next step. */
|
|
177
|
+
export function statusLine(m) {
|
|
178
|
+
const at = m.head.slice(0, 7);
|
|
179
|
+
if (m.state === "stopped")
|
|
180
|
+
return `Review stopped at ${at}. Remove the \`thurview:stop\` label and run \`thurview pr-review start\` to resume.`;
|
|
181
|
+
if (m.state === "merged" || m.state === "closed")
|
|
182
|
+
return `Review ended: ${m.state} at ${at}.`;
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
/** Words a reader reads: the marker and the counts table are not prose. */
|
|
186
|
+
export function summaryWords(body) {
|
|
187
|
+
return body
|
|
188
|
+
.split("\n")
|
|
189
|
+
.filter((l) => !l.startsWith("<!--") && !l.startsWith("|"))
|
|
190
|
+
.join(" ")
|
|
191
|
+
.split(/\s+/)
|
|
192
|
+
.filter((w) => /[\p{L}\p{N}]/u.test(w)).length;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* The one summary a change request carries: the next step, the confidence
|
|
196
|
+
* with its reason, the risk, the change in short, the open findings by
|
|
197
|
+
* category and severity, and a link to the full review when there is one.
|
|
198
|
+
*/
|
|
199
|
+
export function renderSummary(p, m, author, open) {
|
|
200
|
+
const tail = [`Reviewed \`${m.head.slice(0, 7)}\``];
|
|
201
|
+
if (p.reviewUrl)
|
|
202
|
+
tail.push(`[Full review](${p.reviewUrl})`);
|
|
203
|
+
const parts = [
|
|
204
|
+
`${marker(SUMMARY_TAG, m)}\n${statusLine(m) ?? headline(p, author, open)}`,
|
|
205
|
+
`**Confidence ${p.confidence}/5:** ${p.reason.trim()}`,
|
|
206
|
+
`**Risk**\n${p.risk.map((r) => `- ${r.trim()}`).join("\n")}`,
|
|
207
|
+
`**Change:** ${p.change.trim()}`,
|
|
208
|
+
table(open),
|
|
209
|
+
tail.join(" · "),
|
|
210
|
+
];
|
|
211
|
+
if (p.signoff)
|
|
212
|
+
parts.push(p.signoff);
|
|
213
|
+
const body = parts.join("\n\n");
|
|
214
|
+
const words = summaryWords(body);
|
|
215
|
+
if (words > SUMMARY_WORDS)
|
|
216
|
+
throw new AxiError(`the summary is ${words} words, over the ${SUMMARY_WORDS}-word budget`, "VALIDATION_ERROR", ["Cut reason, risk and change to what the author acts on; the full review holds the rest"]);
|
|
217
|
+
return body;
|
|
218
|
+
}
|
|
219
|
+
/** The same summary under a new marker and opening line; the rest is kept as written. */
|
|
220
|
+
export function restate(body, m, opening) {
|
|
221
|
+
const lines = body.split("\n");
|
|
222
|
+
return [marker(SUMMARY_TAG, m), opening, ...lines.slice(2)].join("\n");
|
|
223
|
+
}
|
|
224
|
+
export function bareSummary(m, opening) {
|
|
225
|
+
return `${marker(SUMMARY_TAG, m)}\n${opening}`;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/pr-review/format.ts"],"names":[],"mappings":"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"}
|