mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-14 20:37:14 +01:00
fix(post): only a trailing Capy badge splits off; other marker blocks stay in their section; tighter run-id exemptions (GST-1227 review)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
637b60bc2f
commit
899ee9a8a9
6 files changed
+108
-46
No files matched your search
@@ -274,14 +274,14 @@ describe("gstack-post owns the redaction scan", () => {
|
||||
test("run ids in a runs= list or an actions/runs/ path are not phone numbers; a phone number beside them still is", () => {
|
||||
const clean = [
|
||||
"CI_CARRY: cleared pregate.json/remote_gate run tree=704992822996 tests=412 runs=41629861819/1,41629861820/2 profiles=free-tests,windows-smoke heads=2f9d11a0c3e4 verified=2026-11-03T18:00:00.000Z",
|
||||
"carried run=41629861819/1,4162986182/3",
|
||||
"carried run=41629861819/1,416298618201/3",
|
||||
"logs at garrytan/gstack actions/runs/41629861819/attempts/2",
|
||||
];
|
||||
for (const body of clean) {
|
||||
const f = fakeEnv({ files: { "/b": body + "\n" }, visibility: "public" });
|
||||
expect([body, run(["pr-comment", "12", "--body-file", "/b"], f)]).toEqual([body, EXIT.posted]);
|
||||
}
|
||||
for (const body of ["call +1 415 555 0123 about runs=41629861819/1,41629861820/2", "runs=+14155550123/1", "runs=1/4155550123", "runs=4155550123"]) {
|
||||
for (const body of ["call +1 415 555 0123 about runs=41629861819/1,41629861820/2", "runs=+14155550123/1", "runs=1/4155550123", "runs=4155550123", "runs=4155550123/1", "sales-actions/runs/4155550123"]) {
|
||||
const f = fakeEnv({ files: { "/b": body + "\n" }, visibility: "public" });
|
||||
expect([body, run(["pr-comment", "12", "--body-file", "/b"], f)]).toEqual([body, EXIT.confirm]);
|
||||
expect(f.stdout()).toContain("RULE: pii.phone.e164 LINE: 1 PART: body");
|
||||
@@ -517,6 +517,55 @@ describe("gstack-post pr-body merges the PR body by section", () => {
|
||||
expect(trailing.stderr()).toBe("gstack-post: pr-body merged by section: replaced ship receipt, Approvals; kept What changed, Why, capy-badge block; added Tests\n");
|
||||
});
|
||||
|
||||
const BADGE = "<!-- capy-badge:start -->\n[](https://capy.ai)\n<!-- capy-badge:end -->\n";
|
||||
const SHOTS = (alt: string) => "<!-- shots:start -->\n\n<!-- shots:end -->\n";
|
||||
|
||||
// Value: protects=a marker block other than a Capy badge that ends the body stays inside its section exactly as before GST-1227, so a rewrite
|
||||
// replaces it with the section, a new section lands after it, text after a badge in mid-section never pairs with the note under a receipt,
|
||||
// and the file's own block stays in its section; fails_when=the old block and the stale lines after it survive a rewrite, a shots block is
|
||||
// split from its section, "Fixes #12" is dropped, or the file's block moves above the first heading; why_new=GST-1227 review P1; seam=none
|
||||
// (mergeBodySections); no_claim=the gh round trip
|
||||
test("a marker block that is not a trailing Capy badge stays inside its section", () => {
|
||||
const mid = mergeBodySections(RECEIPT("aaaa") + "\n## What changed\n\n- old\n" + SHOTS("a") + "stale line\n\n## Why\n\nBecause.\n", RECEIPT("bbbb") + "\n## What changed\n\n- new\n");
|
||||
expect([mid.body, mid.kept]).toEqual([RECEIPT("bbbb") + "\n## What changed\n\n- new\n\n## Why\n\nBecause.\n", ["Why"]]);
|
||||
const last = mergeBodySections(CURRENT + SHOTS("a"), RECEIPT("bbbb") + "\n## Tests\n\n- one\n");
|
||||
expect(last.body).toBe(RECEIPT("bbbb") + "\n## What changed\n\n- the old summary\n\n## Why\n\nBecause.\n\n## Approvals\n\n- owner: do it\n" + SHOTS("a") + "\n## Tests\n\n- one\n");
|
||||
const collide = mergeBodySections("Intro\n\n## What changed\n\n- x\n" + BADGE + "Fixes #12\n", RECEIPT("bbbb") + "Review-loop exception: foo\n");
|
||||
expect(collide.body).toBe("Intro\n\n" + RECEIPT("bbbb") + "\nReview-loop exception: foo\n\n## What changed\n\n- x\n" + BADGE + "Fixes #12\n");
|
||||
const own = mergeBodySections(CURRENT, RECEIPT("bbbb") + "\n## What changed\n\n" + SHOTS("n") + "after the shots\n");
|
||||
expect(own.body).toBe(RECEIPT("bbbb") + "\n## What changed\n\n" + SHOTS("n") + "after the shots\n\n## Why\n\nBecause.\n\n## Approvals\n\n- owner: do it\n");
|
||||
});
|
||||
|
||||
// Value: protects=a trailing badge the file brings into a body without one lands at the end once and a repost is byte-identical; fails_when=each
|
||||
// identical post adds another badge or moves it above the sections; why_new=GST-1227 review P2; seam=none (mergeBodySections); no_claim=the gh round trip
|
||||
test("a trailing badge the file brings lands at the end once and reposts byte for byte", () => {
|
||||
const file = RECEIPT("bbbb") + "\n## Tests\n\n- one\n\n" + BADGE;
|
||||
const once = mergeBodySections(CURRENT, file).body;
|
||||
expect(once).toBe(RECEIPT("bbbb") + "\n## What changed\n\n- the old summary\n\n## Why\n\nBecause.\n\n## Approvals\n\n- owner: do it\n\n## Tests\n\n- one\n\n" + BADGE);
|
||||
const twice = mergeBodySections(once, file).body;
|
||||
expect(twice).toBe(once);
|
||||
expect(mergeBodySections(twice, file).body).toBe(once);
|
||||
});
|
||||
|
||||
// Value: protects=fences inside a marker block keep their meaning (a fenced `## x` opens no section), a badge inside a fence is text, and an
|
||||
// unclosed badge marker swallows nothing; fails_when=the fenced `## x` ends the block and opens a section, or the unclosed marker keeps the
|
||||
// rest of the old section after a rewrite; why_new=GST-1227 review P2; seam=none (mergeBodySections); no_claim=the gh round trip
|
||||
test("a fenced heading inside a marker block and an unclosed badge marker change nothing about the sections", () => {
|
||||
const fenced = mergeBodySections("## What changed\n\n<!-- shots:start -->\n```md\n## x\n```\n<!-- shots:end -->\n\n## Why\n\nold\n", "## Why\n\nnew\n");
|
||||
expect([fenced.body, fenced.replaced, fenced.kept]).toEqual(["## What changed\n\n<!-- shots:start -->\n```md\n## x\n```\n<!-- shots:end -->\n\n## Why\n\nnew\n", ["Why"], ["What changed"]]);
|
||||
const example = mergeBodySections("## Docs\n\n```md\n" + BADGE + "```\n", "## Docs\n\nnew\n");
|
||||
expect([example.body, example.kept]).toEqual(["## Docs\n\nnew\n", []]);
|
||||
const unclosed = mergeBodySections(RECEIPT("aaaa") + "\n## Why\n\nBecause.\n\n## Approvals\n\n- x\n<!-- capy-badge:start -->\nmore approvals\n", RECEIPT("bbbb") + "\n## Approvals\n\n- y\n");
|
||||
expect([unclosed.body, unclosed.kept]).toEqual([RECEIPT("bbbb") + "\n## Why\n\nBecause.\n\n## Approvals\n\n- y\n", ["Why"]]);
|
||||
});
|
||||
|
||||
// Value: protects=two marker blocks with one name merge by their sections; fails_when=the merge refuses them as a duplicated section;
|
||||
// why_new=GST-1227 review; seam=none (mergeBodySections); no_claim=the gh round trip
|
||||
test("two marker blocks with the same name merge by their sections", () => {
|
||||
const two = mergeBodySections("## A\n\n" + SHOTS("a") + "\n## B\n\n" + SHOTS("b"), "## A\n\n" + SHOTS("a2"));
|
||||
expect([two.body, two.replaced, two.kept]).toEqual(["## A\n\n" + SHOTS("a2") + "\n## B\n\n" + SHOTS("b"), ["A"], ["B"]]);
|
||||
});
|
||||
|
||||
// Value: protects=a partial post (no receipt above its first `## ` heading, even with a caption of its own or a receipt further down) keeps
|
||||
// the human intro and the note under the receipt; fails_when=a lane posting only `## Screenshots`
|
||||
// deletes the intro or a Review-loop exception line; why_new=GST-412 made any file with a section own the preamble (GstackMinder,
|
||||
|
||||
Reference in new issue
Block a user