@webpieces/pr-gate 0.4.598 → 0.4.600
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/package.json +2 -2
- package/src/scripts/commands/finish-upsert-pr-command.d.ts +3 -3
- package/src/scripts/commands/finish-upsert-pr-command.js +11 -17
- package/src/scripts/commands/finish-upsert-pr-command.js.map +1 -1
- package/src/scripts/commands/land-pr-command.d.ts +82 -72
- package/src/scripts/commands/land-pr-command.js +166 -157
- package/src/scripts/commands/land-pr-command.js.map +1 -1
- package/src/scripts/pr-gate-app.d.ts +2 -2
- package/src/scripts/pr-gate-app.js +2 -2
- package/src/scripts/pr-gate-app.js.map +1 -1
- package/src/scripts/workflow/cleanTmp.d.ts +12 -14
- package/src/scripts/workflow/cleanTmp.js +13 -21
- package/src/scripts/workflow/cleanTmp.js.map +1 -1
- package/src/scripts/workflow/merge-body-temp-file.d.ts +23 -0
- package/src/scripts/workflow/merge-body-temp-file.js +40 -0
- package/src/scripts/workflow/merge-body-temp-file.js.map +1 -0
- package/src/scripts/wp-land-pr.js +7 -17
- package/src/scripts/wp-land-pr.js.map +1 -1
- package/src/scripts/workflow/merge-body-filer.d.ts +0 -44
- package/src/scripts/workflow/merge-body-filer.js +0 -92
- package/src/scripts/workflow/merge-body-filer.js.map +0 -1
|
@@ -3,7 +3,7 @@ import { FinishUpdateCommand } from './commands/finish-update-command';
|
|
|
3
3
|
import { StartUpsertPrCommand } from './commands/start-upsert-pr-command';
|
|
4
4
|
import { FinishUpsertPrCommand } from './commands/finish-upsert-pr-command';
|
|
5
5
|
import { CleanupCommand } from './commands/cleanup-command';
|
|
6
|
-
import { LandPrCommand
|
|
6
|
+
import { LandPrCommand } from './commands/land-pr-command';
|
|
7
7
|
import { CheckPrCommand } from './commands/check-pr-command';
|
|
8
8
|
import { ReviewUpsertPrCommand, ReviewUpsertPrOptions } from './commands/review-upsert-pr-command';
|
|
9
9
|
import { ReapWorktreeCommand } from './commands/reap-worktree-command';
|
|
@@ -62,7 +62,7 @@ export declare class PrGateApp {
|
|
|
62
62
|
/** `wp-cleanup`: reap what is provably merged, and ASK about everything that merely looks dead. */
|
|
63
63
|
cleanup(): Promise<void>;
|
|
64
64
|
/** `wp-land-pr`: squash-merge this branch's PR into main with the compact commit body. */
|
|
65
|
-
landPr(
|
|
65
|
+
landPr(): Promise<void>;
|
|
66
66
|
/**
|
|
67
67
|
* `wp-review-upsert-pr`: STAGE ② — validate the 3-point merge, run the build gate, extract this
|
|
68
68
|
* branch's diff, and brief the reviewer subagents. Unlike the report-only command it replaces, this CAN
|
|
@@ -107,9 +107,9 @@ let PrGateApp = class PrGateApp {
|
|
|
107
107
|
return this.cleanupCommand.run();
|
|
108
108
|
}
|
|
109
109
|
/** `wp-land-pr`: squash-merge this branch's PR into main with the compact commit body. */
|
|
110
|
-
landPr(
|
|
110
|
+
landPr() {
|
|
111
111
|
this.assertNoResolveInProgress('wp-land-pr');
|
|
112
|
-
return this.landPrCommand.run(
|
|
112
|
+
return this.landPrCommand.run();
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
115
|
* `wp-review-upsert-pr`: STAGE ② — validate the 3-point merge, run the build gate, extract this
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-gate-app.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/pr-gate-app.ts"],"names":[],"mappings":";;;;AAAA,0DAAyD;AACzD,yCAA2D;AAC3D,0EAAqE;AACrE,4EAAuE;AACvE,gFAA0E;AAC1E,kFAA4E;AAC5E,gEAA4D;AAC5D,
|
|
1
|
+
{"version":3,"file":"pr-gate-app.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/pr-gate-app.ts"],"names":[],"mappings":"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","sourcesContent":["import { DocumentDesign } from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { StartUpdateCommand } from './commands/start-update-command';\nimport { FinishUpdateCommand } from './commands/finish-update-command';\nimport { StartUpsertPrCommand } from './commands/start-upsert-pr-command';\nimport { FinishUpsertPrCommand } from './commands/finish-upsert-pr-command';\nimport { CleanupCommand } from './commands/cleanup-command';\nimport { LandPrCommand } from './commands/land-pr-command';\nimport { CheckPrCommand } from './commands/check-pr-command';\nimport { ReviewUpsertPrCommand, ReviewUpsertPrOptions } from './commands/review-upsert-pr-command';\nimport { ReapWorktreeCommand } from './commands/reap-worktree-command';\nimport { PushDevCommand, PushDevOptions } from './commands/push-dev-command';\nimport { FinishPushDevCommand, FinishPushDevOptions } from './commands/finish-push-dev-command';\nimport { PushDevStateStore } from './workflow/push-dev-state';\nimport { RepoRootFinder } from '@webpieces/rules-config';\n\n/**\n * The pr-gate application root. `container.get(PrGateApp)` resolves the entire workflow DAG (the command\n * classes → the injected git/merge/dashboard services). `@DocumentDesign` marks it the\n * top-of-DAG the DI-design analyzer roots on, so `role:app` pr-gate draws its design. Each `bin/*`\n * entry resolves THIS and calls the matching command method.\n */\n@DocumentDesign()\n@injectable(bindingScopeValues.Singleton)\nexport class PrGateApp {\n constructor(\n private readonly startUpdateCommand: StartUpdateCommand,\n private readonly finishUpdateCommand: FinishUpdateCommand,\n private readonly startUpsertPrCommand: StartUpsertPrCommand,\n private readonly finishUpsertPrCommand: FinishUpsertPrCommand,\n private readonly cleanupCommand: CleanupCommand,\n private readonly landPrCommand: LandPrCommand,\n private readonly checkPrCommand: CheckPrCommand,\n private readonly reviewUpsertPrCommand: ReviewUpsertPrCommand,\n private readonly reapWorktreeCommand: ReapWorktreeCommand,\n private readonly pushDevCommand: PushDevCommand,\n private readonly finishPushDevCommand: FinishPushDevCommand,\n private readonly pushDevStateStore: PushDevStateStore,\n private readonly repoRootFinder: RepoRootFinder,\n ) {}\n\n /**\n * Refuse `command` while a `wp-push-dev --resolve` is half-finished.\n *\n * Enforced HERE, at the one place every bin funnels through, rather than in each command: a resolve\n * parks the checkout on a throwaway branch, so every command below would act on a branch that is not\n * the one it thinks it is. Putting the check in nine constructors is nine chances to forget it in the\n * tenth. PushDevStateStore owns the blocked list AND renders the hint from it, so the two cannot drift.\n */\n private assertNoResolveInProgress(command: string): void {\n if (!this.pushDevStateStore.isBlockedDuringResolve(command)) return;\n this.pushDevStateStore.assertIdle(this.repoRootFinder.resolveRepoRoot(process.cwd()), command);\n }\n\n /** `wp-push-dev`: publish a DISPOSABLE copy of this branch for the shared dev environment. No PR, no build. */\n pushDev(opts: PushDevOptions = new PushDevOptions()): Promise<void> {\n return this.pushDevCommand.run(opts);\n }\n\n /** `wp-finish-push-dev`: commit a resolved dev composition and publish it (conflict path only). */\n finishPushDev(opts: FinishPushDevOptions = new FinishPushDevOptions()): Promise<void> {\n return this.finishPushDevCommand.run(opts);\n }\n\n /**\n * INTERNAL (`wp-reap-worktree.js`, no `bin` entry): remove ONE named worktree and its branch.\n * `wp-land-pr` spawns it with cwd = the primary clone so the tree it just landed from can be\n * reaped by a process that is not standing in it.\n */\n reapWorktree(args: string[]): Promise<void> {\n return this.reapWorktreeCommand.run(args);\n }\n\n /** `wp-start-update`: 3-point squash-update from main (no PR). */\n startUpdate(): Promise<void> {\n this.assertNoResolveInProgress('wp-start-update');\n return this.startUpdateCommand.run();\n }\n\n /** `wp-finish-update`: validate + finalize a resolved 3-point merge (no PR). */\n finishUpdate(): Promise<void> {\n this.assertNoResolveInProgress('wp-finish-update');\n return this.finishUpdateCommand.run();\n }\n\n /** `wp-start-upsert-pr`: update from main (3-point merge), hand off review.json. No build gate, no push. */\n startUpsertPr(): Promise<void> {\n this.assertNoResolveInProgress('wp-start-upsert-pr');\n return this.startUpsertPrCommand.run();\n }\n\n /** `wp-finish-upsert-pr`: finalize merge, authoritative build gate, dashboard, create/update PR. */\n finishUpsertPr(): Promise<void> {\n this.assertNoResolveInProgress('wp-finish-upsert-pr');\n return this.finishUpsertPrCommand.run();\n }\n\n /** `wp-cleanup`: reap what is provably merged, and ASK about everything that merely looks dead. */\n cleanup(): Promise<void> {\n this.assertNoResolveInProgress('wp-cleanup');\n return this.cleanupCommand.run();\n }\n\n /** `wp-land-pr`: squash-merge this branch's PR into main with the compact commit body. */\n landPr(): Promise<void> {\n this.assertNoResolveInProgress('wp-land-pr');\n return this.landPrCommand.run();\n }\n\n /**\n * `wp-review-upsert-pr`: STAGE ② — validate the 3-point merge, run the build gate, extract this\n * branch's diff, and brief the reviewer subagents. Unlike the report-only command it replaces, this CAN\n * fail — before any reviewer is spawned, so a broken branch costs no reviewer tokens.\n */\n reviewUpsertPr(opts: ReviewUpsertPrOptions = new ReviewUpsertPrOptions()): Promise<void> {\n this.assertNoResolveInProgress('wp-review-upsert-pr');\n return this.reviewUpsertPrCommand.run(opts);\n }\n\n /** `wp-check-pr`: READ-ONLY CI check — verify the PR body carries a valid HMAC gate token for its head sha. */\n checkPr(): Promise<void> {\n return this.checkPrCommand.run();\n }\n}\n"]}
|
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import { AgedTreeSweeper, DotWebpieces,
|
|
1
|
+
import { AgedTreeSweeper, DotWebpieces, RepoRootFinder } from '@webpieces/rules-config';
|
|
2
2
|
/**
|
|
3
|
-
* 30-day garbage collection of
|
|
3
|
+
* 30-day garbage collection of this tree's `.webpieces` scratch state.
|
|
4
4
|
*
|
|
5
|
-
* Runs at the end of every merge/PR flow (see merge-end.ts).
|
|
6
|
-
*
|
|
7
|
-
* directory left empty afterwards is pruned. Neither root itself is ever removed.
|
|
5
|
+
* Runs at the end of every merge/PR flow (see merge-end.ts). Any file older than the cutoff is deleted
|
|
6
|
+
* and any directory left empty afterwards is pruned; the root itself is never removed.
|
|
8
7
|
*
|
|
9
8
|
* Everything the tooling still needs is rewritten on each run under a fresh mtime (instruct-ai/ docs,
|
|
10
|
-
* the active logs, the *-status.json state files, and
|
|
11
|
-
*
|
|
12
|
-
* the next write recreates it.
|
|
9
|
+
* the active logs, the *-status.json state files), and every writer `mkdirSync(..., { recursive: true })`s
|
|
10
|
+
* its target first — so pruning a stale home dir is harmless; the next write recreates it.
|
|
13
11
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
12
|
+
* There is exactly ONE root, because there is exactly one place webpieces writes state: `{repo}/.webpieces`.
|
|
13
|
+
* This used to sweep a second, machine-global root (`~/.webpieces/prs/...`, which survived `rm -rf <clone>`
|
|
14
|
+
* and so had no other owner). That store is gone — the gated merge body is the PR's own description now —
|
|
15
|
+
* and with it the only artifact webpieces ever wrote outside a repo. See
|
|
16
|
+
* `decisions/0005-the-pr-description-is-the-merge-body.md`.
|
|
18
17
|
*/
|
|
19
18
|
export declare class CleanTmp {
|
|
20
19
|
private readonly repoRootFinder;
|
|
21
|
-
private readonly prBodies;
|
|
22
20
|
private readonly sweeper;
|
|
23
21
|
private readonly dotDir;
|
|
24
|
-
constructor(repoRootFinder: RepoRootFinder,
|
|
22
|
+
constructor(repoRootFinder: RepoRootFinder, sweeper: AgedTreeSweeper, dotDir?: DotWebpieces);
|
|
25
23
|
cleanTmp(): Promise<void>;
|
|
26
24
|
private reporter;
|
|
27
25
|
}
|
|
@@ -8,30 +8,27 @@ const rules_config_1 = require("@webpieces/rules-config");
|
|
|
8
8
|
const inversify_1 = require("inversify");
|
|
9
9
|
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
10
10
|
/**
|
|
11
|
-
* 30-day garbage collection of
|
|
11
|
+
* 30-day garbage collection of this tree's `.webpieces` scratch state.
|
|
12
12
|
*
|
|
13
|
-
* Runs at the end of every merge/PR flow (see merge-end.ts).
|
|
14
|
-
*
|
|
15
|
-
* directory left empty afterwards is pruned. Neither root itself is ever removed.
|
|
13
|
+
* Runs at the end of every merge/PR flow (see merge-end.ts). Any file older than the cutoff is deleted
|
|
14
|
+
* and any directory left empty afterwards is pruned; the root itself is never removed.
|
|
16
15
|
*
|
|
17
16
|
* Everything the tooling still needs is rewritten on each run under a fresh mtime (instruct-ai/ docs,
|
|
18
|
-
* the active logs, the *-status.json state files, and
|
|
19
|
-
*
|
|
20
|
-
* the next write recreates it.
|
|
17
|
+
* the active logs, the *-status.json state files), and every writer `mkdirSync(..., { recursive: true })`s
|
|
18
|
+
* its target first — so pruning a stale home dir is harmless; the next write recreates it.
|
|
21
19
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
20
|
+
* There is exactly ONE root, because there is exactly one place webpieces writes state: `{repo}/.webpieces`.
|
|
21
|
+
* This used to sweep a second, machine-global root (`~/.webpieces/prs/...`, which survived `rm -rf <clone>`
|
|
22
|
+
* and so had no other owner). That store is gone — the gated merge body is the PR's own description now —
|
|
23
|
+
* and with it the only artifact webpieces ever wrote outside a repo. See
|
|
24
|
+
* `decisions/0005-the-pr-description-is-the-merge-body.md`.
|
|
26
25
|
*/
|
|
27
26
|
let CleanTmp = class CleanTmp {
|
|
28
27
|
repoRootFinder;
|
|
29
|
-
prBodies;
|
|
30
28
|
sweeper;
|
|
31
29
|
dotDir;
|
|
32
|
-
constructor(repoRootFinder,
|
|
30
|
+
constructor(repoRootFinder, sweeper, dotDir = rules_config_1.dotWebpieces) {
|
|
33
31
|
this.repoRootFinder = repoRootFinder;
|
|
34
|
-
this.prBodies = prBodies;
|
|
35
32
|
this.sweeper = sweeper;
|
|
36
33
|
this.dotDir = dotDir;
|
|
37
34
|
}
|
|
@@ -41,17 +38,13 @@ let CleanTmp = class CleanTmp {
|
|
|
41
38
|
// never sweep the repo-wide dir, whose entries (merged-branches.json, the main-sync status and
|
|
42
39
|
// its lock) belong to every worktree at once.
|
|
43
40
|
const tmpBase = this.dotDir.local(repoRoot);
|
|
44
|
-
|
|
45
|
-
if (!fs.existsSync(tmpBase) && !fs.existsSync(prsRoot))
|
|
41
|
+
if (!fs.existsSync(tmpBase))
|
|
46
42
|
return;
|
|
47
43
|
process.stdout.write('\n' + SEP + '🧹 Garbage-Collecting .webpieces\n' + SEP + '\n');
|
|
48
44
|
process.stdout.write(`Location: ${tmpBase}\n`);
|
|
49
|
-
process.stdout.write(` ${prsRoot} (machine-global PR merge bodies)\n`);
|
|
50
45
|
process.stdout.write(`Retention: ${rules_config_1.RETENTION_DAYS} days (older files reaped; empty dirs pruned)\n\n`);
|
|
51
46
|
const cutoffMs = rules_config_1.RETENTION_DAYS * 24 * 60 * 60 * 1000;
|
|
52
|
-
const total =
|
|
53
|
-
total.add(this.sweeper.sweep(tmpBase, cutoffMs, this.reporter(tmpBase)));
|
|
54
|
-
total.add(this.prBodies.sweep(repoRoot, this.reporter(prsRoot)));
|
|
47
|
+
const total = this.sweeper.sweep(tmpBase, cutoffMs, this.reporter(tmpBase));
|
|
55
48
|
if (total.empty) {
|
|
56
49
|
process.stdout.write(` ✅ Nothing older than ${rules_config_1.RETENTION_DAYS} days; no empty directories\n`);
|
|
57
50
|
}
|
|
@@ -74,7 +67,6 @@ exports.CleanTmp = CleanTmp;
|
|
|
74
67
|
exports.CleanTmp = CleanTmp = tslib_1.__decorate([
|
|
75
68
|
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton),
|
|
76
69
|
tslib_1.__metadata("design:paramtypes", [rules_config_1.RepoRootFinder,
|
|
77
|
-
rules_config_1.PrBodyStore,
|
|
78
70
|
rules_config_1.AgedTreeSweeper,
|
|
79
71
|
rules_config_1.DotWebpieces])
|
|
80
72
|
], CleanTmp);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanTmp.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/cleanTmp.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,0DAEiC;AACjC,yCAA2D;AAE3D,MAAM,GAAG,GAAG,0DAA0D,CAAC;AAEvE
|
|
1
|
+
{"version":3,"file":"cleanTmp.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/cleanTmp.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,0DAEiC;AACjC,yCAA2D;AAE3D,MAAM,GAAG,GAAG,0DAA0D,CAAC;AAEvE;;;;;;;;;;;;;;;GAeG;AAEI,IAAM,QAAQ,GAAd,MAAM,QAAQ;IAEI;IACA;IACA;IAHrB,YACqB,cAA8B,EAC9B,OAAwB,EACxB,SAAuB,2BAAY;QAFnC,mBAAc,GAAd,cAAc,CAAgB;QAC9B,YAAO,GAAP,OAAO,CAAiB;QACxB,WAAM,GAAN,MAAM,CAA6B;IACrD,CAAC;IAEJ,KAAK,CAAC,QAAQ;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,8FAA8F;QAC9F,+FAA+F;QAC/F,8CAA8C;QAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,OAAO;QAEpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,oCAAoC,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,OAAO,IAAI,CAAC,CAAC;QAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,cAAc,6BAAc,mDAAmD,CAAC,CAAC;QAEtG,MAAM,QAAQ,GAAG,6BAAc,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;QACtD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QAE5E,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,6BAAc,+BAA+B,CAAC,CAAC;QAClG,CAAC;aAAM,CAAC;YACJ,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACtD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,aAAa,CAAC;YAC/D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,KAAK,CAAC,KAAK,QAAQ,QAAQ,eAAe,KAAK,CAAC,IAAI,UAAU,OAAO,IAAI,CAAC,CAAC;QACpH,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,gGAAgG;IACxF,QAAQ,CAAC,IAAY;QACzB,OAAO,CAAC,OAAe,EAAE,KAAc,EAAQ,EAAE;YAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACjG,CAAC,CAAC;IACN,CAAC;CACJ,CAAA;AAxCY,4BAAQ;mBAAR,QAAQ;IADpB,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAGA,6BAAc;QACrB,8BAAe;QAChB,2BAAY;GAJhC,QAAQ,CAwCpB","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport {\n AgedTreeSweeper, DotWebpieces, RETENTION_DAYS, RepoRootFinder, dotWebpieces,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * 30-day garbage collection of this tree's `.webpieces` scratch state.\n *\n * Runs at the end of every merge/PR flow (see merge-end.ts). Any file older than the cutoff is deleted\n * and any directory left empty afterwards is pruned; the root itself is never removed.\n *\n * Everything the tooling still needs is rewritten on each run under a fresh mtime (instruct-ai/ docs,\n * the active logs, the *-status.json state files), and every writer `mkdirSync(..., { recursive: true })`s\n * its target first — so pruning a stale home dir is harmless; the next write recreates it.\n *\n * There is exactly ONE root, because there is exactly one place webpieces writes state: `{repo}/.webpieces`.\n * This used to sweep a second, machine-global root (`~/.webpieces/prs/...`, which survived `rm -rf <clone>`\n * and so had no other owner). That store is gone — the gated merge body is the PR's own description now —\n * and with it the only artifact webpieces ever wrote outside a repo. See\n * `decisions/0005-the-pr-description-is-the-merge-body.md`.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class CleanTmp {\n constructor(\n private readonly repoRootFinder: RepoRootFinder,\n private readonly sweeper: AgedTreeSweeper,\n private readonly dotDir: DotWebpieces = dotWebpieces,\n ) {}\n\n async cleanTmp(): Promise<void> {\n const repoRoot = this.repoRootFinder.resolveRepoRoot(process.cwd());\n // LOCAL scope: a worktree garbage-collects its OWN merge-info/pr-review scratch dirs. It must\n // never sweep the repo-wide dir, whose entries (merged-branches.json, the main-sync status and\n // its lock) belong to every worktree at once.\n const tmpBase = this.dotDir.local(repoRoot);\n if (!fs.existsSync(tmpBase)) return;\n\n process.stdout.write('\\n' + SEP + '🧹 Garbage-Collecting .webpieces\\n' + SEP + '\\n');\n process.stdout.write(`Location: ${tmpBase}\\n`);\n process.stdout.write(`Retention: ${RETENTION_DAYS} days (older files reaped; empty dirs pruned)\\n\\n`);\n\n const cutoffMs = RETENTION_DAYS * 24 * 60 * 60 * 1000;\n const total = this.sweeper.sweep(tmpBase, cutoffMs, this.reporter(tmpBase));\n\n if (total.empty) {\n process.stdout.write(` ✅ Nothing older than ${RETENTION_DAYS} days; no empty directories\\n`);\n } else {\n const fileWord = total.files === 1 ? 'file' : 'files';\n const dirWord = total.dirs === 1 ? 'directory' : 'directories';\n process.stdout.write(`\\n ✅ Reaped ${total.files} old ${fileWord} and pruned ${total.dirs} empty ${dirWord}\\n`);\n }\n\n process.stdout.write('\\n' + SEP + '\\n');\n }\n\n // One line per removal, relative to the root it came from, so the two roots read as one report.\n private reporter(base: string): (removed: string, isDir: boolean) => void {\n return (removed: string, isDir: boolean): void => {\n const label = isDir ? 'dir: ' : 'file: ';\n process.stdout.write(` 🗑️ ${label} ${path.relative(base, removed)}${isDir ? '/' : ''}\\n`);\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The filename inside the temp dir. Named, because it is what a human sees in a `gh` error. */
|
|
2
|
+
export declare const MERGE_BODY_FILE = "merge-commit-body.md";
|
|
3
|
+
/**
|
|
4
|
+
* Writes the gated squash-commit body to a THROWAWAY file, because `gh pr merge --body-file` takes a
|
|
5
|
+
* path and not a string. That is the entire job.
|
|
6
|
+
*
|
|
7
|
+
* ─── Why this is temp state and not a store ────────────────────────────────────────────────────────
|
|
8
|
+
* It replaces `MergeBodyFiler`, which filed the same bytes under
|
|
9
|
+
* `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md` so a later `wp-land-pr` could find
|
|
10
|
+
* them. That store existed because the PR DESCRIPTION was a different (much longer) string from the
|
|
11
|
+
* merge body, so the bytes finish produced lived nowhere else. Since the two surfaces were swapped —
|
|
12
|
+
* the description IS the compact body, and the dashboard moved into the PR's comments — the durable
|
|
13
|
+
* copy is the PR itself. A machine-global cache of a fact GitHub owns can only be missing, stale, or on
|
|
14
|
+
* the wrong computer, and it was all three. `wp-land-pr` now reads `gh pr view --json body`.
|
|
15
|
+
*
|
|
16
|
+
* So both callers reach the same place from different directions: finish already holds the bytes it
|
|
17
|
+
* just published, land reads them back from the PR, and each needs them on disk for the length of one
|
|
18
|
+
* `gh` invocation. See `decisions/0005-the-pr-description-is-the-merge-body.md`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class MergeBodyTempFile {
|
|
21
|
+
/** @returns an absolute path to a file holding exactly `body`. Never ''. */
|
|
22
|
+
write(body: string): string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MergeBodyTempFile = exports.MERGE_BODY_FILE = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const os = tslib_1.__importStar(require("os"));
|
|
7
|
+
const path = tslib_1.__importStar(require("path"));
|
|
8
|
+
const inversify_1 = require("inversify");
|
|
9
|
+
/** The filename inside the temp dir. Named, because it is what a human sees in a `gh` error. */
|
|
10
|
+
exports.MERGE_BODY_FILE = 'merge-commit-body.md';
|
|
11
|
+
/**
|
|
12
|
+
* Writes the gated squash-commit body to a THROWAWAY file, because `gh pr merge --body-file` takes a
|
|
13
|
+
* path and not a string. That is the entire job.
|
|
14
|
+
*
|
|
15
|
+
* ─── Why this is temp state and not a store ────────────────────────────────────────────────────────
|
|
16
|
+
* It replaces `MergeBodyFiler`, which filed the same bytes under
|
|
17
|
+
* `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md` so a later `wp-land-pr` could find
|
|
18
|
+
* them. That store existed because the PR DESCRIPTION was a different (much longer) string from the
|
|
19
|
+
* merge body, so the bytes finish produced lived nowhere else. Since the two surfaces were swapped —
|
|
20
|
+
* the description IS the compact body, and the dashboard moved into the PR's comments — the durable
|
|
21
|
+
* copy is the PR itself. A machine-global cache of a fact GitHub owns can only be missing, stale, or on
|
|
22
|
+
* the wrong computer, and it was all three. `wp-land-pr` now reads `gh pr view --json body`.
|
|
23
|
+
*
|
|
24
|
+
* So both callers reach the same place from different directions: finish already holds the bytes it
|
|
25
|
+
* just published, land reads them back from the PR, and each needs them on disk for the length of one
|
|
26
|
+
* `gh` invocation. See `decisions/0005-the-pr-description-is-the-merge-body.md`.
|
|
27
|
+
*/
|
|
28
|
+
let MergeBodyTempFile = class MergeBodyTempFile {
|
|
29
|
+
/** @returns an absolute path to a file holding exactly `body`. Never ''. */
|
|
30
|
+
write(body) {
|
|
31
|
+
const file = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'wp-merge-body-')), exports.MERGE_BODY_FILE);
|
|
32
|
+
fs.writeFileSync(file, body);
|
|
33
|
+
return file;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
exports.MergeBodyTempFile = MergeBodyTempFile;
|
|
37
|
+
exports.MergeBodyTempFile = MergeBodyTempFile = tslib_1.__decorate([
|
|
38
|
+
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton)
|
|
39
|
+
], MergeBodyTempFile);
|
|
40
|
+
//# sourceMappingURL=merge-body-temp-file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge-body-temp-file.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/merge-body-temp-file.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,+CAAyB;AACzB,mDAA6B;AAC7B,yCAA2D;AAE3D,gGAAgG;AACnF,QAAA,eAAe,GAAG,sBAAsB,CAAC;AAEtD;;;;;;;;;;;;;;;;GAgBG;AAEI,IAAM,iBAAiB,GAAvB,MAAM,iBAAiB;IAC1B,4EAA4E;IAC5E,KAAK,CAAC,IAAY;QACd,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,EAAE,uBAAe,CAAC,CAAC;QAClG,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ,CAAA;AAPY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;GAC5B,iBAAiB,CAO7B","sourcesContent":["import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/** The filename inside the temp dir. Named, because it is what a human sees in a `gh` error. */\nexport const MERGE_BODY_FILE = 'merge-commit-body.md';\n\n/**\n * Writes the gated squash-commit body to a THROWAWAY file, because `gh pr merge --body-file` takes a\n * path and not a string. That is the entire job.\n *\n * ─── Why this is temp state and not a store ────────────────────────────────────────────────────────\n * It replaces `MergeBodyFiler`, which filed the same bytes under\n * `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md` so a later `wp-land-pr` could find\n * them. That store existed because the PR DESCRIPTION was a different (much longer) string from the\n * merge body, so the bytes finish produced lived nowhere else. Since the two surfaces were swapped —\n * the description IS the compact body, and the dashboard moved into the PR's comments — the durable\n * copy is the PR itself. A machine-global cache of a fact GitHub owns can only be missing, stale, or on\n * the wrong computer, and it was all three. `wp-land-pr` now reads `gh pr view --json body`.\n *\n * So both callers reach the same place from different directions: finish already holds the bytes it\n * just published, land reads them back from the PR, and each needs them on disk for the length of one\n * `gh` invocation. See `decisions/0005-the-pr-description-is-the-merge-body.md`.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class MergeBodyTempFile {\n /** @returns an absolute path to a file holding exactly `body`. Never ''. */\n write(body: string): string {\n const file = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'wp-merge-body-')), MERGE_BODY_FILE);\n fs.writeFileSync(file, body);\n return file;\n }\n}\n"]}
|
|
@@ -5,26 +5,16 @@ require("reflect-metadata");
|
|
|
5
5
|
const inversify_1 = require("inversify");
|
|
6
6
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
7
7
|
const pr_gate_app_1 = require("./pr-gate-app");
|
|
8
|
-
const land_pr_command_1 = require("./commands/land-pr-command");
|
|
9
|
-
const FALLBACK_TITLE_ONLY = '--fallback-title-only';
|
|
10
8
|
// Composition root: build the container and resolve the app so inversify constructs the whole DAG.
|
|
11
9
|
(0, rules_config_1.runMain)(async () => {
|
|
12
10
|
// autobind self-binds every @injectable(Singleton) tooling class (replaces the buildProviderModule registry scan)
|
|
13
11
|
const container = new inversify_1.Container({ autobind: true });
|
|
14
|
-
// Reject `--help`/bogus flags BEFORE the app touches git
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
+ ' TITLE + LINK and a line saying the gated body was unavailable. The PR\n'
|
|
22
|
-
+ ' DESCRIPTION is never used — dumping a PR Gate Dashboard into main is\n'
|
|
23
|
-
+ ' the ugly git log this whole mechanism exists to prevent. Do not pass\n'
|
|
24
|
-
+ ' this on your own initiative; ask.'),
|
|
25
|
-
]));
|
|
26
|
-
const opts = new land_pr_command_1.LandPrOptions();
|
|
27
|
-
opts.fallbackTitleOnly = args.has(FALLBACK_TITLE_ONLY);
|
|
28
|
-
await container.get(pr_gate_app_1.PrGateApp).landPr(opts);
|
|
12
|
+
// Reject `--help`/bogus flags BEFORE the app touches git. This command takes NO flags: the commit
|
|
13
|
+
// body is the PR's own description, read back from GitHub, so there is nothing left for a human to
|
|
14
|
+
// choose. `--fallback-title-only` was deleted with the machine-global receipt store that made it
|
|
15
|
+
// necessary — see decisions/0005 — and a mistyped flag must still refuse rather than be ignored on a
|
|
16
|
+
// command that writes main's history.
|
|
17
|
+
container.get(rules_config_1.CliArgs).parse(new rules_config_1.CliUsage('wp-land-pr', "Squash-merge this branch's PR into main with its description as the commit body.", []));
|
|
18
|
+
await container.get(pr_gate_app_1.PrGateApp).landPr();
|
|
29
19
|
});
|
|
30
20
|
//# sourceMappingURL=wp-land-pr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wp-land-pr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-land-pr.ts"],"names":[],"mappings":";;;AACA,4BAA0B;AAC1B,yCAAsC;AACtC,
|
|
1
|
+
{"version":3,"file":"wp-land-pr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-land-pr.ts"],"names":[],"mappings":";;;AACA,4BAA0B;AAC1B,yCAAsC;AACtC,0DAAqE;AACrE,+CAA0C;AAE1C,mGAAmG;AACnG,IAAA,sBAAO,EAAC,KAAK,IAAmB,EAAE;IAC9B,kHAAkH;IAClH,MAAM,SAAS,GAAG,IAAI,qBAAS,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,kGAAkG;IAClG,mGAAmG;IACnG,iGAAiG;IACjG,qGAAqG;IACrG,sCAAsC;IACtC,SAAS,CAAC,GAAG,CAAC,sBAAO,CAAC,CAAC,KAAK,CAAC,IAAI,uBAAQ,CACrC,YAAY,EACZ,kFAAkF,EAClF,EAAE,CACL,CAAC,CAAC;IACH,MAAM,SAAS,CAAC,GAAG,CAAC,uBAAS,CAAC,CAAC,MAAM,EAAE,CAAC;AAC5C,CAAC,CAAC,CAAC","sourcesContent":["#!/usr/bin/env node\nimport 'reflect-metadata';\nimport { Container } from 'inversify';\nimport { runMain, CliArgs, CliUsage } from '@webpieces/rules-config';\nimport { PrGateApp } from './pr-gate-app';\n\n// Composition root: build the container and resolve the app so inversify constructs the whole DAG.\nrunMain(async (): Promise<void> => {\n // autobind self-binds every @injectable(Singleton) tooling class (replaces the buildProviderModule registry scan)\n const container = new Container({ autobind: true });\n // Reject `--help`/bogus flags BEFORE the app touches git. This command takes NO flags: the commit\n // body is the PR's own description, read back from GitHub, so there is nothing left for a human to\n // choose. `--fallback-title-only` was deleted with the machine-global receipt store that made it\n // necessary — see decisions/0005 — and a mistyped flag must still refuse rather than be ignored on a\n // command that writes main's history.\n container.get(CliArgs).parse(new CliUsage(\n 'wp-land-pr',\n \"Squash-merge this branch's PR into main with its description as the commit body.\",\n [],\n ));\n await container.get(PrGateApp).landPr();\n});\n"]}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { DotWebpieces, PrBodyStore } from '@webpieces/rules-config';
|
|
2
|
-
/** Everything needed to file one gated squash body. Data-only, per CLAUDE.md. */
|
|
3
|
-
export declare class MergeBodyRequest {
|
|
4
|
-
treeRoot: string;
|
|
5
|
-
branch: string;
|
|
6
|
-
feature: string;
|
|
7
|
-
prNumber: string;
|
|
8
|
-
prUrl: string;
|
|
9
|
-
body: string;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Files the gated squash-commit body where LANDING can find it, and returns the path to hand `gh`.
|
|
13
|
-
*
|
|
14
|
-
* MACHINE-GLOBAL, keyed by the PR's own identity —
|
|
15
|
-
* `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md`. It used to be written into the
|
|
16
|
-
* rendering worktree's `pr-review/<branch>/`, which made landing work only while the branch never moved
|
|
17
|
-
* trees: the gated flow ran in the primary clone, landing happened from a linked worktree, and
|
|
18
|
-
* `wp-land-pr` found nothing and said "Nothing to land" at a perfectly good PR. See PrBodyStore for why
|
|
19
|
-
* the PR's identity — not the tree, not the branch — is the right key.
|
|
20
|
-
*
|
|
21
|
-
* There is deliberately NO second write to the old in-repo path. Per CLAUDE.md this is a hard cut: two
|
|
22
|
-
* homes for the receipt is two answers to "which bytes land", and the stale one wins in exactly the
|
|
23
|
-
* situation that broke. `wp-land-pr` prints a LOUD one-time signpost if it finds a body left by an older
|
|
24
|
-
* release, and never reads it.
|
|
25
|
-
*
|
|
26
|
-
* Separate from FinishUpsertPrCommand because it is the WRITE half of a two-command contract whose read
|
|
27
|
-
* half lives in LandPrCommand — the pair is the thing that has to stay true, and a private method inside
|
|
28
|
-
* a 600-line command is not something the read half's spec can hold still next to.
|
|
29
|
-
*/
|
|
30
|
-
export declare class MergeBodyFiler {
|
|
31
|
-
private readonly prBodies;
|
|
32
|
-
private readonly dotDir;
|
|
33
|
-
constructor(prBodies: PrBodyStore, dotDir?: DotWebpieces);
|
|
34
|
-
/**
|
|
35
|
-
* @returns the file to pass as `gh pr merge --body-file`. Never ''.
|
|
36
|
-
*
|
|
37
|
-
* The temp file is the ONE case the store cannot serve: `gh pr view` gave no number back (or the
|
|
38
|
-
* remote could not be parsed), so there is no key to file under. This process still holds the bytes
|
|
39
|
-
* and can merge with them right now; a later `wp-land-pr` correctly reports the PR as not found on
|
|
40
|
-
* this machine, because no durable receipt was ever filed. Saying so beats pretending otherwise.
|
|
41
|
-
*/
|
|
42
|
-
file(request: MergeBodyRequest): string;
|
|
43
|
-
private warnIfDegraded;
|
|
44
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MergeBodyFiler = exports.MergeBodyRequest = void 0;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
-
const os = tslib_1.__importStar(require("os"));
|
|
7
|
-
const path = tslib_1.__importStar(require("path"));
|
|
8
|
-
const rules_config_1 = require("@webpieces/rules-config");
|
|
9
|
-
const inversify_1 = require("inversify");
|
|
10
|
-
/** Everything needed to file one gated squash body. Data-only, per CLAUDE.md. */
|
|
11
|
-
class MergeBodyRequest {
|
|
12
|
-
treeRoot = '';
|
|
13
|
-
branch = '';
|
|
14
|
-
feature = '';
|
|
15
|
-
prNumber = '';
|
|
16
|
-
prUrl = '';
|
|
17
|
-
body = '';
|
|
18
|
-
}
|
|
19
|
-
exports.MergeBodyRequest = MergeBodyRequest;
|
|
20
|
-
/**
|
|
21
|
-
* Files the gated squash-commit body where LANDING can find it, and returns the path to hand `gh`.
|
|
22
|
-
*
|
|
23
|
-
* MACHINE-GLOBAL, keyed by the PR's own identity —
|
|
24
|
-
* `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md`. It used to be written into the
|
|
25
|
-
* rendering worktree's `pr-review/<branch>/`, which made landing work only while the branch never moved
|
|
26
|
-
* trees: the gated flow ran in the primary clone, landing happened from a linked worktree, and
|
|
27
|
-
* `wp-land-pr` found nothing and said "Nothing to land" at a perfectly good PR. See PrBodyStore for why
|
|
28
|
-
* the PR's identity — not the tree, not the branch — is the right key.
|
|
29
|
-
*
|
|
30
|
-
* There is deliberately NO second write to the old in-repo path. Per CLAUDE.md this is a hard cut: two
|
|
31
|
-
* homes for the receipt is two answers to "which bytes land", and the stale one wins in exactly the
|
|
32
|
-
* situation that broke. `wp-land-pr` prints a LOUD one-time signpost if it finds a body left by an older
|
|
33
|
-
* release, and never reads it.
|
|
34
|
-
*
|
|
35
|
-
* Separate from FinishUpsertPrCommand because it is the WRITE half of a two-command contract whose read
|
|
36
|
-
* half lives in LandPrCommand — the pair is the thing that has to stay true, and a private method inside
|
|
37
|
-
* a 600-line command is not something the read half's spec can hold still next to.
|
|
38
|
-
*/
|
|
39
|
-
let MergeBodyFiler = class MergeBodyFiler {
|
|
40
|
-
prBodies;
|
|
41
|
-
dotDir;
|
|
42
|
-
constructor(prBodies, dotDir = rules_config_1.dotWebpieces) {
|
|
43
|
-
this.prBodies = prBodies;
|
|
44
|
-
this.dotDir = dotDir;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* @returns the file to pass as `gh pr merge --body-file`. Never ''.
|
|
48
|
-
*
|
|
49
|
-
* The temp file is the ONE case the store cannot serve: `gh pr view` gave no number back (or the
|
|
50
|
-
* remote could not be parsed), so there is no key to file under. This process still holds the bytes
|
|
51
|
-
* and can merge with them right now; a later `wp-land-pr` correctly reports the PR as not found on
|
|
52
|
-
* this machine, because no durable receipt was ever filed. Saying so beats pretending otherwise.
|
|
53
|
-
*/
|
|
54
|
-
file(request) {
|
|
55
|
-
const origin = new rules_config_1.PrBodyOrigin();
|
|
56
|
-
origin.treeRoot = request.treeRoot;
|
|
57
|
-
origin.primaryRoot = this.dotDir.primaryRoot(request.treeRoot);
|
|
58
|
-
origin.branch = request.branch;
|
|
59
|
-
origin.feature = request.feature;
|
|
60
|
-
origin.prNumber = request.prNumber;
|
|
61
|
-
origin.prUrl = request.prUrl;
|
|
62
|
-
origin.writtenAt = new Date().toISOString();
|
|
63
|
-
const stored = this.prBodies.write(request.treeRoot, request.prNumber, request.body, origin);
|
|
64
|
-
if (stored !== null) {
|
|
65
|
-
process.stdout.write(` merge body → ${stored.bodyFile}\n`);
|
|
66
|
-
this.warnIfDegraded(request.treeRoot);
|
|
67
|
-
return stored.bodyFile;
|
|
68
|
-
}
|
|
69
|
-
const tmp = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'wp-merge-body-')), rules_config_1.MERGE_BODY_FILE);
|
|
70
|
-
fs.writeFileSync(tmp, request.body);
|
|
71
|
-
process.stderr.write(' ⚠️ could not file the merge body under this PR\'s identity (no PR number read back, or the\n' +
|
|
72
|
-
' git remote could not be parsed). It was written to a temp file for THIS run only, so\n' +
|
|
73
|
-
' `pnpm wp-land-pr` will report the PR as not found on this machine.\n');
|
|
74
|
-
return tmp;
|
|
75
|
-
}
|
|
76
|
-
// A degraded home is a receipt written INSIDE the clone — which is the thing that was broken. It
|
|
77
|
-
// still works from here, so it is a warning, not a failure; it is never silent.
|
|
78
|
-
warnIfDegraded(treeRoot) {
|
|
79
|
-
const home = this.prBodies.home(treeRoot);
|
|
80
|
-
if (!home.degraded)
|
|
81
|
-
return;
|
|
82
|
-
process.stderr.write(` ⚠️ that path is INSIDE this clone (${home.reason}), so \`pnpm wp-land-pr\` will only find\n` +
|
|
83
|
-
` it from this clone. Set ${rules_config_1.WEBPIECES_STATE_HOME_ENV} to a writable directory.\n`);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
exports.MergeBodyFiler = MergeBodyFiler;
|
|
87
|
-
exports.MergeBodyFiler = MergeBodyFiler = tslib_1.__decorate([
|
|
88
|
-
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton),
|
|
89
|
-
tslib_1.__metadata("design:paramtypes", [rules_config_1.PrBodyStore,
|
|
90
|
-
rules_config_1.DotWebpieces])
|
|
91
|
-
], MergeBodyFiler);
|
|
92
|
-
//# sourceMappingURL=merge-body-filer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"merge-body-filer.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/merge-body-filer.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,+CAAyB;AACzB,mDAA6B;AAC7B,0DAEiC;AACjC,yCAA2D;AAE3D,iFAAiF;AACjF,MAAa,gBAAgB;IACzB,QAAQ,GAAG,EAAE,CAAC;IACd,MAAM,GAAG,EAAE,CAAC;IACZ,OAAO,GAAG,EAAE,CAAC;IACb,QAAQ,GAAG,EAAE,CAAC;IACd,KAAK,GAAG,EAAE,CAAC;IACX,IAAI,GAAG,EAAE,CAAC;CACb;AAPD,4CAOC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AAEI,IAAM,cAAc,GAApB,MAAM,cAAc;IAEF;IACA;IAFrB,YACqB,QAAqB,EACrB,SAAuB,2BAAY;QADnC,aAAQ,GAAR,QAAQ,CAAa;QACrB,WAAM,GAAN,MAAM,CAA6B;IACrD,CAAC;IAEJ;;;;;;;OAOG;IACH,IAAI,CAAC,OAAyB;QAC1B,MAAM,MAAM,GAAG,IAAI,2BAAY,EAAE,CAAC;QAClC,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACnC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC/B,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QACjC,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACnC,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC7B,MAAM,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAE5C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7F,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;YAC7D,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACtC,OAAO,MAAM,CAAC,QAAQ,CAAC;QAC3B,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC,EAAE,8BAAe,CAAC,CAAC;QACjG,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,kGAAkG;YAClG,+FAA+F;YAC/F,6EAA6E,CAAC,CAAC;QACnF,OAAO,GAAG,CAAC;IACf,CAAC;IAED,iGAAiG;IACjG,gFAAgF;IACxE,cAAc,CAAC,QAAgB;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,0CAA0C,IAAI,CAAC,MAAM,4CAA4C;YACjG,kCAAkC,uCAAwB,6BAA6B,CAAC,CAAC;IACjG,CAAC;CACJ,CAAA;AAjDY,wCAAc;yBAAd,cAAc;IAD1B,IAAA,sBAAU,EAAC,8BAAkB,CAAC,SAAS,CAAC;6CAGN,0BAAW;QACb,2BAAY;GAHhC,cAAc,CAiD1B","sourcesContent":["import * as fs from 'fs';\nimport * as os from 'os';\nimport * as path from 'path';\nimport {\n DotWebpieces, MERGE_BODY_FILE, PrBodyOrigin, PrBodyStore, WEBPIECES_STATE_HOME_ENV, dotWebpieces,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\n/** Everything needed to file one gated squash body. Data-only, per CLAUDE.md. */\nexport class MergeBodyRequest {\n treeRoot = '';\n branch = '';\n feature = '';\n prNumber = '';\n prUrl = '';\n body = '';\n}\n\n/**\n * Files the gated squash-commit body where LANDING can find it, and returns the path to hand `gh`.\n *\n * MACHINE-GLOBAL, keyed by the PR's own identity —\n * `~/.webpieces/prs/<host>/<owner>/<repo>/<n>/merge-commit-body.md`. It used to be written into the\n * rendering worktree's `pr-review/<branch>/`, which made landing work only while the branch never moved\n * trees: the gated flow ran in the primary clone, landing happened from a linked worktree, and\n * `wp-land-pr` found nothing and said \"Nothing to land\" at a perfectly good PR. See PrBodyStore for why\n * the PR's identity — not the tree, not the branch — is the right key.\n *\n * There is deliberately NO second write to the old in-repo path. Per CLAUDE.md this is a hard cut: two\n * homes for the receipt is two answers to \"which bytes land\", and the stale one wins in exactly the\n * situation that broke. `wp-land-pr` prints a LOUD one-time signpost if it finds a body left by an older\n * release, and never reads it.\n *\n * Separate from FinishUpsertPrCommand because it is the WRITE half of a two-command contract whose read\n * half lives in LandPrCommand — the pair is the thing that has to stay true, and a private method inside\n * a 600-line command is not something the read half's spec can hold still next to.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class MergeBodyFiler {\n constructor(\n private readonly prBodies: PrBodyStore,\n private readonly dotDir: DotWebpieces = dotWebpieces,\n ) {}\n\n /**\n * @returns the file to pass as `gh pr merge --body-file`. Never ''.\n *\n * The temp file is the ONE case the store cannot serve: `gh pr view` gave no number back (or the\n * remote could not be parsed), so there is no key to file under. This process still holds the bytes\n * and can merge with them right now; a later `wp-land-pr` correctly reports the PR as not found on\n * this machine, because no durable receipt was ever filed. Saying so beats pretending otherwise.\n */\n file(request: MergeBodyRequest): string {\n const origin = new PrBodyOrigin();\n origin.treeRoot = request.treeRoot;\n origin.primaryRoot = this.dotDir.primaryRoot(request.treeRoot);\n origin.branch = request.branch;\n origin.feature = request.feature;\n origin.prNumber = request.prNumber;\n origin.prUrl = request.prUrl;\n origin.writtenAt = new Date().toISOString();\n\n const stored = this.prBodies.write(request.treeRoot, request.prNumber, request.body, origin);\n if (stored !== null) {\n process.stdout.write(` merge body → ${stored.bodyFile}\\n`);\n this.warnIfDegraded(request.treeRoot);\n return stored.bodyFile;\n }\n\n const tmp = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'wp-merge-body-')), MERGE_BODY_FILE);\n fs.writeFileSync(tmp, request.body);\n process.stderr.write(\n ' ⚠️ could not file the merge body under this PR\\'s identity (no PR number read back, or the\\n' +\n ' git remote could not be parsed). It was written to a temp file for THIS run only, so\\n' +\n ' `pnpm wp-land-pr` will report the PR as not found on this machine.\\n');\n return tmp;\n }\n\n // A degraded home is a receipt written INSIDE the clone — which is the thing that was broken. It\n // still works from here, so it is a warning, not a failure; it is never silent.\n private warnIfDegraded(treeRoot: string): void {\n const home = this.prBodies.home(treeRoot);\n if (!home.degraded) return;\n process.stderr.write(\n ` ⚠️ that path is INSIDE this clone (${home.reason}), so \\`pnpm wp-land-pr\\` will only find\\n` +\n ` it from this clone. Set ${WEBPIECES_STATE_HOME_ENV} to a writable directory.\\n`);\n }\n}\n"]}
|