@webpieces/pr-gate 0.3.281 → 0.3.284
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 +3 -3
- package/src/scripts/git-finishUpsertPr.js +2 -7
- package/src/scripts/git-finishUpsertPr.js.map +1 -1
- package/src/scripts/git-gatherInfo.d.ts +12 -2
- package/src/scripts/git-gatherInfo.js +24 -13
- package/src/scripts/git-gatherInfo.js.map +1 -1
- package/src/scripts/workflow/build-affected.d.ts +4 -3
- package/src/scripts/workflow/build-affected.js +6 -6
- package/src/scripts/workflow/build-affected.js.map +1 -1
- package/src/scripts/workflow/cleanTmp.d.ts +1 -1
- package/src/scripts/workflow/cleanTmp.js +4 -9
- package/src/scripts/workflow/cleanTmp.js.map +1 -1
- package/src/scripts/workflow/git-exec.js +20 -23
- package/src/scripts/workflow/git-exec.js.map +1 -1
- package/src/scripts/workflow/git-findForkPoint.js +7 -14
- package/src/scripts/workflow/git-findForkPoint.js.map +1 -1
- package/src/scripts/workflow/git-readAiBranchName.js +3 -7
- package/src/scripts/workflow/git-readAiBranchName.js.map +1 -1
- package/src/scripts/workflow/git-validateUpToDate.js +6 -8
- package/src/scripts/workflow/git-validateUpToDate.js.map +1 -1
- package/src/scripts/workflow/merge-end.js +15 -18
- package/src/scripts/workflow/merge-end.js.map +1 -1
- package/src/scripts/workflow/merge-start.js +11 -6
- package/src/scripts/workflow/merge-start.js.map +1 -1
- package/src/scripts/wp-build-affected.d.ts +1 -1
- package/src/scripts/wp-build-affected.js +7 -5
- package/src/scripts/wp-build-affected.js.map +1 -1
- package/src/scripts/wp-git-gather.d.ts +2 -0
- package/src/scripts/wp-git-gather.js +15 -0
- package/src/scripts/wp-git-gather.js.map +1 -0
- package/src/scripts/wp-start-upsert-pr.js +3 -9
- package/src/scripts/wp-start-upsert-pr.js.map +1 -1
- package/src/scripts/wp-update-end.js +5 -10
- package/src/scripts/wp-update-end.js.map +1 -1
- package/src/scripts/wp-update-start.js +6 -12
- package/src/scripts/wp-update-start.js.map +1 -1
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/pr-gate",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.284",
|
|
4
4
|
"description": "Gated PR system: 3-point squash-merge, merge validation gate, and red/yellow/green PR dashboard. Standalone scripts, no Nx dependency required.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"wp-git-gather": "./src/scripts/git-
|
|
8
|
+
"wp-git-gather": "./src/scripts/wp-git-gather.js",
|
|
9
9
|
"wp-finish-upsert-pr": "./src/scripts/git-finishUpsertPr.js",
|
|
10
10
|
"wp-build-affected": "./src/scripts/wp-build-affected.js",
|
|
11
11
|
"wp-start-upsert-pr": "./src/scripts/wp-start-upsert-pr.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"directory": "packages/tooling/pr-gate"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@webpieces/rules-config": "0.3.
|
|
26
|
+
"@webpieces/rules-config": "0.3.284"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
@@ -93,11 +93,6 @@ async function main() {
|
|
|
93
93
|
upsertPr(repoRoot, base, body);
|
|
94
94
|
process.stdout.write('\n✅ Done.\n');
|
|
95
95
|
}
|
|
96
|
-
if (require.main === module)
|
|
97
|
-
|
|
98
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
99
|
-
process.stderr.write(message + '\n');
|
|
100
|
-
process.exit(1);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
96
|
+
if (require.main === module)
|
|
97
|
+
(0, rules_config_1.runMain)(main);
|
|
103
98
|
//# sourceMappingURL=git-finishUpsertPr.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-finishUpsertPr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-finishUpsertPr.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n ReviewJson,\n writeTemplate,\n} from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { baseBranchName } from './workflow/branch-naming';\nimport { assertCleanTree, ensurePushed } from './workflow/git-exec';\nimport { runBuildGate, BuildGateOptions } from './workflow/build-affected';\nimport { mergeDirFor, readMergeMarker } from './workflow/merge-state';\nimport { mergeEnd } from './workflow/merge-end';\nimport { MergeContext } from './workflow/merge-start';\nimport {\n computeGateResults,\n countAddedDisables,\n renderDashboard,\n DashboardInput,\n} from '../dashboard/dashboard';\n\n// FINISH of the AI-first PR flow. Runs after the AI has written review.json (see wp-start-upsert-pr).\n// Responsibilities, in order: (1) if a 3-point merge was in progress, validate + commit + FINALIZE the\n// AI's resolution via merge-END (so the PR is posted from the finalized feature branch, not the squash\n// branch); (2) REQUIRE review.json (hard-fail with the schema if absent/invalid); (3) run the\n// authoritative build gate; (4) render the dashboard; (5) create/update the PR via `gh`. This is the\n// ONLY command that posts PRs.\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\nfunction gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n}\n\nfunction buildDashboard(repoRoot: string, buildPassed: boolean, review: ReviewJson): string {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = gitOut(['rev-parse', 'HEAD']);\n const mainHead = gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = gitOut(['diff', range]);\n const title = gitOut(['log', '-1', '--format=%s']);\n\n const gateResults = computeGateResults(config.gates, changedFiles);\n const disables = countAddedDisables(patch);\n const input = new DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review);\n return renderDashboard(input);\n}\n\n// The PR always lives on the stable base branch (the local branch may be a numbered generation\n// base2/base3/…). Look up / create / merge against `baseBranch`, never the numbered branch, or\n// generation 2+ would fail to find its PR and open a duplicate.\nfunction upsertPr(repoRoot: string, baseBranch: string, body: string): void {\n const prDir = prDirFor(repoRoot, getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, body + '\\n');\n\n const existing = gitOut(['log', '-1', '--format=%s']); // title fallback\n const prNumber = spawnSync(\n 'gh', ['pr', 'list', '--head', baseBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';\n\n if (num === '') {\n process.stdout.write('Creating PR...\\n');\n const create = spawnSync('gh', ['pr', 'create', '--head', baseBranch, '--base', 'main', '--title', existing, '--body-file', bodyFile], { stdio: 'inherit' });\n if (create.status !== 0) {\n process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return;\n }\n } else {\n process.stdout.write(`Updating PR #${num}...\\n`);\n spawnSync('gh', ['pr', 'edit', num, '--body-file', bodyFile], { stdio: 'inherit' });\n }\n spawnSync('gh', ['pr', 'merge', baseBranch, '--auto', '--squash'], { stdio: 'inherit' });\n}\n\nexport async function main(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n const mergeDir = mergeDirFor(repoRoot, getFeatureName());\n\n // 1. Finish any in-progress conflict resolution: validate + commit + finalize the branch swap.\n // No marker (or already validated) => no merge in progress => nothing to do (the common case).\n const marker = readMergeMarker(mergeDir);\n if (marker && !marker.validated) {\n await mergeEnd(\n repoRoot, mergeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n marker.conflictedFiles,\n );\n }\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n const review = loadReviewJson(reviewJsonPath(repoRoot, getFeatureName()));\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD — so they MUST be identical, or a\n // fix edited after the merge commit builds green yet a stale commit gets pushed (CI then fails on\n // the committed tree). Require a clean tree here; the tooling won't commit your work for you.\n assertCleanTree(repoRoot);\n\n // 3. Authoritative build gate, then push, then post.\n runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n ));\n // After finalize the local branch is the numbered generation (base2/…); the remote branch and\n // PR live on the stable base name — push and upsert against that.\n const base = baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n ensurePushed(base);\n\n process.stdout.write('\\n' + SEP + '📋 Dashboard + PR\\n' + SEP + '\\n');\n const body = buildDashboard(repoRoot, true, review);\n upsertPr(repoRoot, base, body);\n process.stdout.write('\\n✅ Done.\\n');\n}\n\nif (require.main === module) {\n main().catch((err: Error) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + '\\n');\n process.exit(1);\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"git-finishUpsertPr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-finishUpsertPr.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport {\n loadAndValidate,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n ReviewJson,\n writeTemplate,\n runMain,\n} from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { baseBranchName } from './workflow/branch-naming';\nimport { assertCleanTree, ensurePushed } from './workflow/git-exec';\nimport { runBuildGate, BuildGateOptions } from './workflow/build-affected';\nimport { mergeDirFor, readMergeMarker } from './workflow/merge-state';\nimport { mergeEnd } from './workflow/merge-end';\nimport { MergeContext } from './workflow/merge-start';\nimport {\n computeGateResults,\n countAddedDisables,\n renderDashboard,\n DashboardInput,\n} from '../dashboard/dashboard';\n\n// FINISH of the AI-first PR flow. Runs after the AI has written review.json (see wp-start-upsert-pr).\n// Responsibilities, in order: (1) if a 3-point merge was in progress, validate + commit + FINALIZE the\n// AI's resolution via merge-END (so the PR is posted from the finalized feature branch, not the squash\n// branch); (2) REQUIRE review.json (hard-fail with the schema if absent/invalid); (3) run the\n// authoritative build gate; (4) render the dashboard; (5) create/update the PR via `gh`. This is the\n// ONLY command that posts PRs.\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\nfunction gitOut(args: string[]): string {\n const result = spawnSync('git', args, { encoding: 'utf8' });\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n}\n\nfunction buildDashboard(repoRoot: string, buildPassed: boolean, review: ReviewJson): string {\n const config = loadAndValidate(repoRoot).prGate;\n const forkPoint = gitOut(['merge-base', 'origin/main', 'HEAD']);\n const featureHead = gitOut(['rev-parse', 'HEAD']);\n const mainHead = gitOut(['rev-parse', 'origin/main']);\n const range = `${forkPoint}..${featureHead}`;\n const changedFiles = gitOut(['diff', range, '--name-only']).split('\\n').filter((f: string): boolean => f.trim() !== '');\n const patch = gitOut(['diff', range]);\n const title = gitOut(['log', '-1', '--format=%s']);\n\n const gateResults = computeGateResults(config.gates, changedFiles);\n const disables = countAddedDisables(patch);\n const input = new DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review);\n return renderDashboard(input);\n}\n\n// The PR always lives on the stable base branch (the local branch may be a numbered generation\n// base2/base3/…). Look up / create / merge against `baseBranch`, never the numbered branch, or\n// generation 2+ would fail to find its PR and open a duplicate.\nfunction upsertPr(repoRoot: string, baseBranch: string, body: string): void {\n const prDir = prDirFor(repoRoot, getFeatureName());\n fs.mkdirSync(prDir, { recursive: true });\n const bodyFile = path.join(prDir, 'pr-body.md');\n fs.writeFileSync(bodyFile, body + '\\n');\n\n const existing = gitOut(['log', '-1', '--format=%s']); // title fallback\n const prNumber = spawnSync(\n 'gh', ['pr', 'list', '--head', baseBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';\n\n if (num === '') {\n process.stdout.write('Creating PR...\\n');\n const create = spawnSync('gh', ['pr', 'create', '--head', baseBranch, '--base', 'main', '--title', existing, '--body-file', bodyFile], { stdio: 'inherit' });\n if (create.status !== 0) {\n process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\\n ' + bodyFile + '\\n');\n return;\n }\n } else {\n process.stdout.write(`Updating PR #${num}...\\n`);\n spawnSync('gh', ['pr', 'edit', num, '--body-file', bodyFile], { stdio: 'inherit' });\n }\n spawnSync('gh', ['pr', 'merge', baseBranch, '--auto', '--squash'], { stdio: 'inherit' });\n}\n\nexport async function main(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n // Refresh the AI-facing workflow doc so it's present + current for any failure message to cite.\n writeTemplate(repoRoot, 'webpieces.git-workflow.md');\n const mergeDir = mergeDirFor(repoRoot, getFeatureName());\n\n // 1. Finish any in-progress conflict resolution: validate + commit + finalize the branch swap.\n // No marker (or already validated) => no merge in progress => nothing to do (the common case).\n const marker = readMergeMarker(mergeDir);\n if (marker && !marker.validated) {\n await mergeEnd(\n repoRoot, mergeDir,\n new MergeContext(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber),\n marker.conflictedFiles,\n );\n }\n\n // 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).\n const review = loadReviewJson(reviewJsonPath(repoRoot, getFeatureName()));\n\n // 2b. The build gate validates the WORKING TREE but we push HEAD — so they MUST be identical, or a\n // fix edited after the merge commit builds green yet a stale commit gets pushed (CI then fails on\n // the committed tree). Require a clean tree here; the tooling won't commit your work for you.\n assertCleanTree(repoRoot);\n\n // 3. Authoritative build gate, then push, then post.\n runBuildGate(repoRoot, new BuildGateOptions(\n '🛠️ Build gate (authoritative)', 'pnpm wp-finish-upsert-pr', 'Build failed — no PR created/updated.',\n ));\n // After finalize the local branch is the numbered generation (base2/…); the remote branch and\n // PR live on the stable base name — push and upsert against that.\n const base = baseBranchName(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n ensurePushed(base);\n\n process.stdout.write('\\n' + SEP + '📋 Dashboard + PR\\n' + SEP + '\\n');\n const body = buildDashboard(repoRoot, true, review);\n upsertPr(repoRoot, base, body);\n process.stdout.write('\\n✅ Done.\\n');\n}\n\nif (require.main === module) runMain(main);\n"]}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
interface HashPoints {
|
|
2
|
+
hashForkPoint: string;
|
|
3
|
+
hashFeatureHead: string;
|
|
4
|
+
hashMainHead: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class GatherInfoResult {
|
|
7
|
+
alreadyUpToDate: boolean;
|
|
8
|
+
hashes: HashPoints;
|
|
9
|
+
constructor(alreadyUpToDate: boolean, hashes: HashPoints);
|
|
10
|
+
}
|
|
11
|
+
export declare function gatherInfo(): Promise<GatherInfoResult>;
|
|
12
|
+
export {};
|
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
"use strict";
|
|
3
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.
|
|
3
|
+
exports.GatherInfoResult = void 0;
|
|
4
|
+
exports.gatherInfo = gatherInfo;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
const child_process_1 = require("child_process");
|
|
7
7
|
const fs = tslib_1.__importStar(require("fs"));
|
|
8
8
|
const path = tslib_1.__importStar(require("path"));
|
|
9
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
9
10
|
const git_readAiBranchName_1 = require("./workflow/git-readAiBranchName");
|
|
10
11
|
const git_findForkPoint_1 = require("./workflow/git-findForkPoint");
|
|
11
12
|
const merge_state_1 = require("./workflow/merge-state");
|
|
12
13
|
const git_exec_1 = require("./workflow/git-exec");
|
|
13
14
|
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
15
|
+
// Result of gathering merge context: whether the branch is already even with main (fork point == main
|
|
16
|
+
// HEAD, so there is nothing to merge) plus the 3 hash points. A class (not an object literal) per the
|
|
17
|
+
// codebase's data-structure convention. gatherInfo RETURNS this instead of calling process.exit — a
|
|
18
|
+
// library function must never exit the process (see CliExitError / runMain).
|
|
19
|
+
class GatherInfoResult {
|
|
20
|
+
alreadyUpToDate;
|
|
21
|
+
hashes;
|
|
22
|
+
constructor(alreadyUpToDate, hashes) {
|
|
23
|
+
this.alreadyUpToDate = alreadyUpToDate;
|
|
24
|
+
this.hashes = hashes;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.GatherInfoResult = GatherInfoResult;
|
|
14
28
|
function validateCleanTree(currentBranch, repoRoot) {
|
|
15
29
|
if (currentBranch === 'main') {
|
|
16
|
-
|
|
17
|
-
process.exit(1);
|
|
30
|
+
throw new rules_config_1.CliExitError(1, '❌ Error: Already on main branch. No need to update from main.');
|
|
18
31
|
}
|
|
19
32
|
process.stderr.write(`Current branch: ${currentBranch}\n`);
|
|
20
33
|
// Require a fully-committed tree (tracked AND untracked). The old check used `git diff-index`,
|
|
@@ -37,7 +50,11 @@ function printHashPoints(hashes, currentBranch, mergeDir) {
|
|
|
37
50
|
process.stderr.write(`Merge directory: ${mergeDir}\n`);
|
|
38
51
|
process.stderr.write('\n');
|
|
39
52
|
}
|
|
40
|
-
|
|
53
|
+
// Gather the 3-point merge context for the current feature branch (fetches main, finds the fork
|
|
54
|
+
// point, writes updatemain-hashes.json) and RETURN whether the branch is already even with main.
|
|
55
|
+
// NEVER calls process.exit: it is called both as a library (merge-start) and via the wp-git-gather
|
|
56
|
+
// bin. Callers decide what to do with an up-to-date branch; the bin just prints and exits 0.
|
|
57
|
+
async function gatherInfo() {
|
|
41
58
|
const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
42
59
|
const featureName = (0, git_readAiBranchName_1.getFeatureName)();
|
|
43
60
|
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
@@ -64,18 +81,12 @@ async function main() {
|
|
|
64
81
|
process.stderr.write('There are no new changes from main to merge.\n');
|
|
65
82
|
process.stderr.write('\n');
|
|
66
83
|
process.stderr.write(SEP);
|
|
67
|
-
|
|
84
|
+
return new GatherInfoResult(true, hashes);
|
|
68
85
|
}
|
|
69
86
|
process.stderr.write('Main has advanced. Merge will be needed.\n');
|
|
70
87
|
process.stderr.write('\n');
|
|
71
88
|
process.stderr.write(SEP);
|
|
72
89
|
process.stderr.write('\n');
|
|
73
|
-
|
|
74
|
-
if (require.main === module) {
|
|
75
|
-
main().catch((err) => {
|
|
76
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
77
|
-
process.stderr.write(message + '\n');
|
|
78
|
-
process.exit(1);
|
|
79
|
-
});
|
|
90
|
+
return new GatherInfoResult(false, hashes);
|
|
80
91
|
}
|
|
81
92
|
//# sourceMappingURL=git-gatherInfo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-gatherInfo.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-gatherInfo.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"git-gatherInfo.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-gatherInfo.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync, spawnSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { CliExitError } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { findForkPoint } from './workflow/git-findForkPoint';\nimport { mergeDirFor } from './workflow/merge-state';\nimport { assertCleanTree } from './workflow/git-exec';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\ninterface HashPoints {\n hashForkPoint: string;\n hashFeatureHead: string;\n hashMainHead: string;\n}\n\n// Result of gathering merge context: whether the branch is already even with main (fork point == main\n// HEAD, so there is nothing to merge) plus the 3 hash points. A class (not an object literal) per the\n// codebase's data-structure convention. gatherInfo RETURNS this instead of calling process.exit — a\n// library function must never exit the process (see CliExitError / runMain).\nexport class GatherInfoResult {\n alreadyUpToDate: boolean;\n hashes: HashPoints;\n\n constructor(alreadyUpToDate: boolean, hashes: HashPoints) {\n this.alreadyUpToDate = alreadyUpToDate;\n this.hashes = hashes;\n }\n}\n\nfunction validateCleanTree(currentBranch: string, repoRoot: string): void {\n if (currentBranch === 'main') {\n throw new CliExitError(1, '❌ Error: Already on main branch. No need to update from main.');\n }\n process.stderr.write(`Current branch: ${currentBranch}\\n`);\n // Require a fully-committed tree (tracked AND untracked). The old check used `git diff-index`,\n // which is tracked-only, so a stale untracked dir slipped through and was later swept into the\n // squash commit by `git add -A`. assertCleanTree uses `git status --porcelain` (untracked-aware,\n // gitignore-respecting) and aborts with instructions — the tooling never commits your work for you.\n assertCleanTree(repoRoot);\n}\n\nfunction printHashPoints(hashes: HashPoints, currentBranch: string, mergeDir: string): void {\n process.stderr.write('📍 The 3 Hash Points:\\n');\n process.stderr.write(` 1. Fork point (A): ${hashes.hashForkPoint}\\n`);\n process.stderr.write(` (where ${currentBranch} diverged from main)\\n`);\n process.stderr.write('\\n');\n process.stderr.write(` 2. Feature HEAD (B): ${hashes.hashFeatureHead}\\n`);\n process.stderr.write(` (tip of ${currentBranch})\\n`);\n process.stderr.write('\\n');\n process.stderr.write(` 3. Main HEAD (C): ${hashes.hashMainHead}\\n`);\n process.stderr.write(' (current origin/main)\\n');\n process.stderr.write('\\n');\n process.stderr.write(`Merge directory: ${mergeDir}\\n`);\n process.stderr.write('\\n');\n}\n\n// Gather the 3-point merge context for the current feature branch (fetches main, finds the fork\n// point, writes updatemain-hashes.json) and RETURN whether the branch is already even with main.\n// NEVER calls process.exit: it is called both as a library (merge-start) and via the wp-git-gather\n// bin. Callers decide what to do with an up-to-date branch; the bin just prints and exits 0.\nexport async function gatherInfo(): Promise<GatherInfoResult> {\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const featureName = getFeatureName();\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const mergeDir = mergeDirFor(repoRoot, featureName);\n fs.mkdirSync(mergeDir, { recursive: true });\n\n validateCleanTree(currentBranch, repoRoot);\n\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.stderr.write('📍 Gathering Merge Context\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n process.stderr.write('Fetching latest changes from origin/main...\\n');\n spawnSync('git', ['fetch', 'origin', 'main'], { stdio: 'inherit' });\n\n await findForkPoint('merge');\n\n const hashesFile = path.join(mergeDir, 'updatemain-hashes.json');\n const hashes = JSON.parse(fs.readFileSync(hashesFile, 'utf8')) as HashPoints;\n\n printHashPoints(hashes, currentBranch, mergeDir);\n\n if (hashes.hashForkPoint === hashes.hashMainHead) {\n process.stderr.write(SEP);\n process.stderr.write('✅ Already up to date with main!\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n process.stderr.write('Your branch has not diverged from main.\\n');\n process.stderr.write('There are no new changes from main to merge.\\n');\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n return new GatherInfoResult(true, hashes);\n }\n\n process.stderr.write('Main has advanced. Merge will be needed.\\n');\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n return new GatherInfoResult(false, hashes);\n}\n"]}
|
|
@@ -31,8 +31,9 @@ export declare class BuildGateOptions {
|
|
|
31
31
|
constructor(label: string, rerunCommand: string, failureHeadline: string);
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
* Run the configured build gate with consistent framing,
|
|
35
|
-
* source of truth: wp-start-upsert-pr and
|
|
36
|
-
* differ) so a fix to the gate flow is
|
|
34
|
+
* Run the configured build gate with consistent framing, throwing CliExitError(buildCode) on failure
|
|
35
|
+
* so the bin's main()/runMain owns the exit. Single source of truth: wp-start-upsert-pr and
|
|
36
|
+
* wp-finish-upsert-pr both call THIS (only the BuildGateOptions differ) so a fix to the gate flow is
|
|
37
|
+
* made once, not in two near-identical copies.
|
|
37
38
|
*/
|
|
38
39
|
export declare function runBuildGate(repoRoot: string, opts: BuildGateOptions): void;
|
|
@@ -64,9 +64,10 @@ class BuildGateOptions {
|
|
|
64
64
|
}
|
|
65
65
|
exports.BuildGateOptions = BuildGateOptions;
|
|
66
66
|
/**
|
|
67
|
-
* Run the configured build gate with consistent framing,
|
|
68
|
-
* source of truth: wp-start-upsert-pr and
|
|
69
|
-
* differ) so a fix to the gate flow is
|
|
67
|
+
* Run the configured build gate with consistent framing, throwing CliExitError(buildCode) on failure
|
|
68
|
+
* so the bin's main()/runMain owns the exit. Single source of truth: wp-start-upsert-pr and
|
|
69
|
+
* wp-finish-upsert-pr both call THIS (only the BuildGateOptions differ) so a fix to the gate flow is
|
|
70
|
+
* made once, not in two near-identical copies.
|
|
70
71
|
*/
|
|
71
72
|
function runBuildGate(repoRoot, opts) {
|
|
72
73
|
const buildCommand = resolveBuildCommand(repoRoot);
|
|
@@ -75,10 +76,9 @@ function runBuildGate(repoRoot, opts) {
|
|
|
75
76
|
` ${buildCommand}\n\n`);
|
|
76
77
|
const buildCode = runConfiguredBuildGate(repoRoot);
|
|
77
78
|
if (buildCode !== 0) {
|
|
78
|
-
|
|
79
|
+
throw new rules_config_1.CliExitError(buildCode, `\n❌ ${opts.failureHeadline}\n\n` +
|
|
79
80
|
`Run THIS exact command to reproduce and fix all errors, then re-run ${opts.rerunCommand}:\n\n` +
|
|
80
|
-
` ${buildCommand}\n
|
|
81
|
-
process.exit(buildCode);
|
|
81
|
+
` ${buildCommand}\n`);
|
|
82
82
|
}
|
|
83
83
|
process.stdout.write('\n✅ Build passed.\n');
|
|
84
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-affected.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/build-affected.ts"],"names":[],"mappings":";;;AAkBA,kDAGC;AAOD,4CAKC;AAQD,wDAEC;
|
|
1
|
+
{"version":3,"file":"build-affected.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/build-affected.ts"],"names":[],"mappings":";;;AAkBA,kDAGC;AAOD,4CAKC;AAQD,wDAEC;AA4BD,oCAgBC;AAvFD,iDAA0C;AAC1C,0DAAwE;AAExE,2FAA2F;AAC3F,0FAA0F;AAC1F,0FAA0F;AAC1F,kCAAkC;AAClC,gGAAgG;AAChG,kGAAkG;AAClG,kGAAkG;AAClG,0FAA0F;AAC7E,QAAA,qBAAqB,GAAG,wEAAwE,CAAC;AAE9G;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,QAAgB;IAChD,MAAM,UAAU,GAAG,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;IACjE,OAAO,UAAU,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,6BAAqB,CAAC;AACrG,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,QAAgB,EAAE,YAAqB;IACpE,MAAM,GAAG,GAAG,YAAY,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,6BAAqB,CAAC;IAC5G,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,GAAG,MAAM,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAA,yBAAS,EAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,QAAgB;IACnD,OAAO,gBAAgB,CAAC,QAAQ,EAAE,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,GAAG,GAAG,0DAA0D,CAAC;AAEvE;;;;;GAKG;AACH,MAAa,gBAAgB;IACzB,KAAK,CAAS,CAAY,sCAAsC;IAChE,YAAY,CAAS,CAAK,gDAAgD;IAC1E,eAAe,CAAS,CAAE,gCAAgC;IAE1D,YAAY,KAAa,EAAE,YAAoB,EAAE,eAAuB;QACpE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,eAAe,GAAG,eAAe,CAAC;IAC3C,CAAC;CACJ;AAVD,4CAUC;AAED;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,QAAgB,EAAE,IAAsB;IACjE,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC,CAAC;IAClE,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,6GAA6G;QAC7G,OAAO,YAAY,MAAM,CAC5B,CAAC;IACF,MAAM,SAAS,GAAG,sBAAsB,CAAC,QAAQ,CAAC,CAAC;IACnD,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,2BAAY,CAAC,SAAS,EAC5B,OAAO,IAAI,CAAC,eAAe,MAAM;YACjC,uEAAuE,IAAI,CAAC,YAAY,OAAO;YAC/F,OAAO,YAAY,IAAI,CAC1B,CAAC;IACN,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;AAChD,CAAC","sourcesContent":["import { spawnSync } from 'child_process';\nimport { loadAndValidate, CliExitError } from '@webpieces/rules-config';\n\n// Single source of truth for the build gate. Both `wp-build-affected` (CI + local) and the\n// merge validation gate (`wp-finish-upsert-pr`) run THIS, so \"what CI runs\" and \"what the\n// PR command runs\" can never drift. nx `affected` only rebuilds changed projects, so this\n// stays fast on a large monorepo.\n// `--base=$(git merge-base origin/main HEAD)` (the fork point) instead of `--base=origin/main`:\n// origin/main rebuilds projects touched by OTHER people's merged PRs (your branch still holds the\n// pre-merge versions, so nx sees them as \"affected\"). The fork point scopes affected to only YOUR\n// branch's changes. The `$(...)` resolves because runBuildAffected runs with shell: true.\nexport const DEFAULT_BUILD_COMMAND = 'pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)';\n\n/**\n * Resolve the exact build command this gate will run for a repo: the project's configured\n * PrGateConfig.buildCommand, or the default affected-ci command when none is set. Callers\n * print this so the AI knows precisely which command to run locally to get the gate passing.\n */\nexport function resolveBuildCommand(repoRoot: string): string {\n const configured = loadAndValidate(repoRoot).prGate.buildCommand;\n return configured !== undefined && configured.trim() !== '' ? configured : DEFAULT_BUILD_COMMAND;\n}\n\n/**\n * Run the build gate. Returns the process exit code (0 = pass). `buildCommand` overrides\n * the default (sourced from PrGateConfig.buildCommand by callers); empty/undefined uses the\n * default affected-ci command.\n */\nexport function runBuildAffected(repoRoot: string, buildCommand?: string): number {\n const cmd = buildCommand !== undefined && buildCommand.trim() !== '' ? buildCommand : DEFAULT_BUILD_COMMAND;\n process.stdout.write(`\\n▶ Build gate: ${cmd}\\n\\n`);\n const result = spawnSync(cmd, { stdio: 'inherit', cwd: repoRoot, shell: true });\n return result.status ?? 1;\n}\n\n/**\n * Run the build gate using the project's configured command (PrGateConfig.buildCommand). The\n * finish-upsert-pr gate and wp-start-upsert-pr both call THIS so they provably build with the same\n * command — the resolution the AI validates is built exactly as the PR command builds it.\n * Returns the exit code; callers print their own re-run hint.\n */\nexport function runConfiguredBuildGate(repoRoot: string): number {\n return runBuildAffected(repoRoot, loadAndValidate(repoRoot).prGate.buildCommand);\n}\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n/**\n * The two facts that differ between the wp-start (advisory) and wp-finish (authoritative) build\n * gates. Everything else — resolve command, run it, print the re-run hint, exit non-zero on failure\n * — is identical, so it lives once in runBuildGate and the difference is passed in here. A class (not\n * an object literal) per the codebase's data-structure convention.\n */\nexport class BuildGateOptions {\n label: string; // section header shown above the gate\n rerunCommand: string; // command the AI re-runs after fixing the build\n failureHeadline: string; // first line printed on failure\n\n constructor(label: string, rerunCommand: string, failureHeadline: string) {\n this.label = label;\n this.rerunCommand = rerunCommand;\n this.failureHeadline = failureHeadline;\n }\n}\n\n/**\n * Run the configured build gate with consistent framing, throwing CliExitError(buildCode) on failure\n * so the bin's main()/runMain owns the exit. Single source of truth: wp-start-upsert-pr and\n * wp-finish-upsert-pr both call THIS (only the BuildGateOptions differ) so a fix to the gate flow is\n * made once, not in two near-identical copies.\n */\nexport function runBuildGate(repoRoot: string, opts: BuildGateOptions): void {\n const buildCommand = resolveBuildCommand(repoRoot);\n process.stdout.write('\\n' + SEP + opts.label + '\\n' + SEP + '\\n');\n process.stdout.write(\n `Running the build gate. To get it passing, run the SAME command yourself and fix everything it reports:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n const buildCode = runConfiguredBuildGate(repoRoot);\n if (buildCode !== 0) {\n throw new CliExitError(buildCode,\n `\\n❌ ${opts.failureHeadline}\\n\\n` +\n `Run THIS exact command to reproduce and fix all errors, then re-run ${opts.rerunCommand}:\\n\\n` +\n ` ${buildCommand}\\n`,\n );\n }\n process.stdout.write('\\n✅ Build passed.\\n');\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function
|
|
1
|
+
export declare function cleanTmp(): Promise<void>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.cleanTmp = cleanTmp;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const child_process_1 = require("child_process");
|
|
6
6
|
const fs = tslib_1.__importStar(require("fs"));
|
|
@@ -13,7 +13,7 @@ const SEP = '━━━━━━━━━━━━━━━━━━━━━━
|
|
|
13
13
|
// only their stale subdirs are cleaned. `LEGACY_PREFIXES` sweeps the old flat top-level layout
|
|
14
14
|
// (`merge-<feature>`/`review-<feature>`/`pr-<feature>`) so it self-clears after the move.
|
|
15
15
|
const LEGACY_PREFIXES = ['merge-', 'review-', 'pr-'];
|
|
16
|
-
async function
|
|
16
|
+
async function cleanTmp() {
|
|
17
17
|
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
18
18
|
const tmpBase = path.join(repoRoot, rules_config_1.WEBPIECES_TMP_DIR);
|
|
19
19
|
if (!fs.existsSync(tmpBase)) {
|
|
@@ -86,11 +86,6 @@ function cleanLegacyTopLevel(tmpBase, now, cutoffMs) {
|
|
|
86
86
|
}
|
|
87
87
|
return count;
|
|
88
88
|
}
|
|
89
|
-
if (require.main === module)
|
|
90
|
-
|
|
91
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
92
|
-
process.stderr.write(message + '\n');
|
|
93
|
-
process.exit(1);
|
|
94
|
-
});
|
|
95
|
-
}
|
|
89
|
+
if (require.main === module)
|
|
90
|
+
(0, rules_config_1.runMain)(cleanTmp);
|
|
96
91
|
//# sourceMappingURL=cleanTmp.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cleanTmp.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/cleanTmp.ts"],"names":[],"mappings":";;AAcA,
|
|
1
|
+
{"version":3,"file":"cleanTmp.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/cleanTmp.ts"],"names":[],"mappings":"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","sourcesContent":["import { execSync } from 'child_process';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { WEBPIECES_TMP_DIR, MERGE_INFO_DIR, PR_INFO_DIR, runMain } from '@webpieces/rules-config';\n\nconst CUTOFF_DAYS = 30;\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// Per-feature workflow dirs now live under `.webpieces/merge-info/<feature>` and\n// `.webpieces/pr-info/<feature>`; those two homes are permanent (like hooks/ and instruct-ai/) and\n// only their stale subdirs are cleaned. `LEGACY_PREFIXES` sweeps the old flat top-level layout\n// (`merge-<feature>`/`review-<feature>`/`pr-<feature>`) so it self-clears after the move.\nconst LEGACY_PREFIXES = ['merge-', 'review-', 'pr-'];\n\nexport async function cleanTmp(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const tmpBase = path.join(repoRoot, WEBPIECES_TMP_DIR);\n\n if (!fs.existsSync(tmpBase)) {\n return;\n }\n\n process.stdout.write('\\n');\n process.stdout.write(SEP);\n process.stdout.write('🧹 Cleaning Old Temporary Directories\\n');\n process.stdout.write(SEP);\n process.stdout.write('\\n');\n process.stdout.write(`Location: ${tmpBase}\\n`);\n process.stdout.write(`Retention: ${CUTOFF_DAYS} days\\n`);\n process.stdout.write('\\n');\n\n const cutoffMs = CUTOFF_DAYS * 24 * 60 * 60 * 1000;\n const now = Date.now();\n let deletedCount = 0;\n\n // Stale per-feature subdirs under each permanent home.\n deletedCount += cleanStaleSubdirs(path.join(tmpBase, MERGE_INFO_DIR), MERGE_INFO_DIR, now, cutoffMs);\n deletedCount += cleanStaleSubdirs(path.join(tmpBase, PR_INFO_DIR), PR_INFO_DIR, now, cutoffMs);\n // Legacy flat top-level dirs from before the merge-info/pr-info nesting.\n deletedCount += cleanLegacyTopLevel(tmpBase, now, cutoffMs);\n\n if (deletedCount === 0) {\n process.stdout.write(` ✅ No directories older than ${CUTOFF_DAYS} days found\\n`);\n } else {\n process.stdout.write('\\n');\n process.stdout.write(` ✅ Deleted ${deletedCount} old director${deletedCount === 1 ? 'y' : 'ies'}\\n`);\n }\n\n process.stdout.write('\\n');\n process.stdout.write(SEP);\n process.stdout.write('\\n');\n}\n\n// Delete immediate subdirs of `parentDir` whose mtime is older than the cutoff. `label` prefixes\n// the printed path so output reads e.g. `merge-info/dean-foo`. No-op if the home doesn't exist.\nfunction cleanStaleSubdirs(parentDir: string, label: string, now: number, cutoffMs: number): number {\n if (!fs.existsSync(parentDir)) return 0;\n let count = 0;\n for (const entry of fs.readdirSync(parentDir)) {\n const fullPath = path.join(parentDir, entry);\n const stat = fs.statSync(fullPath);\n if (!stat.isDirectory()) continue;\n if (now - stat.mtimeMs < cutoffMs) continue;\n process.stdout.write(` 🗑️ Deleting: ${label}/${entry}\\n`);\n fs.rmSync(fullPath, { recursive: true, force: true });\n count += 1;\n }\n return count;\n}\n\n// Sweep the pre-nesting flat layout (`merge-<feature>` etc.), skipping the current homes (whose\n// names also start with `merge-`/`pr-`) so this never deletes merge-info/ or pr-info/ themselves.\nfunction cleanLegacyTopLevel(tmpBase: string, now: number, cutoffMs: number): number {\n let count = 0;\n for (const entry of fs.readdirSync(tmpBase)) {\n if (entry === MERGE_INFO_DIR || entry === PR_INFO_DIR) continue;\n if (!LEGACY_PREFIXES.some((prefix: string): boolean => entry.startsWith(prefix))) continue;\n const fullPath = path.join(tmpBase, entry);\n const stat = fs.statSync(fullPath);\n if (!stat.isDirectory()) continue;\n if (now - stat.mtimeMs < cutoffMs) continue;\n process.stdout.write(` 🗑️ Deleting: ${entry}\\n`);\n fs.rmSync(fullPath, { recursive: true, force: true });\n count += 1;\n }\n return count;\n}\n\nif (require.main === module) runMain(cleanTmp);\n"]}
|
|
@@ -7,6 +7,7 @@ exports.assertNoUntracked = assertNoUntracked;
|
|
|
7
7
|
exports.runGitChecked = runGitChecked;
|
|
8
8
|
exports.ensurePushed = ensurePushed;
|
|
9
9
|
const child_process_1 = require("child_process");
|
|
10
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
10
11
|
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
11
12
|
// The AI-facing doc the failure messages point at (generated under .webpieces/instruct-ai, gitignored).
|
|
12
13
|
const GIT_WORKFLOW_DOC = '.webpieces/instruct-ai/webpieces.git-workflow.md';
|
|
@@ -16,8 +17,7 @@ const GIT_WORKFLOW_DOC = '.webpieces/instruct-ai/webpieces.git-workflow.md';
|
|
|
16
17
|
function gitQuery(args, cwd, failMsg) {
|
|
17
18
|
const out = (0, child_process_1.spawnSync)('git', args, { encoding: 'utf8', cwd });
|
|
18
19
|
if (out.status !== 0) {
|
|
19
|
-
|
|
20
|
-
process.exit(1);
|
|
20
|
+
throw new rules_config_1.CliExitError(1, `❌ ${failMsg}`);
|
|
21
21
|
}
|
|
22
22
|
return out.stdout.trim();
|
|
23
23
|
}
|
|
@@ -41,16 +41,15 @@ function assertCleanTree(cwd) {
|
|
|
41
41
|
const dirty = uncommittedFiles(cwd);
|
|
42
42
|
if (dirty === '')
|
|
43
43
|
return;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
process.exit(1);
|
|
44
|
+
throw new rules_config_1.CliExitError(1, '\n' + SEP +
|
|
45
|
+
'❌ ERROR: You have uncommitted or untracked changes\n' +
|
|
46
|
+
SEP + '\n' +
|
|
47
|
+
'The webpieces PR tooling will NOT commit your work for you. Commit your\n' +
|
|
48
|
+
'changes, and either commit or delete any untracked files, then re-run.\n\n' +
|
|
49
|
+
'Working tree (git status --porcelain):\n' +
|
|
50
|
+
dirty + '\n\n' +
|
|
51
|
+
`See ${GIT_WORKFLOW_DOC} for the full merge + PR process.\n` +
|
|
52
|
+
SEP);
|
|
54
53
|
}
|
|
55
54
|
/**
|
|
56
55
|
* Narrower guard for the merge-resolve commit point, where tracked resolutions are legitimately in the
|
|
@@ -61,15 +60,14 @@ function assertNoUntracked(cwd) {
|
|
|
61
60
|
const untracked = untrackedFiles(cwd);
|
|
62
61
|
if (untracked === '')
|
|
63
62
|
return;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
process.exit(1);
|
|
63
|
+
throw new rules_config_1.CliExitError(1, '\n' + SEP +
|
|
64
|
+
'❌ ERROR: Untracked files present during merge finalize\n' +
|
|
65
|
+
SEP + '\n' +
|
|
66
|
+
'The tooling will not sweep untracked files into the squash commit. Commit\n' +
|
|
67
|
+
'or delete these, then re-run:\n\n' +
|
|
68
|
+
untracked + '\n\n' +
|
|
69
|
+
`See ${GIT_WORKFLOW_DOC} for the full merge + PR process.\n` +
|
|
70
|
+
SEP);
|
|
73
71
|
}
|
|
74
72
|
/**
|
|
75
73
|
* Run a git command that is expected to succeed; abort the process with a clear message
|
|
@@ -79,8 +77,7 @@ function assertNoUntracked(cwd) {
|
|
|
79
77
|
function runGitChecked(args, errMsg) {
|
|
80
78
|
const result = (0, child_process_1.spawnSync)('git', args, { stdio: 'inherit' });
|
|
81
79
|
if (result.status !== 0) {
|
|
82
|
-
|
|
83
|
-
process.exit(1);
|
|
80
|
+
throw new rules_config_1.CliExitError(1, `❌ ${errMsg} (git ${args.join(' ')} exited ${String(result.status)})`);
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-exec.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/git-exec.ts"],"names":[],"mappings":";;AAqBA,4CAEC;AAGD,wCAEC;AASD,
|
|
1
|
+
{"version":3,"file":"git-exec.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/git-exec.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from 'child_process';\nimport { CliExitError } from '@webpieces/rules-config';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// The AI-facing doc the failure messages point at (generated under .webpieces/instruct-ai, gitignored).\nconst GIT_WORKFLOW_DOC = '.webpieces/instruct-ai/webpieces.git-workflow.md';\n\n// Run a read-only git query from the repo root; abort if git itself errors. Kept `cwd`-explicit (not\n// process.cwd()) because `git ls-files --others` is scoped to the cwd subtree — running from a subdir\n// would miss untracked files elsewhere in the repo.\nfunction gitQuery(args: string[], cwd: string, failMsg: string): string {\n const out = spawnSync('git', args, { encoding: 'utf8', cwd });\n if (out.status !== 0) {\n throw new CliExitError(1, `❌ ${failMsg}`);\n }\n return out.stdout.trim();\n}\n\n// Tracked changes (staged + unstaged) AND untracked files, EXCLUDING gitignored paths — so `.webpieces/`\n// tooling artifacts never count. Empty string = fully committed. Exported for unit testing.\nexport function uncommittedFiles(cwd: string): string {\n return gitQuery(['status', '--porcelain'], cwd, 'Failed to run `git status --porcelain` to check the working tree.');\n}\n\n// Untracked files only (respects .gitignore). Empty string = none. Exported for unit testing.\nexport function untrackedFiles(cwd: string): string {\n return gitQuery(['ls-files', '--others', '--exclude-standard'], cwd, 'Failed to list untracked files (git ls-files --others).');\n}\n\n/**\n * Precondition for every PR/merge entry point: the working tree must be fully committed. The webpieces\n * tooling deliberately does NOT `git add`/`commit` the developer's work for them — a blanket `git add\n * -A` once swept a stale untracked directory into a squash commit, and building the working tree while\n * pushing HEAD let an uncommitted fix pass the build gate yet push a stale commit. Aborts with\n * instructions if anything is uncommitted (tracked or untracked, excluding gitignored).\n */\nexport function assertCleanTree(cwd: string): void {\n const dirty = uncommittedFiles(cwd);\n if (dirty === '') return;\n throw new CliExitError(1,\n '\\n' + SEP +\n '❌ ERROR: You have uncommitted or untracked changes\\n' +\n SEP + '\\n' +\n 'The webpieces PR tooling will NOT commit your work for you. Commit your\\n' +\n 'changes, and either commit or delete any untracked files, then re-run.\\n\\n' +\n 'Working tree (git status --porcelain):\\n' +\n dirty + '\\n\\n' +\n `See ${GIT_WORKFLOW_DOC} for the full merge + PR process.\\n` +\n SEP,\n );\n}\n\n/**\n * Narrower guard for the merge-resolve commit point, where tracked resolutions are legitimately in the\n * tree but untracked files must NOT be swept into the squash commit (replaces the old blanket `git add\n * -A`). Lists any untracked files and aborts so the AI commits or deletes them explicitly.\n */\nexport function assertNoUntracked(cwd: string): void {\n const untracked = untrackedFiles(cwd);\n if (untracked === '') return;\n throw new CliExitError(1,\n '\\n' + SEP +\n '❌ ERROR: Untracked files present during merge finalize\\n' +\n SEP + '\\n' +\n 'The tooling will not sweep untracked files into the squash commit. Commit\\n' +\n 'or delete these, then re-run:\\n\\n' +\n untracked + '\\n\\n' +\n `See ${GIT_WORKFLOW_DOC} for the full merge + PR process.\\n` +\n SEP,\n );\n}\n\n/**\n * Run a git command that is expected to succeed; abort the process with a clear message\n * if it fails. Used for fetch/pull/checkout/commit where silently continuing on failure\n * would operate on stale or wrong state.\n */\nexport function runGitChecked(args: string[], errMsg: string): void {\n const result = spawnSync('git', args, { stdio: 'inherit' });\n if (result.status !== 0) {\n throw new CliExitError(1, `❌ ${errMsg} (git ${args.join(' ')} exited ${String(result.status)})`);\n }\n}\n\n/**\n * Push HEAD to origin/<currentBranch>. Single source of truth shared by wp-start-upsert-pr and\n * wp-finish-upsert-pr (they pushed with identical copy-pasted logic). Uses --force-with-lease for an\n * existing remote branch (the 3-point squash rewrites history) and -u for a brand-new branch.\n */\nexport function ensurePushed(currentBranch: string): void {\n const remoteExists = spawnSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;\n if (remoteExists) {\n runGitChecked(['push', '--force-with-lease', 'origin', `HEAD:${currentBranch}`], 'Failed to push branch');\n } else {\n runGitChecked(['push', '-u', 'origin', `HEAD:${currentBranch}`], 'Failed to push new branch');\n }\n}\n"]}
|
|
@@ -61,7 +61,7 @@ function checkMergeCommits(mergeCommits, outputDir, prefix, featureName, current
|
|
|
61
61
|
}, null, 2);
|
|
62
62
|
fs.writeFileSync(path.join(outputDir, `${prefix}forkpoint-error.json`), errorJson + '\n');
|
|
63
63
|
printMergeFromMainError(commit, parent, featureName, currentBranch);
|
|
64
|
-
|
|
64
|
+
throw new rules_config_1.CliExitError(1, '');
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -78,10 +78,9 @@ function forkPointOutputDir(repoRoot, featureName, workflow) {
|
|
|
78
78
|
}
|
|
79
79
|
async function findForkPoint(workflow) {
|
|
80
80
|
if (workflow !== 'review' && workflow !== 'merge') {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
process.exit(1);
|
|
81
|
+
throw new rules_config_1.CliExitError(1, 'ERROR: Workflow argument required\n' +
|
|
82
|
+
'Usage: git-findForkPoint <workflow>\n' +
|
|
83
|
+
" workflow: 'review' or 'merge'");
|
|
85
84
|
}
|
|
86
85
|
const featureName = (0, git_readAiBranchName_1.getFeatureName)();
|
|
87
86
|
const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
@@ -96,8 +95,7 @@ async function findForkPoint(workflow) {
|
|
|
96
95
|
const forkPointResult = (0, child_process_1.spawnSync)('git', ['merge-base', 'origin/main', 'HEAD'], { encoding: 'utf8' });
|
|
97
96
|
const forkPoint = (forkPointResult.stdout ?? '').trim();
|
|
98
97
|
if (!forkPoint || forkPointResult.status !== 0) {
|
|
99
|
-
|
|
100
|
-
process.exit(1);
|
|
98
|
+
throw new rules_config_1.CliExitError(1, 'ERROR: Could not find common ancestor with origin/main');
|
|
101
99
|
}
|
|
102
100
|
process.stderr.write(`✅ Fork point found: ${forkPoint.slice(0, 7)}\n`);
|
|
103
101
|
process.stderr.write('Checking for improper merges from main...\n');
|
|
@@ -122,11 +120,6 @@ async function findForkPoint(workflow) {
|
|
|
122
120
|
async function main() {
|
|
123
121
|
await findForkPoint(process.argv[2] ?? '');
|
|
124
122
|
}
|
|
125
|
-
if (require.main === module)
|
|
126
|
-
|
|
127
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
128
|
-
process.stderr.write(message + '\n');
|
|
129
|
-
process.exit(1);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
123
|
+
if (require.main === module)
|
|
124
|
+
(0, rules_config_1.runMain)(main);
|
|
132
125
|
//# sourceMappingURL=git-findForkPoint.js.map
|