@webpieces/pr-gate 0.3.190 → 0.3.191
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 +4 -4
- package/src/dashboard/dashboard.d.ts +5 -5
- package/src/dashboard/dashboard.js +35 -12
- package/src/dashboard/dashboard.js.map +1 -1
- package/src/index.js +3 -3
- package/src/index.js.map +1 -1
- package/src/scripts/git-finishUpsertPr.js +161 -0
- package/src/scripts/git-finishUpsertPr.js.map +1 -0
- package/src/scripts/git-updateFromMain.js +7 -14
- package/src/scripts/git-updateFromMain.js.map +1 -1
- package/src/scripts/workflow/build-affected.d.ts +2 -2
- package/src/scripts/workflow/build-affected.js +7 -3
- package/src/scripts/workflow/build-affected.js.map +1 -1
- package/src/scripts/workflow/merge-state.js +1 -1
- package/src/scripts/workflow/merge-state.js.map +1 -1
- package/src/scripts/wp-build-affected.js +4 -1
- package/src/scripts/wp-build-affected.js.map +1 -1
- package/src/scripts/wp-start-upsert-pr.js +65 -0
- package/src/scripts/wp-start-upsert-pr.js.map +1 -0
- package/src/scripts/git-mergeComplete.js +0 -100
- package/src/scripts/git-mergeComplete.js.map +0 -1
- package/src/scripts/wp-upsert-pr.js +0 -103
- package/src/scripts/wp-upsert-pr.js.map +0 -1
- /package/src/scripts/{git-mergeComplete.d.ts → git-finishUpsertPr.d.ts} +0 -0
- /package/src/scripts/{wp-upsert-pr.d.ts → wp-start-upsert-pr.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/pr-gate",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.191",
|
|
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
8
|
"wp-git-update": "./src/scripts/git-updateFromMain.js",
|
|
9
9
|
"wp-git-gather": "./src/scripts/git-gatherInfo.js",
|
|
10
|
-
"wp-
|
|
10
|
+
"wp-finish-upsert-pr": "./src/scripts/git-finishUpsertPr.js",
|
|
11
11
|
"wp-build-affected": "./src/scripts/wp-build-affected.js",
|
|
12
|
-
"wp-upsert-pr": "./src/scripts/wp-upsert-pr.js"
|
|
12
|
+
"wp-start-upsert-pr": "./src/scripts/wp-start-upsert-pr.js"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
15
|
"src/**/*"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"directory": "packages/tooling/pr-gate"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/rules-config": "0.3.
|
|
25
|
+
"@webpieces/rules-config": "0.3.191"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { GateDefinition } from '@webpieces/rules-config';
|
|
1
|
+
import { GateDefinition, ReviewJson } from '@webpieces/rules-config';
|
|
2
2
|
export declare class GateResult {
|
|
3
3
|
name: string;
|
|
4
|
-
|
|
4
|
+
color: string;
|
|
5
5
|
matchedFiles: string[];
|
|
6
|
-
constructor(name: string,
|
|
6
|
+
constructor(name: string, color: string, matchedFiles: string[]);
|
|
7
7
|
}
|
|
8
8
|
export declare function computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[];
|
|
9
9
|
export declare class DisableCounts {
|
|
@@ -21,7 +21,7 @@ export declare class DashboardInput {
|
|
|
21
21
|
forkPoint: string;
|
|
22
22
|
featureHead: string;
|
|
23
23
|
mainHead: string;
|
|
24
|
-
|
|
25
|
-
constructor(title: string, gateResults: GateResult[], disables: DisableCounts, buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string,
|
|
24
|
+
review: ReviewJson;
|
|
25
|
+
constructor(title: string, gateResults: GateResult[], disables: DisableCounts, buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, review: ReviewJson);
|
|
26
26
|
}
|
|
27
27
|
export declare function renderDashboard(input: DashboardInput): string;
|
|
@@ -47,19 +47,22 @@ function matchesAny(patterns, file) {
|
|
|
47
47
|
}
|
|
48
48
|
class GateResult {
|
|
49
49
|
name;
|
|
50
|
-
|
|
50
|
+
color; // 'yellow' | 'red' — the color shown WHEN files matched (green is implicit)
|
|
51
51
|
matchedFiles;
|
|
52
|
-
constructor(name,
|
|
52
|
+
constructor(name, color, matchedFiles) {
|
|
53
53
|
this.name = name;
|
|
54
|
-
this.
|
|
54
|
+
this.color = color;
|
|
55
55
|
this.matchedFiles = matchedFiles;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
exports.GateResult = GateResult;
|
|
59
|
+
// Disabled gates are in-file examples (JSON has no comments) — skip them entirely.
|
|
59
60
|
function computeGateResults(gates, changedFiles) {
|
|
60
|
-
return gates
|
|
61
|
+
return gates
|
|
62
|
+
.filter((gate) => !gate.disabled)
|
|
63
|
+
.map((gate) => {
|
|
61
64
|
const matched = changedFiles.filter((file) => matchesAny(gate.patterns, file));
|
|
62
|
-
return new GateResult(gate.name, gate.
|
|
65
|
+
return new GateResult(gate.name, gate.color, matched);
|
|
63
66
|
});
|
|
64
67
|
}
|
|
65
68
|
class DisableCounts {
|
|
@@ -103,8 +106,8 @@ class DashboardInput {
|
|
|
103
106
|
forkPoint;
|
|
104
107
|
featureHead;
|
|
105
108
|
mainHead;
|
|
106
|
-
summary
|
|
107
|
-
constructor(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead,
|
|
109
|
+
review; // AI-authored risk/violations/summary (from review.json)
|
|
110
|
+
constructor(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review) {
|
|
108
111
|
this.title = title;
|
|
109
112
|
this.gateResults = gateResults;
|
|
110
113
|
this.disables = disables;
|
|
@@ -112,16 +115,34 @@ class DashboardInput {
|
|
|
112
115
|
this.forkPoint = forkPoint;
|
|
113
116
|
this.featureHead = featureHead;
|
|
114
117
|
this.mainHead = mainHead;
|
|
115
|
-
this.
|
|
118
|
+
this.review = review;
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
121
|
exports.DashboardInput = DashboardInput;
|
|
119
122
|
function gateLine(result) {
|
|
120
123
|
if (result.matchedFiles.length === 0)
|
|
121
124
|
return `**${result.name}:** 🟢 No`;
|
|
122
|
-
const emoji = result.
|
|
125
|
+
const emoji = result.color === 'red' ? '🔴' : '🟡';
|
|
123
126
|
return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;
|
|
124
127
|
}
|
|
128
|
+
// 10-cell risk bar colored by band (🟩 ≤25, 🟨 ≤50, 🟧 ≤75, 🟥 >75), at least one filled cell —
|
|
129
|
+
// ported from trytami's github_risk_bar (git-display-utils.sh).
|
|
130
|
+
function riskBar(score) {
|
|
131
|
+
const clamped = Math.max(0, Math.min(100, score));
|
|
132
|
+
const cell = clamped <= 25 ? '🟩' : clamped <= 50 ? '🟨' : clamped <= 75 ? '🟧' : '🟥';
|
|
133
|
+
const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));
|
|
134
|
+
return cell.repeat(filled) + '⬜'.repeat(10 - filled);
|
|
135
|
+
}
|
|
136
|
+
// RISK section (trytami's AI half): Risk Score bar, Risk Level, Pattern Violations.
|
|
137
|
+
function riskLines(review) {
|
|
138
|
+
const violations = review.violations.length;
|
|
139
|
+
const violationLine = violations === 0 ? '🟢 No' : `🟡 Yes (${violations} violation(s))`;
|
|
140
|
+
return [
|
|
141
|
+
`**Risk Score:** ${riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,
|
|
142
|
+
`**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,
|
|
143
|
+
`**Pattern Violations:** ${violationLine}`,
|
|
144
|
+
];
|
|
145
|
+
}
|
|
125
146
|
function disableLine(disables) {
|
|
126
147
|
if (disables.webpiecesCount === 0)
|
|
127
148
|
return '**Webpieces Disables Added:** 🟢 No';
|
|
@@ -132,6 +153,8 @@ function renderDashboard(input) {
|
|
|
132
153
|
const lines = [];
|
|
133
154
|
lines.push('## 🚦 PR Gate Dashboard');
|
|
134
155
|
lines.push('');
|
|
156
|
+
for (const line of riskLines(input.review))
|
|
157
|
+
lines.push(line);
|
|
135
158
|
lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);
|
|
136
159
|
for (const result of input.gateResults)
|
|
137
160
|
lines.push(gateLine(result));
|
|
@@ -139,9 +162,9 @@ function renderDashboard(input) {
|
|
|
139
162
|
const eslintEmoji = input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;
|
|
140
163
|
lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);
|
|
141
164
|
lines.push('');
|
|
142
|
-
if (input.summary.trim() !== '') {
|
|
165
|
+
if (input.review.summary.trim() !== '') {
|
|
143
166
|
lines.push('### Summary');
|
|
144
|
-
lines.push(input.summary.trim());
|
|
167
|
+
lines.push(input.review.summary.trim());
|
|
145
168
|
lines.push('');
|
|
146
169
|
}
|
|
147
170
|
lines.push('### 🔍 3-Point Hash Points');
|
|
@@ -149,7 +172,7 @@ function renderDashboard(input) {
|
|
|
149
172
|
lines.push(`- Feature HEAD (B): \`${input.featureHead.slice(0, 12)}\``);
|
|
150
173
|
lines.push(`- Main HEAD (C): \`${input.mainHead.slice(0, 12)}\``);
|
|
151
174
|
lines.push('');
|
|
152
|
-
lines.push('<sub>🤖 Generated by `pnpm wp-upsert-pr` (build ran via nx affected — not self-attested).</sub>');
|
|
175
|
+
lines.push('<sub>🤖 Generated by `pnpm wp-finish-upsert-pr` (build ran via nx affected — not self-attested).</sub>');
|
|
153
176
|
return lines.join('\n');
|
|
154
177
|
}
|
|
155
178
|
//# sourceMappingURL=dashboard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { GateDefinition, WEBPIECES_DISABLE, RULE_NAMES } from '@webpieces/rules-config';\n\n// Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') { re += '[^/]*'; i += 1; continue; }\n if (ch === '?') { re += '[^/]'; i += 1; continue; }\n if ('.+^$(){}|[]\\\\'.includes(ch)) { re += '\\\\' + ch; i += 1; continue; }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (globToRegex(pattern).test(file)) return true;\n }\n return false;\n}\n\nexport class GateResult {\n name: string;\n severity: string;\n matchedFiles: string[];\n\n constructor(name: string, severity: string, matchedFiles: string[]) {\n this.name = name;\n this.severity = severity;\n this.matchedFiles = matchedFiles;\n }\n}\n\nexport function computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates.map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean => matchesAny(gate.patterns, file));\n return new GateResult(gate.name, gate.severity, matched);\n });\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\n// Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n// reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\nexport function countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map((key: string): string => (RULE_NAMES as Record<string, string>)[key]);\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n summary: string;\n\n constructor(\n title: string, gateResults: GateResult[], disables: DisableCounts,\n buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, summary: string,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.summary = summary;\n }\n}\n\nfunction gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** 🟢 No`;\n const emoji = result.severity === 'block' ? '🔴' : '🟡';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n}\n\nfunction disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** 🟢 No';\n const which = disables.webpiecesRules.length > 0 ? ` — ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** 🟡 ${disables.webpiecesCount} line(s)${which}`;\n}\n\nexport function renderDashboard(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## 🚦 PR Gate Dashboard');\n lines.push('');\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);\n for (const result of input.gateResults) lines.push(gateLine(result));\n lines.push(disableLine(input.disables));\n const eslintEmoji = input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n lines.push('');\n if (input.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.summary.trim());\n lines.push('');\n }\n lines.push('### 🔍 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push('<sub>🤖 Generated by `pnpm wp-upsert-pr` (build ran via nx affected — not self-attested).</sub>');\n return lines.join('\\n');\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/dashboard/dashboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { GateDefinition, WEBPIECES_DISABLE, RULE_NAMES, ReviewJson } from '@webpieces/rules-config';\n\n// Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.\nfunction globToRegex(pattern: string): RegExp {\n let re = '';\n let i = 0;\n while (i < pattern.length) {\n const ch = pattern[i];\n if (ch === '*' && pattern[i + 1] === '*') {\n re += '.*';\n i += 2;\n if (pattern[i] === '/') i += 1;\n continue;\n }\n if (ch === '*') { re += '[^/]*'; i += 1; continue; }\n if (ch === '?') { re += '[^/]'; i += 1; continue; }\n if ('.+^$(){}|[]\\\\'.includes(ch)) { re += '\\\\' + ch; i += 1; continue; }\n re += ch;\n i += 1;\n }\n return new RegExp('^' + re + '$');\n}\n\nfunction matchesAny(patterns: string[], file: string): boolean {\n for (const pattern of patterns) {\n if (globToRegex(pattern).test(file)) return true;\n }\n return false;\n}\n\nexport class GateResult {\n name: string;\n color: string; // 'yellow' | 'red' — the color shown WHEN files matched (green is implicit)\n matchedFiles: string[];\n\n constructor(name: string, color: string, matchedFiles: string[]) {\n this.name = name;\n this.color = color;\n this.matchedFiles = matchedFiles;\n }\n}\n\n// Disabled gates are in-file examples (JSON has no comments) — skip them entirely.\nexport function computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[] {\n return gates\n .filter((gate: GateDefinition): boolean => !gate.disabled)\n .map((gate: GateDefinition): GateResult => {\n const matched = changedFiles.filter((file: string): boolean => matchesAny(gate.patterns, file));\n return new GateResult(gate.name, gate.color, matched);\n });\n}\n\nexport class DisableCounts {\n webpiecesCount: number;\n eslintCount: number;\n webpiecesRules: string[];\n\n constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]) {\n this.webpiecesCount = webpiecesCount;\n this.eslintCount = eslintCount;\n this.webpiecesRules = webpiecesRules;\n }\n}\n\n// Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:\n// reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.\nexport function countAddedDisables(patch: string): DisableCounts {\n let webpiecesCount = 0;\n let eslintCount = 0;\n const rules = new Set<string>();\n const allRuleTokens = Object.keys(RULE_NAMES).map((key: string): string => (RULE_NAMES as Record<string, string>)[key]);\n\n for (const line of patch.split('\\n')) {\n if (!line.startsWith('+') || line.startsWith('+++')) continue;\n if (line.includes(WEBPIECES_DISABLE)) {\n webpiecesCount += 1;\n for (const token of allRuleTokens) {\n if (line.includes(token)) rules.add(token);\n }\n }\n if (line.includes('eslint-disable')) eslintCount += 1;\n }\n return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());\n}\n\nexport class DashboardInput {\n title: string;\n gateResults: GateResult[];\n disables: DisableCounts;\n buildPassed: boolean;\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n review: ReviewJson; // AI-authored risk/violations/summary (from review.json)\n\n constructor(\n title: string, gateResults: GateResult[], disables: DisableCounts,\n buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, review: ReviewJson,\n ) {\n this.title = title;\n this.gateResults = gateResults;\n this.disables = disables;\n this.buildPassed = buildPassed;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.review = review;\n }\n}\n\nfunction gateLine(result: GateResult): string {\n if (result.matchedFiles.length === 0) return `**${result.name}:** 🟢 No`;\n const emoji = result.color === 'red' ? '🔴' : '🟡';\n return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;\n}\n\n// 10-cell risk bar colored by band (🟩 ≤25, 🟨 ≤50, 🟧 ≤75, 🟥 >75), at least one filled cell —\n// ported from trytami's github_risk_bar (git-display-utils.sh).\nfunction riskBar(score: number): string {\n const clamped = Math.max(0, Math.min(100, score));\n const cell = clamped <= 25 ? '🟩' : clamped <= 50 ? '🟨' : clamped <= 75 ? '🟧' : '🟥';\n const filled = Math.max(1, Math.min(10, Math.round(clamped / 10)));\n return cell.repeat(filled) + '⬜'.repeat(10 - filled);\n}\n\n// RISK section (trytami's AI half): Risk Score bar, Risk Level, Pattern Violations.\nfunction riskLines(review: ReviewJson): string[] {\n const violations = review.violations.length;\n const violationLine = violations === 0 ? '🟢 No' : `🟡 Yes (${violations} violation(s))`;\n return [\n `**Risk Score:** ${riskBar(review.riskScore)} **${review.riskScore}/100** ${review.riskEmoji}`,\n `**Risk Level:** ${review.riskEmoji} **${review.riskLevel}**`,\n `**Pattern Violations:** ${violationLine}`,\n ];\n}\n\nfunction disableLine(disables: DisableCounts): string {\n if (disables.webpiecesCount === 0) return '**Webpieces Disables Added:** 🟢 No';\n const which = disables.webpiecesRules.length > 0 ? ` — ${disables.webpiecesRules.join(', ')}` : '';\n return `**Webpieces Disables Added:** 🟡 ${disables.webpiecesCount} line(s)${which}`;\n}\n\nexport function renderDashboard(input: DashboardInput): string {\n const lines: string[] = [];\n lines.push('## 🚦 PR Gate Dashboard');\n lines.push('');\n for (const line of riskLines(input.review)) lines.push(line);\n lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);\n for (const result of input.gateResults) lines.push(gateLine(result));\n lines.push(disableLine(input.disables));\n const eslintEmoji = input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;\n lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);\n lines.push('');\n if (input.review.summary.trim() !== '') {\n lines.push('### Summary');\n lines.push(input.review.summary.trim());\n lines.push('');\n }\n lines.push('### 🔍 3-Point Hash Points');\n lines.push(`- Fork point (A): \\`${input.forkPoint.slice(0, 12)}\\``);\n lines.push(`- Feature HEAD (B): \\`${input.featureHead.slice(0, 12)}\\``);\n lines.push(`- Main HEAD (C): \\`${input.mainHead.slice(0, 12)}\\``);\n lines.push('');\n lines.push('<sub>🤖 Generated by `pnpm wp-finish-upsert-pr` (build ran via nx affected — not self-attested).</sub>');\n return lines.join('\\n');\n}\n"]}
|
package/src/index.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
//
|
|
4
4
|
// Now published to npm via the release workflow (OIDC trusted publishing).
|
|
5
5
|
// Public surface is intentionally small for now. The package mainly ships bin
|
|
6
|
-
// commands (wp-git-update, wp-git-gather, wp-
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// commands (wp-git-update, wp-git-gather, wp-finish-upsert-pr, wp-start-upsert-pr,
|
|
7
|
+
// wp-build-affected). Shared library exports (dashboard gate computation, etc.) back
|
|
8
|
+
// the dashboard those commands render.
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.renderDashboard = exports.countAddedDisables = exports.computeGateResults = exports.DashboardInput = exports.DisableCounts = exports.GateResult = void 0;
|
|
11
11
|
var dashboard_1 = require("./dashboard/dashboard");
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/pr-gate/src/index.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/pr-gate/src/index.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,mFAAmF;AACnF,qFAAqF;AACrF,uCAAuC;;;AAEvC,mDAO+B;AAN3B,uGAAA,UAAU,OAAA;AACV,0GAAA,aAAa,OAAA;AACb,2GAAA,cAAc,OAAA;AACd,+GAAA,kBAAkB,OAAA;AAClB,+GAAA,kBAAkB,OAAA;AAClB,4GAAA,eAAe,OAAA","sourcesContent":["// @webpieces/pr-gate — gated PR system.\n//\n// Now published to npm via the release workflow (OIDC trusted publishing).\n// Public surface is intentionally small for now. The package mainly ships bin\n// commands (wp-git-update, wp-git-gather, wp-finish-upsert-pr, wp-start-upsert-pr,\n// wp-build-affected). Shared library exports (dashboard gate computation, etc.) back\n// the dashboard those commands render.\n\nexport {\n GateResult,\n DisableCounts,\n DashboardInput,\n computeGateResults,\n countAddedDisables,\n renderDashboard,\n} from './dashboard/dashboard';\n"]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.main = main;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
+
const path = tslib_1.__importStar(require("path"));
|
|
9
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
10
|
+
const git_readAiBranchName_1 = require("./workflow/git-readAiBranchName");
|
|
11
|
+
const git_exec_1 = require("./workflow/git-exec");
|
|
12
|
+
const build_affected_1 = require("./workflow/build-affected");
|
|
13
|
+
const merge_state_1 = require("./workflow/merge-state");
|
|
14
|
+
const dashboard_1 = require("../dashboard/dashboard");
|
|
15
|
+
// FINISH of the AI-first PR flow. Runs after the AI has written review.json (see wp-start-upsert-pr).
|
|
16
|
+
// Responsibilities, in order: (1) if a 3-point merge was in progress, validate the AI's conflict
|
|
17
|
+
// resolution and commit it; (2) REQUIRE review.json (hard-fail with the schema if absent/invalid);
|
|
18
|
+
// (3) run the authoritative build gate; (4) render the dashboard (shell facts + AI risk/violations);
|
|
19
|
+
// (5) create/update the PR via `gh`. This is the ONLY command that posts PRs.
|
|
20
|
+
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
21
|
+
function gitOut(args) {
|
|
22
|
+
const result = (0, child_process_1.spawnSync)('git', args, { encoding: 'utf8' });
|
|
23
|
+
return result.status === 0 ? (result.stdout ?? '').trim() : '';
|
|
24
|
+
}
|
|
25
|
+
// Validate the AI's resolution of the conflicted files — the part of the process the AI owns
|
|
26
|
+
// (branch creation/finalization is the script's job, so it is not re-checked here). Exits the
|
|
27
|
+
// process with a fix instruction on any failure; returns only when all three checks pass.
|
|
28
|
+
function validateResolution(repoRoot, mergeDir, conflictedFiles) {
|
|
29
|
+
// 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).
|
|
30
|
+
const scan = (0, merge_state_1.scanConflictMarkers)(repoRoot, conflictedFiles);
|
|
31
|
+
if (!scan.clean) {
|
|
32
|
+
process.stderr.write('❌ Unresolved conflict markers (<<<<<<< / ======= / >>>>>>>) remain in:\n');
|
|
33
|
+
for (const file of scan.filesWithMarkers)
|
|
34
|
+
process.stderr.write(` - ${file}\n`);
|
|
35
|
+
process.stderr.write('\nResolve them, then re-run: pnpm wp-finish-upsert-pr\n');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
// 2. Ensure git itself has no remaining unmerged entries.
|
|
39
|
+
const unmerged = (0, child_process_1.execSync)('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();
|
|
40
|
+
if (unmerged !== '') {
|
|
41
|
+
process.stderr.write('❌ Git still reports unmerged files:\n' + unmerged + '\n');
|
|
42
|
+
process.stderr.write('\nResolve and `git add` them, then re-run: pnpm wp-finish-upsert-pr\n');
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
process.stdout.write('✅ No conflict markers in resolved files.\n');
|
|
46
|
+
// 3. Explanation check — every conflicted file must have a non-empty merge-explanation.md in
|
|
47
|
+
// its per-file context dir, proving the AI deliberately 3-point merged it (and recording how)
|
|
48
|
+
// rather than blindly taking one side. A sidecar file works for any type, incl. JSON/deletes.
|
|
49
|
+
const explanations = (0, merge_state_1.scanMergeExplanations)(mergeDir, conflictedFiles);
|
|
50
|
+
if (!explanations.clean) {
|
|
51
|
+
process.stderr.write(`❌ Missing/empty merge explanation (${rules_config_1.MERGE_EXPLANATION_FILE}) for:\n`);
|
|
52
|
+
for (const file of explanations.filesWithMarkers) {
|
|
53
|
+
process.stderr.write(` - ${file}\n → ${path.join((0, merge_state_1.perFileContextDir)(mergeDir, file), rules_config_1.MERGE_EXPLANATION_FILE)}\n`);
|
|
54
|
+
}
|
|
55
|
+
process.stderr.write('\nWrite a few sentences on how you resolved each (which side, what you combined, why),\n' +
|
|
56
|
+
'then re-run: pnpm wp-finish-upsert-pr\n');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
process.stdout.write('✅ Merge explanations present for all resolved files.\n');
|
|
60
|
+
}
|
|
61
|
+
// If a 3-point merge was in progress, validate + commit the AI's resolution. No marker => no merge
|
|
62
|
+
// happened (the common case) => nothing to do here. Already-validated => previously committed.
|
|
63
|
+
function completeMergeIfInProgress(repoRoot, mergeDir) {
|
|
64
|
+
const marker = (0, merge_state_1.readMergeMarker)(mergeDir);
|
|
65
|
+
if (!marker || marker.validated)
|
|
66
|
+
return;
|
|
67
|
+
process.stdout.write('\n' + SEP + '🔎 Validating Merge Resolution\n' + SEP + '\n');
|
|
68
|
+
validateResolution(repoRoot, mergeDir, marker.conflictedFiles);
|
|
69
|
+
(0, git_exec_1.runGitChecked)(['add', '-A'], 'Failed to stage resolved files');
|
|
70
|
+
const nothingStaged = (0, child_process_1.spawnSync)('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;
|
|
71
|
+
if (!nothingStaged) {
|
|
72
|
+
(0, git_exec_1.runGitChecked)(['commit', '-m', `Squash merge of ${marker.currentBranch} (conflicts resolved)`], 'Failed to commit resolved merge');
|
|
73
|
+
}
|
|
74
|
+
marker.validated = true;
|
|
75
|
+
(0, merge_state_1.writeMergeMarker)(mergeDir, marker);
|
|
76
|
+
fs.writeFileSync(path.join(mergeDir, 'conflicts-resolved'), '');
|
|
77
|
+
process.stdout.write('\n✅ Merge validated and committed.\n');
|
|
78
|
+
}
|
|
79
|
+
function runBuildGate(repoRoot) {
|
|
80
|
+
const buildCommand = (0, build_affected_1.resolveBuildCommand)(repoRoot);
|
|
81
|
+
process.stdout.write('\n' + SEP + '🛠️ Build gate (authoritative)\n' + SEP + '\n');
|
|
82
|
+
process.stdout.write(`Running the build gate. To get it passing, run the SAME command yourself and fix everything it reports:\n\n` +
|
|
83
|
+
` ${buildCommand}\n\n`);
|
|
84
|
+
const buildCode = (0, build_affected_1.runConfiguredBuildGate)(repoRoot);
|
|
85
|
+
if (buildCode !== 0) {
|
|
86
|
+
process.stderr.write(`\n❌ Build failed — no PR created/updated.\n\n` +
|
|
87
|
+
`Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-finish-upsert-pr:\n\n` +
|
|
88
|
+
` ${buildCommand}\n\n`);
|
|
89
|
+
process.exit(buildCode);
|
|
90
|
+
}
|
|
91
|
+
process.stdout.write('\n✅ Build passed.\n');
|
|
92
|
+
}
|
|
93
|
+
function ensurePushed(currentBranch) {
|
|
94
|
+
const remoteExists = (0, child_process_1.spawnSync)('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;
|
|
95
|
+
if (remoteExists) {
|
|
96
|
+
(0, git_exec_1.runGitChecked)(['push', '--force-with-lease', 'origin', `HEAD:${currentBranch}`], 'Failed to push branch');
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
(0, git_exec_1.runGitChecked)(['push', '-u', 'origin', `HEAD:${currentBranch}`], 'Failed to push new branch');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function buildDashboard(repoRoot, buildPassed, review) {
|
|
103
|
+
const config = (0, rules_config_1.loadAndValidate)(repoRoot).prGate;
|
|
104
|
+
const forkPoint = gitOut(['merge-base', 'origin/main', 'HEAD']);
|
|
105
|
+
const featureHead = gitOut(['rev-parse', 'HEAD']);
|
|
106
|
+
const mainHead = gitOut(['rev-parse', 'origin/main']);
|
|
107
|
+
const range = `${forkPoint}..${featureHead}`;
|
|
108
|
+
const changedFiles = gitOut(['diff', range, '--name-only']).split('\n').filter((f) => f.trim() !== '');
|
|
109
|
+
const patch = gitOut(['diff', range]);
|
|
110
|
+
const title = gitOut(['log', '-1', '--format=%s']);
|
|
111
|
+
const gateResults = (0, dashboard_1.computeGateResults)(config.gates, changedFiles);
|
|
112
|
+
const disables = (0, dashboard_1.countAddedDisables)(patch);
|
|
113
|
+
const input = new dashboard_1.DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, review);
|
|
114
|
+
return (0, dashboard_1.renderDashboard)(input);
|
|
115
|
+
}
|
|
116
|
+
function upsertPr(repoRoot, currentBranch, body) {
|
|
117
|
+
const prDir = path.join(repoRoot, rules_config_1.WEBPIECES_TMP_DIR, `pr-${(0, git_readAiBranchName_1.getFeatureName)()}`);
|
|
118
|
+
fs.mkdirSync(prDir, { recursive: true });
|
|
119
|
+
const bodyFile = path.join(prDir, 'pr-body.md');
|
|
120
|
+
fs.writeFileSync(bodyFile, body + '\n');
|
|
121
|
+
const existing = gitOut(['log', '-1', '--format=%s']); // title fallback
|
|
122
|
+
const prNumber = (0, child_process_1.spawnSync)('gh', ['pr', 'list', '--head', currentBranch, '--json', 'number', '--jq', '.[0].number'], { encoding: 'utf8' });
|
|
123
|
+
const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';
|
|
124
|
+
if (num === '') {
|
|
125
|
+
process.stdout.write('Creating PR...\n');
|
|
126
|
+
const create = (0, child_process_1.spawnSync)('gh', ['pr', 'create', '--head', currentBranch, '--base', 'main', '--title', existing, '--body-file', bodyFile], { stdio: 'inherit' });
|
|
127
|
+
if (create.status !== 0) {
|
|
128
|
+
process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\n ' + bodyFile + '\n');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
process.stdout.write(`Updating PR #${num}...\n`);
|
|
134
|
+
(0, child_process_1.spawnSync)('gh', ['pr', 'edit', num, '--body-file', bodyFile], { stdio: 'inherit' });
|
|
135
|
+
}
|
|
136
|
+
(0, child_process_1.spawnSync)('gh', ['pr', 'merge', currentBranch, '--auto', '--squash'], { stdio: 'inherit' });
|
|
137
|
+
}
|
|
138
|
+
async function main() {
|
|
139
|
+
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
140
|
+
const mergeDir = (0, merge_state_1.mergeDirFor)(repoRoot, (0, git_readAiBranchName_1.getFeatureName)());
|
|
141
|
+
// 1. Finish any in-progress conflict resolution (no-op when there was no merge).
|
|
142
|
+
completeMergeIfInProgress(repoRoot, mergeDir);
|
|
143
|
+
// 2. REQUIRE the AI-authored review.json (throws InformAiError with the schema if missing/invalid).
|
|
144
|
+
const review = (0, rules_config_1.loadReviewJson)((0, rules_config_1.reviewJsonPath)(repoRoot, (0, git_readAiBranchName_1.getFeatureName)()));
|
|
145
|
+
// 3. Authoritative build gate, then push, then post.
|
|
146
|
+
runBuildGate(repoRoot);
|
|
147
|
+
const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
148
|
+
ensurePushed(currentBranch);
|
|
149
|
+
process.stdout.write('\n' + SEP + '📋 Dashboard + PR\n' + SEP + '\n');
|
|
150
|
+
const body = buildDashboard(repoRoot, true, review);
|
|
151
|
+
upsertPr(repoRoot, currentBranch, body);
|
|
152
|
+
process.stdout.write('\n✅ Done.\n');
|
|
153
|
+
}
|
|
154
|
+
if (require.main === module) {
|
|
155
|
+
main().catch((err) => {
|
|
156
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
157
|
+
process.stderr.write(message + '\n');
|
|
158
|
+
process.exit(1);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=git-finishUpsertPr.js.map
|
|
@@ -0,0 +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 WEBPIECES_TMP_DIR,\n MERGE_EXPLANATION_FILE,\n loadReviewJson,\n reviewJsonPath,\n ReviewJson,\n} from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { runGitChecked } from './workflow/git-exec';\nimport { runConfiguredBuildGate, resolveBuildCommand } from './workflow/build-affected';\nimport {\n mergeDirFor,\n perFileContextDir,\n readMergeMarker,\n writeMergeMarker,\n scanConflictMarkers,\n scanMergeExplanations,\n} from './workflow/merge-state';\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 the AI's conflict\n// resolution and commit it; (2) REQUIRE review.json (hard-fail with the schema if absent/invalid);\n// (3) run the authoritative build gate; (4) render the dashboard (shell facts + AI risk/violations);\n// (5) create/update the PR via `gh`. This is the 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\n// Validate the AI's resolution of the conflicted files — the part of the process the AI owns\n// (branch creation/finalization is the script's job, so it is not re-checked here). Exits the\n// process with a fix instruction on any failure; returns only when all three checks pass.\nfunction validateResolution(repoRoot: string, mergeDir: string, conflictedFiles: string[]): void {\n // 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).\n const scan = scanConflictMarkers(repoRoot, conflictedFiles);\n if (!scan.clean) {\n process.stderr.write('❌ Unresolved conflict markers (<<<<<<< / ======= / >>>>>>>) remain in:\\n');\n for (const file of scan.filesWithMarkers) process.stderr.write(` - ${file}\\n`);\n process.stderr.write('\\nResolve them, then re-run: pnpm wp-finish-upsert-pr\\n');\n process.exit(1);\n }\n\n // 2. Ensure git itself has no remaining unmerged entries.\n const unmerged = execSync('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();\n if (unmerged !== '') {\n process.stderr.write('❌ Git still reports unmerged files:\\n' + unmerged + '\\n');\n process.stderr.write('\\nResolve and `git add` them, then re-run: pnpm wp-finish-upsert-pr\\n');\n process.exit(1);\n }\n process.stdout.write('✅ No conflict markers in resolved files.\\n');\n\n // 3. Explanation check — every conflicted file must have a non-empty merge-explanation.md in\n // its per-file context dir, proving the AI deliberately 3-point merged it (and recording how)\n // rather than blindly taking one side. A sidecar file works for any type, incl. JSON/deletes.\n const explanations = scanMergeExplanations(mergeDir, conflictedFiles);\n if (!explanations.clean) {\n process.stderr.write(`❌ Missing/empty merge explanation (${MERGE_EXPLANATION_FILE}) for:\\n`);\n for (const file of explanations.filesWithMarkers) {\n process.stderr.write(` - ${file}\\n → ${path.join(perFileContextDir(mergeDir, file), MERGE_EXPLANATION_FILE)}\\n`);\n }\n process.stderr.write(\n '\\nWrite a few sentences on how you resolved each (which side, what you combined, why),\\n' +\n 'then re-run: pnpm wp-finish-upsert-pr\\n',\n );\n process.exit(1);\n }\n process.stdout.write('✅ Merge explanations present for all resolved files.\\n');\n}\n\n// If a 3-point merge was in progress, validate + commit the AI's resolution. No marker => no merge\n// happened (the common case) => nothing to do here. Already-validated => previously committed.\nfunction completeMergeIfInProgress(repoRoot: string, mergeDir: string): void {\n const marker = readMergeMarker(mergeDir);\n if (!marker || marker.validated) return;\n\n process.stdout.write('\\n' + SEP + '🔎 Validating Merge Resolution\\n' + SEP + '\\n');\n validateResolution(repoRoot, mergeDir, marker.conflictedFiles);\n runGitChecked(['add', '-A'], 'Failed to stage resolved files');\n\n const nothingStaged = spawnSync('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;\n if (!nothingStaged) {\n runGitChecked(\n ['commit', '-m', `Squash merge of ${marker.currentBranch} (conflicts resolved)`],\n 'Failed to commit resolved merge',\n );\n }\n\n marker.validated = true;\n writeMergeMarker(mergeDir, marker);\n fs.writeFileSync(path.join(mergeDir, 'conflicts-resolved'), '');\n process.stdout.write('\\n✅ Merge validated and committed.\\n');\n}\n\nfunction runBuildGate(repoRoot: string): void {\n const buildCommand = resolveBuildCommand(repoRoot);\n process.stdout.write('\\n' + SEP + '🛠️ Build gate (authoritative)\\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 process.stderr.write(\n `\\n❌ Build failed — no PR created/updated.\\n\\n` +\n `Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-finish-upsert-pr:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n process.exit(buildCode);\n }\n process.stdout.write('\\n✅ Build passed.\\n');\n}\n\nfunction 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\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\nfunction upsertPr(repoRoot: string, currentBranch: string, body: string): void {\n const prDir = path.join(repoRoot, WEBPIECES_TMP_DIR, `pr-${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', currentBranch, '--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', currentBranch, '--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', currentBranch, '--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 const mergeDir = mergeDirFor(repoRoot, getFeatureName());\n\n // 1. Finish any in-progress conflict resolution (no-op when there was no merge).\n completeMergeIfInProgress(repoRoot, mergeDir);\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 // 3. Authoritative build gate, then push, then post.\n runBuildGate(repoRoot);\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n ensurePushed(currentBranch);\n\n process.stdout.write('\\n' + SEP + '📋 Dashboard + PR\\n' + SEP + '\\n');\n const body = buildDashboard(repoRoot, true, review);\n upsertPr(repoRoot, currentBranch, 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"]}
|
|
@@ -60,9 +60,9 @@ You are on branch \`{{SQUASH_BRANCH}}\` with conflict markers in the working tre
|
|
|
60
60
|
## How the gate works
|
|
61
61
|
|
|
62
62
|
- Resolve every conflicted file in the working tree.
|
|
63
|
-
- Run **\`pnpm wp-
|
|
63
|
+
- Run **\`pnpm wp-finish-upsert-pr\`** — the validation + finish gate. It scans for leftover conflict
|
|
64
64
|
markers, checks each conflicted file has a written merge explanation, runs the \`nx affected\`
|
|
65
|
-
build, and — only if all pass — stages, commits, and
|
|
65
|
+
build, and — only if all pass — stages, commits, then renders the dashboard and creates/updates the PR.
|
|
66
66
|
- **Do NOT run \`git add\` / \`git commit\` / \`git push\` / \`gh pr\` yourself.** They are blocked by
|
|
67
67
|
the \`merge-in-progress-guard\` hook until the gate validates. The gate does the commit.
|
|
68
68
|
|
|
@@ -104,24 +104,17 @@ this file: which side you took where, what you combined from B-A.diff vs C-A.dif
|
|
|
104
104
|
gate fails if any conflicted file's explanation is missing or empty. Do not paste A/B/C context
|
|
105
105
|
blocks into the source code.
|
|
106
106
|
|
|
107
|
-
## STEP 3 — Run the gate
|
|
107
|
+
## STEP 3 — Run the gate (validates the merge AND finishes the PR)
|
|
108
108
|
|
|
109
109
|
\`\`\`
|
|
110
|
-
pnpm wp-
|
|
110
|
+
pnpm wp-finish-upsert-pr
|
|
111
111
|
\`\`\`
|
|
112
112
|
|
|
113
113
|
- Leftover conflict markers → fix those files and re-run.
|
|
114
114
|
- Missing merge explanation → write it (see STEP 2) and re-run.
|
|
115
115
|
- Build failure → fix the TypeScript/lint errors and re-run (the gate re-stages for you).
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
## STEP 4 — Finalize
|
|
119
|
-
|
|
120
|
-
\`\`\`
|
|
121
|
-
pnpm wp-upsert-pr # (or pnpm wp-git-update)
|
|
122
|
-
\`\`\`
|
|
123
|
-
|
|
124
|
-
Force-pushes the resolved squash branch over your feature branch and updates/creates the PR.
|
|
116
|
+
- Missing review.json → write it in the printed format (your PR review), then re-run.
|
|
117
|
+
- On success it commits, renders the dashboard, and creates/updates the PR.
|
|
125
118
|
|
|
126
119
|
## Conflicted files
|
|
127
120
|
|
|
@@ -194,7 +187,7 @@ async function handleResume(mergeDir, marker) {
|
|
|
194
187
|
if (!marker.validated) {
|
|
195
188
|
process.stdout.write('\n' + SEP + '⏸️ Merge in progress — not yet validated\n' + SEP + '\n');
|
|
196
189
|
process.stdout.write('Resolve the remaining conflicts in the working tree, then run:\n');
|
|
197
|
-
process.stdout.write(' pnpm wp-
|
|
190
|
+
process.stdout.write(' pnpm wp-finish-upsert-pr\n\n');
|
|
198
191
|
process.exit(1);
|
|
199
192
|
}
|
|
200
193
|
process.stdout.write('Resuming: merge validated — finalizing.\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-updateFromMain.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-updateFromMain.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 { WEBPIECES_TMP_DIR, MERGE_EXPLANATION_FILE } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { main as gatherInfo } from './git-gatherInfo';\nimport { main as cleanTmp } from './workflow/cleanTmp';\nimport { runGitChecked } from './workflow/git-exec';\nimport {\n MergeMarker,\n mergeDirFor,\n perFileContextDir,\n readMergeMarker,\n writeMergeMarker,\n clearMergeMarker,\n} from './workflow/merge-state';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\ninterface HashPoints {\n hashForkPoint: string;\n hashFeatureHead: string;\n hashMainHead: string;\n}\n\nfunction detectPr(currentBranch: string): string {\n const result = spawnSync(\n 'gh', ['pr', 'list', '--head', currentBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n}\n\nfunction createBackup(currentBranch: string): string {\n process.stdout.write('\\n' + SEP + '💾 Creating Incremental Backup\\n' + SEP + '\\n');\n let n = 1;\n while (spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${currentBranch}Backup${n}`]).status === 0) {\n n += 1;\n }\n const backupBranch = `${currentBranch}Backup${n}`;\n runGitChecked(['checkout', '-b', backupBranch], 'Failed to create backup branch');\n runGitChecked(['checkout', currentBranch], 'Failed to return to feature branch');\n process.stdout.write(`✅ Backup created: ${backupBranch}\\n\\n`);\n return backupBranch;\n}\n\nfunction saveConflictContext(\n conflictedFiles: string[], mergeDir: string, forkPoint: string, featureHead: string, mainHead: string,\n): void {\n for (const file of conflictedFiles) {\n const fileDir = perFileContextDir(mergeDir, file);\n fs.mkdirSync(fileDir, { recursive: true });\n\n const fork = spawnSync('git', ['show', `${forkPoint}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'A-forkpoint.txt'), fork.status === 0 ? (fork.stdout ?? '') : '(file did not exist)\\n');\n const feature = spawnSync('git', ['show', `${featureHead}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'B-feature.txt'), feature.status === 0 ? (feature.stdout ?? '') : '(file did not exist)\\n');\n const main = spawnSync('git', ['show', `${mainHead}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'C-main.txt'), main.status === 0 ? (main.stdout ?? '') : '(file did not exist)\\n');\n\n const ba = spawnSync('git', ['diff', forkPoint, featureHead, '--', file], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'B-A.diff'), ba.stdout ?? '');\n const ca = spawnSync('git', ['diff', forkPoint, mainHead, '--', file], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'C-A.diff'), ca.stdout ?? '');\n }\n}\n\n// Single source of truth for the merge process. The script WRITES it at conflict time (rather\n// than the AI relying on a separate hand-maintained doc), parameterized with the live MERGE_DIR\n// and conflicted-file list, so the instructions can never drift from the actual layout. The body\n// is a template constant with {{...}} placeholders so this stays a small filler function.\nconst MERGE_PROCESS_TEMPLATE = `# AI-Assisted Squash-Merge Conflict Resolution (generated)\n\nThis file was generated by \\`pnpm wp-git-update\\` when the 3-point squash-merge hit conflicts.\nIt is the single source of truth for the merge process — follow it exactly.\n\nYou are on branch \\`{{SQUASH_BRANCH}}\\` with conflict markers in the working tree.\n\\`MERGE_DIR = {{MERGE_DIR}}\\`\n\n## How the gate works\n\n- Resolve every conflicted file in the working tree.\n- Run **\\`pnpm wp-git-merge-complete\\`** — the validation gate. It scans for leftover conflict\n markers, checks each conflicted file has a written merge explanation, runs the \\`nx affected\\`\n build, and — only if all pass — stages, commits, and unblocks the workflow.\n- **Do NOT run \\`git add\\` / \\`git commit\\` / \\`git push\\` / \\`gh pr\\` yourself.** They are blocked by\n the \\`merge-in-progress-guard\\` hook until the gate validates. The gate does the commit.\n\n## STEP 1 — Load the merge context\n\nPer conflicted file, \\`MERGE_DIR/updatemain-<safe_path>/\\` holds (\\`<safe_path>\\` = path with \\`/\\`→\\`__\\`):\n\n\\`\\`\\`\nA-forkpoint.txt # file at fork point (base)\nB-feature.txt # file on your feature branch\nC-main.txt # file on main\nB-A.diff # what your feature changed (B−A)\nC-A.diff # what main changed (C−A)\n\\`\\`\\`\n\n\\`updatemain-hashes.json\\` holds A/B/C commit hashes. To see why main changed:\n\\`git log <A>..<C> --oneline\\`.\n\n## STEP 2 — Resolve each conflicted file\n\nFor each file: read the working-tree file (the markers) and its \\`B-A.diff\\` / \\`C-A.diff\\` (intent),\nthen Edit to the resolved version, removing ALL conflict markers.\n\nStrategies: goals align & non-overlapping → merge both · one side removes what the other modifies\n→ prefer the removal · same lines, simple (imports/format) → merge both · same lines, complex or\nconflicting goals → ask the user · feature re-implements what main already squashed → prefer\nmain's, then re-apply only the genuinely new feature work.\n\n**Then write a merge explanation** for each conflicted file — NOT a comment in the source (that\nbreaks for JSON and deleted files). Write it next to that file's diffs, at:\n\n\\`\\`\\`\nMERGE_DIR/updatemain-<safe_path>/{{EXPLANATION_FILE}}\n\\`\\`\\`\n\n(\\`<safe_path>\\` = the conflict file path with \\`/\\` → \\`__\\`, the same dir that holds its\n\\`A-forkpoint.txt\\` / \\`B-A.diff\\` / \\`C-A.diff\\`.) In it, explain in a few sentences how you resolved\nthis file: which side you took where, what you combined from B-A.diff vs C-A.diff, and why. The\ngate fails if any conflicted file's explanation is missing or empty. Do not paste A/B/C context\nblocks into the source code.\n\n## STEP 3 — Run the gate\n\n\\`\\`\\`\npnpm wp-git-merge-complete\n\\`\\`\\`\n\n- Leftover conflict markers → fix those files and re-run.\n- Missing merge explanation → write it (see STEP 2) and re-run.\n- Build failure → fix the TypeScript/lint errors and re-run (the gate re-stages for you).\n- On success it commits and prints the finalize instruction.\n\n## STEP 4 — Finalize\n\n\\`\\`\\`\npnpm wp-upsert-pr # (or pnpm wp-git-update)\n\\`\\`\\`\n\nForce-pushes the resolved squash branch over your feature branch and updates/creates the PR.\n\n## Conflicted files\n\n{{FILE_LIST}}\n\n## If you need to bail out\n\nA backup branch was created (e.g. \\`<feature>Backup1\\`). To abandon:\n\n\\`\\`\\`\ngit merge --abort 2>/dev/null; git checkout <feature> ; git branch -D {{SQUASH_BRANCH}}\n\\`\\`\\`\n\nThen delete \\`{{MERGE_DIR}}/\\` for a clean slate.\n`;\n\nfunction mergeProcessDoc(mergeDir: string, squashBranch: string, conflictedFiles: string[]): string {\n const fileList = conflictedFiles.map((f: string): string => `- \\`${f}\\``).join('\\n');\n return MERGE_PROCESS_TEMPLATE\n .replace(/\\{\\{SQUASH_BRANCH\\}\\}/g, squashBranch)\n .replace(/\\{\\{MERGE_DIR\\}\\}/g, mergeDir)\n .replace(/\\{\\{EXPLANATION_FILE\\}\\}/g, MERGE_EXPLANATION_FILE)\n .replace(/\\{\\{FILE_LIST\\}\\}/g, fileList);\n}\n\n// Returns the absolute path of the written doc.\nfunction writeMergeProcessDoc(repoRoot: string, mergeDir: string, squashBranch: string, conflictedFiles: string[]): string {\n const docDir = path.join(repoRoot, WEBPIECES_TMP_DIR, 'instruct-ai');\n fs.mkdirSync(docDir, { recursive: true });\n const docPath = path.join(docDir, 'webpieces.mergeprocess.md');\n fs.writeFileSync(docPath, mergeProcessDoc(mergeDir, squashBranch, conflictedFiles));\n return docPath;\n}\n\nfunction printConflictHandback(docPath: string, mergeDir: string, squashBranch: string, conflictedFiles: string[]): void {\n process.stdout.write('\\n' + SEP + `⚠️ Conflicts in ${conflictedFiles.length} file(s) — handing control back to you\\n` + SEP + '\\n');\n process.stdout.write(`You are on branch ${squashBranch} with conflicts in the working tree.\\n\\n`);\n process.stdout.write(`FOLLOW THE MERGE PROCESS: ${docPath}\\n`);\n process.stdout.write(`3-point context per file in: ${mergeDir}/updatemain-<file>/\\n\\n`);\n process.stdout.write('Conflicted files:\\n');\n for (const file of conflictedFiles) process.stdout.write(` - ${file}\\n`);\n process.stdout.write('\\n' + SEP);\n}\n\nfunction handleConflictsHandback(\n repoRoot: string, mergeDir: string, currentBranch: string, squashBranch: string,\n backupBranch: string, prNumber: string, hashes: HashPoints,\n): void {\n const raw = execSync('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();\n const conflictedFiles = raw.split('\\n').filter((f: string): boolean => f.trim() !== '');\n fs.writeFileSync(path.join(mergeDir, 'updatemain-conflicted-files.txt'), raw + '\\n');\n saveConflictContext(conflictedFiles, mergeDir, hashes.hashForkPoint, hashes.hashFeatureHead, hashes.hashMainHead);\n\n const marker = new MergeMarker(\n currentBranch, squashBranch, backupBranch, prNumber, conflictedFiles,\n hashes.hashForkPoint, hashes.hashFeatureHead, hashes.hashMainHead, false,\n );\n writeMergeMarker(mergeDir, marker);\n const docPath = writeMergeProcessDoc(repoRoot, mergeDir, squashBranch, conflictedFiles);\n printConflictHandback(docPath, mergeDir, squashBranch, conflictedFiles);\n process.exit(2);\n}\n\nfunction finalizeBranch(\n currentBranch: string, squashBranch: string, backupBranch: string, prNumber: string,\n): void {\n process.stdout.write('\\n' + SEP + '🗑️ Finalizing\\n' + SEP + '\\n');\n runGitChecked(['branch', '-D', currentBranch], 'Failed to delete old feature branch');\n\n const remoteExists = spawnSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;\n if (remoteExists) {\n process.stdout.write(prNumber ? `Updating PR #${prNumber} (force-with-lease)...\\n` : 'Updating remote branch (force-with-lease)...\\n');\n runGitChecked(['push', '-u', '--force-with-lease', 'origin', `${squashBranch}:${currentBranch}`], 'Failed to push to origin');\n } else {\n process.stdout.write('No remote branch — local only.\\n');\n }\n runGitChecked(['checkout', squashBranch], 'Failed to checkout squash branch');\n runGitChecked(['branch', '-m', currentBranch], 'Failed to rename squash branch');\n\n process.stdout.write(`\\n✅ Branch ${currentBranch} updated from main. Backup: ${backupBranch}\\n`);\n process.stdout.write(` Delete backup when safe: git branch -D ${backupBranch}\\n\\n`);\n}\n\nasync function handleResume(mergeDir: string, marker: MergeMarker): Promise<void> {\n if (!marker.validated) {\n process.stdout.write('\\n' + SEP + '⏸️ Merge in progress — not yet validated\\n' + SEP + '\\n');\n process.stdout.write('Resolve the remaining conflicts in the working tree, then run:\\n');\n process.stdout.write(' pnpm wp-git-merge-complete\\n\\n');\n process.exit(1);\n }\n process.stdout.write('Resuming: merge validated — finalizing.\\n');\n finalizeBranch(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber);\n clearMergeMarker(mergeDir);\n await cleanTmp();\n}\n\nasync function runFreshUpdate(repoRoot: string, mergeDir: string): Promise<void> {\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n if (currentBranch.endsWith('Squash')) {\n process.stderr.write(`❌ On a leftover ${currentBranch} branch with no merge marker. Clean up: git branch -D ${currentBranch}\\n`);\n process.exit(1);\n }\n\n process.stdout.write('\\n' + SEP + '🔄 Squash-Merge Update from Main\\n' + SEP + '\\n');\n await gatherInfo();\n const hashes = JSON.parse(fs.readFileSync(path.join(mergeDir, 'updatemain-hashes.json'), 'utf8')) as HashPoints;\n\n const prNumber = detectPr(currentBranch);\n process.stdout.write(prNumber ? `Existing PR #${prNumber} will be updated.\\n` : 'No existing PR (one can be created later).\\n');\n\n const backupBranch = createBackup(currentBranch);\n const squashBranch = `${currentBranch}Squash`;\n if (spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${squashBranch}`]).status === 0) {\n process.stderr.write(`❌ Stale ${squashBranch} from a previous run. Delete it: git branch -D ${squashBranch}\\n`);\n process.exit(1);\n }\n\n runGitChecked(['checkout', 'main'], 'Failed to checkout main');\n runGitChecked(['pull', 'origin', 'main'], 'Failed to pull origin/main');\n runGitChecked(['checkout', '-b', squashBranch], 'Failed to create squash branch');\n\n process.stdout.write('\\n' + SEP + `🔀 Squash merging ${currentBranch}\\n` + SEP + '\\n');\n const merge = spawnSync('git', ['merge', '--squash', currentBranch], { stdio: 'inherit' });\n if (merge.status !== 0) {\n handleConflictsHandback(repoRoot, mergeDir, currentBranch, squashBranch, backupBranch, prNumber, hashes);\n return;\n }\n\n const nothingStaged = spawnSync('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;\n if (nothingStaged) {\n process.stdout.write('ℹ️ Already up-to-date with main (nothing to merge).\\n');\n } else {\n runGitChecked(['commit', '-m', `Squash merge of ${currentBranch}`], 'Failed to commit squash merge');\n }\n finalizeBranch(currentBranch, squashBranch, backupBranch, prNumber);\n await cleanTmp();\n}\n\nexport async function main(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const featureName = getFeatureName();\n const mergeDir = mergeDirFor(repoRoot, featureName);\n fs.mkdirSync(mergeDir, { recursive: true });\n\n const existing = readMergeMarker(mergeDir);\n if (existing) {\n await handleResume(mergeDir, existing);\n return;\n }\n await runFreshUpdate(repoRoot, mergeDir);\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-updateFromMain.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-updateFromMain.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 { WEBPIECES_TMP_DIR, MERGE_EXPLANATION_FILE } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { main as gatherInfo } from './git-gatherInfo';\nimport { main as cleanTmp } from './workflow/cleanTmp';\nimport { runGitChecked } from './workflow/git-exec';\nimport {\n MergeMarker,\n mergeDirFor,\n perFileContextDir,\n readMergeMarker,\n writeMergeMarker,\n clearMergeMarker,\n} from './workflow/merge-state';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\ninterface HashPoints {\n hashForkPoint: string;\n hashFeatureHead: string;\n hashMainHead: string;\n}\n\nfunction detectPr(currentBranch: string): string {\n const result = spawnSync(\n 'gh', ['pr', 'list', '--head', currentBranch, '--json', 'number', '--jq', '.[0].number'],\n { encoding: 'utf8' },\n );\n return result.status === 0 ? (result.stdout ?? '').trim() : '';\n}\n\nfunction createBackup(currentBranch: string): string {\n process.stdout.write('\\n' + SEP + '💾 Creating Incremental Backup\\n' + SEP + '\\n');\n let n = 1;\n while (spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${currentBranch}Backup${n}`]).status === 0) {\n n += 1;\n }\n const backupBranch = `${currentBranch}Backup${n}`;\n runGitChecked(['checkout', '-b', backupBranch], 'Failed to create backup branch');\n runGitChecked(['checkout', currentBranch], 'Failed to return to feature branch');\n process.stdout.write(`✅ Backup created: ${backupBranch}\\n\\n`);\n return backupBranch;\n}\n\nfunction saveConflictContext(\n conflictedFiles: string[], mergeDir: string, forkPoint: string, featureHead: string, mainHead: string,\n): void {\n for (const file of conflictedFiles) {\n const fileDir = perFileContextDir(mergeDir, file);\n fs.mkdirSync(fileDir, { recursive: true });\n\n const fork = spawnSync('git', ['show', `${forkPoint}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'A-forkpoint.txt'), fork.status === 0 ? (fork.stdout ?? '') : '(file did not exist)\\n');\n const feature = spawnSync('git', ['show', `${featureHead}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'B-feature.txt'), feature.status === 0 ? (feature.stdout ?? '') : '(file did not exist)\\n');\n const main = spawnSync('git', ['show', `${mainHead}:${file}`], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'C-main.txt'), main.status === 0 ? (main.stdout ?? '') : '(file did not exist)\\n');\n\n const ba = spawnSync('git', ['diff', forkPoint, featureHead, '--', file], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'B-A.diff'), ba.stdout ?? '');\n const ca = spawnSync('git', ['diff', forkPoint, mainHead, '--', file], { encoding: 'utf8' });\n fs.writeFileSync(path.join(fileDir, 'C-A.diff'), ca.stdout ?? '');\n }\n}\n\n// Single source of truth for the merge process. The script WRITES it at conflict time (rather\n// than the AI relying on a separate hand-maintained doc), parameterized with the live MERGE_DIR\n// and conflicted-file list, so the instructions can never drift from the actual layout. The body\n// is a template constant with {{...}} placeholders so this stays a small filler function.\nconst MERGE_PROCESS_TEMPLATE = `# AI-Assisted Squash-Merge Conflict Resolution (generated)\n\nThis file was generated by \\`pnpm wp-git-update\\` when the 3-point squash-merge hit conflicts.\nIt is the single source of truth for the merge process — follow it exactly.\n\nYou are on branch \\`{{SQUASH_BRANCH}}\\` with conflict markers in the working tree.\n\\`MERGE_DIR = {{MERGE_DIR}}\\`\n\n## How the gate works\n\n- Resolve every conflicted file in the working tree.\n- Run **\\`pnpm wp-finish-upsert-pr\\`** — the validation + finish gate. It scans for leftover conflict\n markers, checks each conflicted file has a written merge explanation, runs the \\`nx affected\\`\n build, and — only if all pass — stages, commits, then renders the dashboard and creates/updates the PR.\n- **Do NOT run \\`git add\\` / \\`git commit\\` / \\`git push\\` / \\`gh pr\\` yourself.** They are blocked by\n the \\`merge-in-progress-guard\\` hook until the gate validates. The gate does the commit.\n\n## STEP 1 — Load the merge context\n\nPer conflicted file, \\`MERGE_DIR/updatemain-<safe_path>/\\` holds (\\`<safe_path>\\` = path with \\`/\\`→\\`__\\`):\n\n\\`\\`\\`\nA-forkpoint.txt # file at fork point (base)\nB-feature.txt # file on your feature branch\nC-main.txt # file on main\nB-A.diff # what your feature changed (B−A)\nC-A.diff # what main changed (C−A)\n\\`\\`\\`\n\n\\`updatemain-hashes.json\\` holds A/B/C commit hashes. To see why main changed:\n\\`git log <A>..<C> --oneline\\`.\n\n## STEP 2 — Resolve each conflicted file\n\nFor each file: read the working-tree file (the markers) and its \\`B-A.diff\\` / \\`C-A.diff\\` (intent),\nthen Edit to the resolved version, removing ALL conflict markers.\n\nStrategies: goals align & non-overlapping → merge both · one side removes what the other modifies\n→ prefer the removal · same lines, simple (imports/format) → merge both · same lines, complex or\nconflicting goals → ask the user · feature re-implements what main already squashed → prefer\nmain's, then re-apply only the genuinely new feature work.\n\n**Then write a merge explanation** for each conflicted file — NOT a comment in the source (that\nbreaks for JSON and deleted files). Write it next to that file's diffs, at:\n\n\\`\\`\\`\nMERGE_DIR/updatemain-<safe_path>/{{EXPLANATION_FILE}}\n\\`\\`\\`\n\n(\\`<safe_path>\\` = the conflict file path with \\`/\\` → \\`__\\`, the same dir that holds its\n\\`A-forkpoint.txt\\` / \\`B-A.diff\\` / \\`C-A.diff\\`.) In it, explain in a few sentences how you resolved\nthis file: which side you took where, what you combined from B-A.diff vs C-A.diff, and why. The\ngate fails if any conflicted file's explanation is missing or empty. Do not paste A/B/C context\nblocks into the source code.\n\n## STEP 3 — Run the gate (validates the merge AND finishes the PR)\n\n\\`\\`\\`\npnpm wp-finish-upsert-pr\n\\`\\`\\`\n\n- Leftover conflict markers → fix those files and re-run.\n- Missing merge explanation → write it (see STEP 2) and re-run.\n- Build failure → fix the TypeScript/lint errors and re-run (the gate re-stages for you).\n- Missing review.json → write it in the printed format (your PR review), then re-run.\n- On success it commits, renders the dashboard, and creates/updates the PR.\n\n## Conflicted files\n\n{{FILE_LIST}}\n\n## If you need to bail out\n\nA backup branch was created (e.g. \\`<feature>Backup1\\`). To abandon:\n\n\\`\\`\\`\ngit merge --abort 2>/dev/null; git checkout <feature> ; git branch -D {{SQUASH_BRANCH}}\n\\`\\`\\`\n\nThen delete \\`{{MERGE_DIR}}/\\` for a clean slate.\n`;\n\nfunction mergeProcessDoc(mergeDir: string, squashBranch: string, conflictedFiles: string[]): string {\n const fileList = conflictedFiles.map((f: string): string => `- \\`${f}\\``).join('\\n');\n return MERGE_PROCESS_TEMPLATE\n .replace(/\\{\\{SQUASH_BRANCH\\}\\}/g, squashBranch)\n .replace(/\\{\\{MERGE_DIR\\}\\}/g, mergeDir)\n .replace(/\\{\\{EXPLANATION_FILE\\}\\}/g, MERGE_EXPLANATION_FILE)\n .replace(/\\{\\{FILE_LIST\\}\\}/g, fileList);\n}\n\n// Returns the absolute path of the written doc.\nfunction writeMergeProcessDoc(repoRoot: string, mergeDir: string, squashBranch: string, conflictedFiles: string[]): string {\n const docDir = path.join(repoRoot, WEBPIECES_TMP_DIR, 'instruct-ai');\n fs.mkdirSync(docDir, { recursive: true });\n const docPath = path.join(docDir, 'webpieces.mergeprocess.md');\n fs.writeFileSync(docPath, mergeProcessDoc(mergeDir, squashBranch, conflictedFiles));\n return docPath;\n}\n\nfunction printConflictHandback(docPath: string, mergeDir: string, squashBranch: string, conflictedFiles: string[]): void {\n process.stdout.write('\\n' + SEP + `⚠️ Conflicts in ${conflictedFiles.length} file(s) — handing control back to you\\n` + SEP + '\\n');\n process.stdout.write(`You are on branch ${squashBranch} with conflicts in the working tree.\\n\\n`);\n process.stdout.write(`FOLLOW THE MERGE PROCESS: ${docPath}\\n`);\n process.stdout.write(`3-point context per file in: ${mergeDir}/updatemain-<file>/\\n\\n`);\n process.stdout.write('Conflicted files:\\n');\n for (const file of conflictedFiles) process.stdout.write(` - ${file}\\n`);\n process.stdout.write('\\n' + SEP);\n}\n\nfunction handleConflictsHandback(\n repoRoot: string, mergeDir: string, currentBranch: string, squashBranch: string,\n backupBranch: string, prNumber: string, hashes: HashPoints,\n): void {\n const raw = execSync('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();\n const conflictedFiles = raw.split('\\n').filter((f: string): boolean => f.trim() !== '');\n fs.writeFileSync(path.join(mergeDir, 'updatemain-conflicted-files.txt'), raw + '\\n');\n saveConflictContext(conflictedFiles, mergeDir, hashes.hashForkPoint, hashes.hashFeatureHead, hashes.hashMainHead);\n\n const marker = new MergeMarker(\n currentBranch, squashBranch, backupBranch, prNumber, conflictedFiles,\n hashes.hashForkPoint, hashes.hashFeatureHead, hashes.hashMainHead, false,\n );\n writeMergeMarker(mergeDir, marker);\n const docPath = writeMergeProcessDoc(repoRoot, mergeDir, squashBranch, conflictedFiles);\n printConflictHandback(docPath, mergeDir, squashBranch, conflictedFiles);\n process.exit(2);\n}\n\nfunction finalizeBranch(\n currentBranch: string, squashBranch: string, backupBranch: string, prNumber: string,\n): void {\n process.stdout.write('\\n' + SEP + '🗑️ Finalizing\\n' + SEP + '\\n');\n runGitChecked(['branch', '-D', currentBranch], 'Failed to delete old feature branch');\n\n const remoteExists = spawnSync('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;\n if (remoteExists) {\n process.stdout.write(prNumber ? `Updating PR #${prNumber} (force-with-lease)...\\n` : 'Updating remote branch (force-with-lease)...\\n');\n runGitChecked(['push', '-u', '--force-with-lease', 'origin', `${squashBranch}:${currentBranch}`], 'Failed to push to origin');\n } else {\n process.stdout.write('No remote branch — local only.\\n');\n }\n runGitChecked(['checkout', squashBranch], 'Failed to checkout squash branch');\n runGitChecked(['branch', '-m', currentBranch], 'Failed to rename squash branch');\n\n process.stdout.write(`\\n✅ Branch ${currentBranch} updated from main. Backup: ${backupBranch}\\n`);\n process.stdout.write(` Delete backup when safe: git branch -D ${backupBranch}\\n\\n`);\n}\n\nasync function handleResume(mergeDir: string, marker: MergeMarker): Promise<void> {\n if (!marker.validated) {\n process.stdout.write('\\n' + SEP + '⏸️ Merge in progress — not yet validated\\n' + SEP + '\\n');\n process.stdout.write('Resolve the remaining conflicts in the working tree, then run:\\n');\n process.stdout.write(' pnpm wp-finish-upsert-pr\\n\\n');\n process.exit(1);\n }\n process.stdout.write('Resuming: merge validated — finalizing.\\n');\n finalizeBranch(marker.currentBranch, marker.squashBranch, marker.backupBranch, marker.prNumber);\n clearMergeMarker(mergeDir);\n await cleanTmp();\n}\n\nasync function runFreshUpdate(repoRoot: string, mergeDir: string): Promise<void> {\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n if (currentBranch.endsWith('Squash')) {\n process.stderr.write(`❌ On a leftover ${currentBranch} branch with no merge marker. Clean up: git branch -D ${currentBranch}\\n`);\n process.exit(1);\n }\n\n process.stdout.write('\\n' + SEP + '🔄 Squash-Merge Update from Main\\n' + SEP + '\\n');\n await gatherInfo();\n const hashes = JSON.parse(fs.readFileSync(path.join(mergeDir, 'updatemain-hashes.json'), 'utf8')) as HashPoints;\n\n const prNumber = detectPr(currentBranch);\n process.stdout.write(prNumber ? `Existing PR #${prNumber} will be updated.\\n` : 'No existing PR (one can be created later).\\n');\n\n const backupBranch = createBackup(currentBranch);\n const squashBranch = `${currentBranch}Squash`;\n if (spawnSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${squashBranch}`]).status === 0) {\n process.stderr.write(`❌ Stale ${squashBranch} from a previous run. Delete it: git branch -D ${squashBranch}\\n`);\n process.exit(1);\n }\n\n runGitChecked(['checkout', 'main'], 'Failed to checkout main');\n runGitChecked(['pull', 'origin', 'main'], 'Failed to pull origin/main');\n runGitChecked(['checkout', '-b', squashBranch], 'Failed to create squash branch');\n\n process.stdout.write('\\n' + SEP + `🔀 Squash merging ${currentBranch}\\n` + SEP + '\\n');\n const merge = spawnSync('git', ['merge', '--squash', currentBranch], { stdio: 'inherit' });\n if (merge.status !== 0) {\n handleConflictsHandback(repoRoot, mergeDir, currentBranch, squashBranch, backupBranch, prNumber, hashes);\n return;\n }\n\n const nothingStaged = spawnSync('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;\n if (nothingStaged) {\n process.stdout.write('ℹ️ Already up-to-date with main (nothing to merge).\\n');\n } else {\n runGitChecked(['commit', '-m', `Squash merge of ${currentBranch}`], 'Failed to commit squash merge');\n }\n finalizeBranch(currentBranch, squashBranch, backupBranch, prNumber);\n await cleanTmp();\n}\n\nexport async function main(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const featureName = getFeatureName();\n const mergeDir = mergeDirFor(repoRoot, featureName);\n fs.mkdirSync(mergeDir, { recursive: true });\n\n const existing = readMergeMarker(mergeDir);\n if (existing) {\n await handleResume(mergeDir, existing);\n return;\n }\n await runFreshUpdate(repoRoot, mergeDir);\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,4 +1,4 @@
|
|
|
1
|
-
export declare const DEFAULT_BUILD_COMMAND = "pnpm nx affected --target=ci --base
|
|
1
|
+
export declare const DEFAULT_BUILD_COMMAND = "pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)";
|
|
2
2
|
/**
|
|
3
3
|
* Resolve the exact build command this gate will run for a repo: the project's configured
|
|
4
4
|
* PrGateConfig.buildCommand, or the default affected-ci command when none is set. Callers
|
|
@@ -13,7 +13,7 @@ export declare function resolveBuildCommand(repoRoot: string): string;
|
|
|
13
13
|
export declare function runBuildAffected(repoRoot: string, buildCommand?: string): number;
|
|
14
14
|
/**
|
|
15
15
|
* Run the build gate using the project's configured command (PrGateConfig.buildCommand). The
|
|
16
|
-
*
|
|
16
|
+
* finish-upsert-pr gate and wp-start-upsert-pr both call THIS so they provably build with the same
|
|
17
17
|
* command — the resolution the AI validates is built exactly as the PR command builds it.
|
|
18
18
|
* Returns the exit code; callers print their own re-run hint.
|
|
19
19
|
*/
|
|
@@ -7,10 +7,14 @@ exports.runConfiguredBuildGate = runConfiguredBuildGate;
|
|
|
7
7
|
const child_process_1 = require("child_process");
|
|
8
8
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
9
9
|
// Single source of truth for the build gate. Both `wp-build-affected` (CI + local) and the
|
|
10
|
-
// merge validation gate (`wp-
|
|
10
|
+
// merge validation gate (`wp-finish-upsert-pr`) run THIS, so "what CI runs" and "what the
|
|
11
11
|
// PR command runs" can never drift. nx `affected` only rebuilds changed projects, so this
|
|
12
12
|
// stays fast on a large monorepo.
|
|
13
|
-
|
|
13
|
+
// `--base=$(git merge-base origin/main HEAD)` (the fork point) instead of `--base=origin/main`:
|
|
14
|
+
// origin/main rebuilds projects touched by OTHER people's merged PRs (your branch still holds the
|
|
15
|
+
// pre-merge versions, so nx sees them as "affected"). The fork point scopes affected to only YOUR
|
|
16
|
+
// branch's changes. The `$(...)` resolves because runBuildAffected runs with shell: true.
|
|
17
|
+
exports.DEFAULT_BUILD_COMMAND = 'pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)';
|
|
14
18
|
/**
|
|
15
19
|
* Resolve the exact build command this gate will run for a repo: the project's configured
|
|
16
20
|
* PrGateConfig.buildCommand, or the default affected-ci command when none is set. Callers
|
|
@@ -33,7 +37,7 @@ function runBuildAffected(repoRoot, buildCommand) {
|
|
|
33
37
|
}
|
|
34
38
|
/**
|
|
35
39
|
* Run the build gate using the project's configured command (PrGateConfig.buildCommand). The
|
|
36
|
-
*
|
|
40
|
+
* finish-upsert-pr gate and wp-start-upsert-pr both call THIS so they provably build with the same
|
|
37
41
|
* command — the resolution the AI validates is built exactly as the PR command builds it.
|
|
38
42
|
* Returns the exit code; callers print their own re-run hint.
|
|
39
43
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-affected.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/build-affected.ts"],"names":[],"mappings":";;;
|
|
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;AA3CD,iDAA0C;AAC1C,0DAA0D;AAE1D,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","sourcesContent":["import { spawnSync } from 'child_process';\nimport { loadAndValidate } 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"]}
|
|
@@ -15,7 +15,7 @@ const path = tslib_1.__importStar(require("path"));
|
|
|
15
15
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
16
16
|
// Proof-obligation marker written when a 3-point squash-merge hits conflicts. Its mere
|
|
17
17
|
// presence (with validated=false) is what the merge-in-progress-guard hook uses to block
|
|
18
|
-
// commit/push/PR until `wp-
|
|
18
|
+
// commit/push/PR until `wp-finish-upsert-pr` validates the resolution and flips it true.
|
|
19
19
|
class MergeMarker {
|
|
20
20
|
currentBranch;
|
|
21
21
|
squashBranch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge-state.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/merge-state.ts"],"names":[],"mappings":";;;AA0DA,kCAEC;AAMD,8CAEC;AAED,gCAEC;AAED,0CAeC;AAED,4CAGC;AAED,4CAGC;AAMD,kDASC;AASD,sDAQC;;AAnID,+CAAyB;AACzB,mDAA6B;AAC7B,0DAKiC;AAEjC,uFAAuF;AACvF,yFAAyF;AACzF,
|
|
1
|
+
{"version":3,"file":"merge-state.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/pr-gate/src/scripts/workflow/merge-state.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport {\n WEBPIECES_TMP_DIR,\n MERGE_DIR_PREFIX,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n} from '@webpieces/rules-config';\n\n// Proof-obligation marker written when a 3-point squash-merge hits conflicts. Its mere\n// presence (with validated=false) is what the merge-in-progress-guard hook uses to block\n// commit/push/PR until `wp-finish-upsert-pr` validates the resolution and flips it true.\nexport class MergeMarker {\n currentBranch: string;\n squashBranch: string;\n backupBranch: string;\n prNumber: string;\n conflictedFiles: string[];\n forkPoint: string;\n featureHead: string;\n mainHead: string;\n validated: boolean;\n\n constructor(\n currentBranch: string,\n squashBranch: string,\n backupBranch: string,\n prNumber: string,\n conflictedFiles: string[],\n forkPoint: string,\n featureHead: string,\n mainHead: string,\n validated: boolean,\n ) {\n this.currentBranch = currentBranch;\n this.squashBranch = squashBranch;\n this.backupBranch = backupBranch;\n this.prNumber = prNumber;\n this.conflictedFiles = conflictedFiles;\n this.forkPoint = forkPoint;\n this.featureHead = featureHead;\n this.mainHead = mainHead;\n this.validated = validated;\n }\n}\n\nexport class MarkerScanResult {\n clean: boolean;\n filesWithMarkers: string[];\n\n constructor(clean: boolean, filesWithMarkers: string[]) {\n this.clean = clean;\n this.filesWithMarkers = filesWithMarkers;\n }\n}\n\nconst CONFLICT_MARKER_RE = /^(<{7}|={7}|>{7})/m;\n\nexport function mergeDirFor(repoRoot: string, featureName: string): string {\n return path.join(repoRoot, WEBPIECES_TMP_DIR, `${MERGE_DIR_PREFIX}${featureName}`);\n}\n\n// Per-conflicted-file context dir holding A-forkpoint.txt / B-feature.txt / C-main.txt /\n// B-A.diff / C-A.diff (and the AI's merge-explanation.md). Shared so the writer\n// (saveConflictContext) and the reader (the explanation gate) agree on the layout: the conflict\n// file path with `/` → `__`, prefixed `updatemain-`.\nexport function perFileContextDir(mergeDir: string, file: string): string {\n return path.join(mergeDir, `updatemain-${file.replace(/\\//g, '__')}`);\n}\n\nexport function markerPath(mergeDir: string): string {\n return path.join(mergeDir, MERGE_IN_PROGRESS_FILE);\n}\n\nexport function readMergeMarker(mergeDir: string): MergeMarker | null {\n const filePath = markerPath(mergeDir);\n if (!fs.existsSync(filePath)) return null;\n const raw = JSON.parse(fs.readFileSync(filePath, 'utf8')) as MergeMarker;\n return new MergeMarker(\n raw.currentBranch,\n raw.squashBranch,\n raw.backupBranch,\n raw.prNumber,\n raw.conflictedFiles ?? [],\n raw.forkPoint,\n raw.featureHead,\n raw.mainHead,\n raw.validated === true,\n );\n}\n\nexport function writeMergeMarker(mergeDir: string, marker: MergeMarker): void {\n fs.mkdirSync(mergeDir, { recursive: true });\n fs.writeFileSync(markerPath(mergeDir), JSON.stringify(marker, null, 2) + '\\n');\n}\n\nexport function clearMergeMarker(mergeDir: string): void {\n const filePath = markerPath(mergeDir);\n if (fs.existsSync(filePath)) fs.rmSync(filePath);\n}\n\n/**\n * Scoped conflict-marker scan: reads ONLY the given conflicted files (relative to repo\n * root), never the whole repo — stays O(conflicts) regardless of monorepo size.\n */\nexport function scanConflictMarkers(repoRoot: string, files: string[]): MarkerScanResult {\n const filesWithMarkers: string[] = [];\n for (const file of files) {\n const abs = path.join(repoRoot, file);\n if (!fs.existsSync(abs)) continue;\n const content = fs.readFileSync(abs, 'utf8');\n if (CONFLICT_MARKER_RE.test(content)) filesWithMarkers.push(file);\n }\n return new MarkerScanResult(filesWithMarkers.length === 0, filesWithMarkers);\n}\n\n/**\n * Explanation scan: every conflicted file the AI resolved must have a non-empty\n * MERGE_EXPLANATION_FILE sitting in its per-file context dir (next to the diffs), proving the AI\n * deliberately 3-point merged it and recording HOW. Returns the files whose explanation is\n * missing or empty. Works for every conflicted file regardless of type — including comment-less\n * files (JSON) and files resolved by deletion (no working-tree file to inspect).\n */\nexport function scanMergeExplanations(mergeDir: string, files: string[]): MarkerScanResult {\n const filesMissingExplanation: string[] = [];\n for (const file of files) {\n const explPath = path.join(perFileContextDir(mergeDir, file), MERGE_EXPLANATION_FILE);\n const present = fs.existsSync(explPath) && fs.readFileSync(explPath, 'utf8').trim() !== '';\n if (!present) filesMissingExplanation.push(file);\n }\n return new MarkerScanResult(filesMissingExplanation.length === 0, filesMissingExplanation);\n}\n"]}
|
|
@@ -7,7 +7,10 @@ const rules_config_1 = require("@webpieces/rules-config");
|
|
|
7
7
|
const build_affected_1 = require("./workflow/build-affected");
|
|
8
8
|
// Single shared build entry point. CI runs this AND the PR command runs this, so the two can
|
|
9
9
|
// never diverge. The build command is sourced from PrGateConfig.buildCommand (webpieces.config.json
|
|
10
|
-
// "pr-gate" section), defaulting to `pnpm nx affected --target=ci --base
|
|
10
|
+
// "pr-gate" section), defaulting to `pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)`.
|
|
11
|
+
// The `--base` is the FORK POINT (git merge-base), NOT origin/main: basing on origin/main would mark
|
|
12
|
+
// projects touched by other people's already-merged PRs as "affected" (your branch still has their
|
|
13
|
+
// pre-merge versions), wasting a rebuild. The fork point scopes "affected" to only your branch's work.
|
|
11
14
|
function main() {
|
|
12
15
|
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
13
16
|
const config = (0, rules_config_1.loadAndValidate)(repoRoot).prGate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wp-build-affected.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-build-affected.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"wp-build-affected.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-build-affected.ts"],"names":[],"mappings":";;;AAWA,oBAKC;AAfD,iDAAyC;AACzC,0DAA0D;AAC1D,8DAA6D;AAE7D,6FAA6F;AAC7F,oGAAoG;AACpG,8GAA8G;AAC9G,qGAAqG;AACrG,mGAAmG;AACnG,uGAAuG;AACvG,SAAgB,IAAI;IAChB,MAAM,QAAQ,GAAG,IAAA,wBAAQ,EAAC,+BAA+B,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxF,MAAM,MAAM,GAAG,IAAA,8BAAe,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;IAChD,MAAM,IAAI,GAAG,IAAA,iCAAgB,EAAC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACvB,CAAC;AAED,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC1B,IAAI,EAAE,CAAC;AACX,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { execSync } from 'child_process';\nimport { loadAndValidate } from '@webpieces/rules-config';\nimport { runBuildAffected } from './workflow/build-affected';\n\n// Single shared build entry point. CI runs this AND the PR command runs this, so the two can\n// never diverge. The build command is sourced from PrGateConfig.buildCommand (webpieces.config.json\n// \"pr-gate\" section), defaulting to `pnpm nx affected --target=ci --base=$(git merge-base origin/main HEAD)`.\n// The `--base` is the FORK POINT (git merge-base), NOT origin/main: basing on origin/main would mark\n// projects touched by other people's already-merged PRs as \"affected\" (your branch still has their\n// pre-merge versions), wasting a rebuild. The fork point scopes \"affected\" to only your branch's work.\nexport function main(): void {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const config = loadAndValidate(repoRoot).prGate;\n const code = runBuildAffected(repoRoot, config.buildCommand);\n process.exit(code);\n}\n\nif (require.main === module) {\n main();\n}\n"]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.main = main;
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
7
|
+
const git_readAiBranchName_1 = require("./workflow/git-readAiBranchName");
|
|
8
|
+
const build_affected_1 = require("./workflow/build-affected");
|
|
9
|
+
const git_exec_1 = require("./workflow/git-exec");
|
|
10
|
+
// START of the AI-first PR flow (webpieces is AI-driven, so we invert trytami's human-first flow):
|
|
11
|
+
// this command does the deterministic setup — update from main, push, run the build gate — then
|
|
12
|
+
// hands the AI instructions to WRITE review.json and run `wp-finish-upsert-pr` (which reads it and
|
|
13
|
+
// posts the PR). This command NEVER creates/updates a PR; all `gh` posting lives in finish.
|
|
14
|
+
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
15
|
+
// Step A — bring the branch up to date with main via the 3-point engine (child process, so its
|
|
16
|
+
// conflict handback / guard interplay is unaffected by this command's hook context).
|
|
17
|
+
function runUpdateFromMain() {
|
|
18
|
+
process.stdout.write('\n' + SEP + '① Updating branch from main\n' + SEP + '\n');
|
|
19
|
+
const result = (0, child_process_1.spawnSync)('pnpm', ['wp-git-update'], { stdio: 'inherit' });
|
|
20
|
+
if (result.status === 2) {
|
|
21
|
+
process.stdout.write('\n⏸️ Conflicts — resolve them, then run pnpm wp-finish-upsert-pr (it validates the merge AND finishes the PR).\n');
|
|
22
|
+
process.exit(2);
|
|
23
|
+
}
|
|
24
|
+
if (result.status !== 0) {
|
|
25
|
+
process.stderr.write('\n❌ Branch update failed — see output above.\n');
|
|
26
|
+
process.exit(result.status ?? 1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function ensurePushed(currentBranch) {
|
|
30
|
+
const remoteExists = (0, child_process_1.spawnSync)('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;
|
|
31
|
+
if (remoteExists) {
|
|
32
|
+
(0, git_exec_1.runGitChecked)(['push', '--force-with-lease', 'origin', `HEAD:${currentBranch}`], 'Failed to push branch');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
(0, git_exec_1.runGitChecked)(['push', '-u', 'origin', `HEAD:${currentBranch}`], 'Failed to push new branch');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function main() {
|
|
39
|
+
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
40
|
+
runUpdateFromMain();
|
|
41
|
+
ensurePushed((0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim());
|
|
42
|
+
const buildCommand = (0, build_affected_1.resolveBuildCommand)(repoRoot);
|
|
43
|
+
process.stdout.write('\n' + SEP + '② Build gate (nx affected)\n' + SEP + '\n');
|
|
44
|
+
process.stdout.write(`This gate runs the build command below. To get it passing BEFORE this command runs it,\n` +
|
|
45
|
+
`run the SAME command yourself first and fix everything it reports:\n\n` +
|
|
46
|
+
` ${buildCommand}\n\n`);
|
|
47
|
+
const buildCode = (0, build_affected_1.runConfiguredBuildGate)(repoRoot);
|
|
48
|
+
if (buildCode !== 0) {
|
|
49
|
+
process.stderr.write(`\n❌ Build failed — fix it before reviewing.\n\n` +
|
|
50
|
+
`Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-start-upsert-pr:\n\n` +
|
|
51
|
+
` ${buildCommand}\n\n`);
|
|
52
|
+
process.exit(buildCode);
|
|
53
|
+
}
|
|
54
|
+
// Hand the AI its next step: write review.json, then run finish (which posts the PR).
|
|
55
|
+
const reviewPath = (0, rules_config_1.reviewJsonPath)(repoRoot, (0, git_readAiBranchName_1.getFeatureName)());
|
|
56
|
+
process.stdout.write('\n' + SEP + '③ Review the PR, then finish\n' + SEP + '\n');
|
|
57
|
+
process.stdout.write(`Branch is updated, pushed, and the build gate passed. Now review your own changes and\n` +
|
|
58
|
+
`${(0, rules_config_1.reviewJsonSchemaHint)(reviewPath)}\n\n` +
|
|
59
|
+
`Then run: pnpm wp-finish-upsert-pr\n` +
|
|
60
|
+
`(It re-validates the build, renders the dashboard with your risk/violations, and creates/updates the PR.)\n\n`);
|
|
61
|
+
}
|
|
62
|
+
if (require.main === module) {
|
|
63
|
+
main();
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=wp-start-upsert-pr.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wp-start-upsert-pr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-start-upsert-pr.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { execSync, spawnSync } from 'child_process';\nimport { reviewJsonPath, reviewJsonSchemaHint } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { runConfiguredBuildGate, resolveBuildCommand } from './workflow/build-affected';\nimport { runGitChecked } from './workflow/git-exec';\n\n// START of the AI-first PR flow (webpieces is AI-driven, so we invert trytami's human-first flow):\n// this command does the deterministic setup — update from main, push, run the build gate — then\n// hands the AI instructions to WRITE review.json and run `wp-finish-upsert-pr` (which reads it and\n// posts the PR). This command NEVER creates/updates a PR; all `gh` posting lives in finish.\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// Step A — bring the branch up to date with main via the 3-point engine (child process, so its\n// conflict handback / guard interplay is unaffected by this command's hook context).\nfunction runUpdateFromMain(): void {\n process.stdout.write('\\n' + SEP + '① Updating branch from main\\n' + SEP + '\\n');\n const result = spawnSync('pnpm', ['wp-git-update'], { stdio: 'inherit' });\n if (result.status === 2) {\n process.stdout.write('\\n⏸️ Conflicts — resolve them, then run pnpm wp-finish-upsert-pr (it validates the merge AND finishes the PR).\\n');\n process.exit(2);\n }\n if (result.status !== 0) {\n process.stderr.write('\\n❌ Branch update failed — see output above.\\n');\n process.exit(result.status ?? 1);\n }\n}\n\nfunction 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\nexport function main(): void {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n\n runUpdateFromMain();\n ensurePushed(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n\n const buildCommand = resolveBuildCommand(repoRoot);\n process.stdout.write('\\n' + SEP + '② Build gate (nx affected)\\n' + SEP + '\\n');\n process.stdout.write(\n `This gate runs the build command below. To get it passing BEFORE this command runs it,\\n` +\n `run the SAME command yourself first and fix everything it reports:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n const buildCode = runConfiguredBuildGate(repoRoot);\n if (buildCode !== 0) {\n process.stderr.write(\n `\\n❌ Build failed — fix it before reviewing.\\n\\n` +\n `Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-start-upsert-pr:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n process.exit(buildCode);\n }\n\n // Hand the AI its next step: write review.json, then run finish (which posts the PR).\n const reviewPath = reviewJsonPath(repoRoot, getFeatureName());\n process.stdout.write('\\n' + SEP + '③ Review the PR, then finish\\n' + SEP + '\\n');\n process.stdout.write(\n `Branch is updated, pushed, and the build gate passed. Now review your own changes and\\n` +\n `${reviewJsonSchemaHint(reviewPath)}\\n\\n` +\n `Then run: pnpm wp-finish-upsert-pr\\n` +\n `(It re-validates the build, renders the dashboard with your risk/violations, and creates/updates the PR.)\\n\\n`,\n );\n}\n\nif (require.main === module) {\n main();\n}\n"]}
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.main = main;
|
|
5
|
-
const tslib_1 = require("tslib");
|
|
6
|
-
const child_process_1 = require("child_process");
|
|
7
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
-
const path = tslib_1.__importStar(require("path"));
|
|
9
|
-
const git_readAiBranchName_1 = require("./workflow/git-readAiBranchName");
|
|
10
|
-
const git_exec_1 = require("./workflow/git-exec");
|
|
11
|
-
const build_affected_1 = require("./workflow/build-affected");
|
|
12
|
-
const rules_config_1 = require("@webpieces/rules-config");
|
|
13
|
-
const merge_state_1 = require("./workflow/merge-state");
|
|
14
|
-
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
15
|
-
// Validate the AI's resolution of the conflicted files — the part of the process the AI owns
|
|
16
|
-
// (branch creation/finalization is the script's job, so it is not re-checked here). Exits the
|
|
17
|
-
// process with a fix instruction on any failure; returns only when all three checks pass.
|
|
18
|
-
function validateResolution(repoRoot, mergeDir, conflictedFiles) {
|
|
19
|
-
// 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).
|
|
20
|
-
const scan = (0, merge_state_1.scanConflictMarkers)(repoRoot, conflictedFiles);
|
|
21
|
-
if (!scan.clean) {
|
|
22
|
-
process.stderr.write('❌ Unresolved conflict markers (<<<<<<< / ======= / >>>>>>>) remain in:\n');
|
|
23
|
-
for (const file of scan.filesWithMarkers)
|
|
24
|
-
process.stderr.write(` - ${file}\n`);
|
|
25
|
-
process.stderr.write('\nResolve them, then re-run: pnpm wp-git-merge-complete\n');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
// 2. Ensure git itself has no remaining unmerged entries.
|
|
29
|
-
const unmerged = (0, child_process_1.execSync)('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();
|
|
30
|
-
if (unmerged !== '') {
|
|
31
|
-
process.stderr.write('❌ Git still reports unmerged files:\n' + unmerged + '\n');
|
|
32
|
-
process.stderr.write('\nResolve and `git add` them, then re-run: pnpm wp-git-merge-complete\n');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
process.stdout.write('✅ No conflict markers in resolved files.\n');
|
|
36
|
-
// 3. Explanation check — every conflicted file must have a non-empty merge-explanation.md in
|
|
37
|
-
// its per-file context dir, proving the AI deliberately 3-point merged it (and recording how)
|
|
38
|
-
// rather than blindly taking one side. A sidecar file works for any type, incl. JSON/deletes.
|
|
39
|
-
const explanations = (0, merge_state_1.scanMergeExplanations)(mergeDir, conflictedFiles);
|
|
40
|
-
if (!explanations.clean) {
|
|
41
|
-
process.stderr.write(`❌ Missing/empty merge explanation (${rules_config_1.MERGE_EXPLANATION_FILE}) for:\n`);
|
|
42
|
-
for (const file of explanations.filesWithMarkers) {
|
|
43
|
-
process.stderr.write(` - ${file}\n → ${path.join((0, merge_state_1.perFileContextDir)(mergeDir, file), rules_config_1.MERGE_EXPLANATION_FILE)}\n`);
|
|
44
|
-
}
|
|
45
|
-
process.stderr.write('\nWrite a few sentences on how you resolved each (which side, what you combined, why),\n' +
|
|
46
|
-
'then re-run: pnpm wp-git-merge-complete\n');
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
process.stdout.write('✅ Merge explanations present for all resolved files.\n');
|
|
50
|
-
}
|
|
51
|
-
async function main() {
|
|
52
|
-
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
53
|
-
const featureName = (0, git_readAiBranchName_1.getFeatureName)();
|
|
54
|
-
const mergeDir = (0, merge_state_1.mergeDirFor)(repoRoot, featureName);
|
|
55
|
-
const marker = (0, merge_state_1.readMergeMarker)(mergeDir);
|
|
56
|
-
if (!marker) {
|
|
57
|
-
process.stderr.write('❌ No merge in progress (no marker found). Nothing to complete.\n');
|
|
58
|
-
process.exit(1);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
if (marker.validated) {
|
|
62
|
-
process.stdout.write('✅ Merge already validated. Run pnpm wp-git-update (or wp-upsert-pr) to finalize.\n');
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
process.stdout.write('\n' + SEP + '🔎 Validating Merge Resolution\n' + SEP + '\n');
|
|
66
|
-
validateResolution(repoRoot, mergeDir, marker.conflictedFiles);
|
|
67
|
-
(0, git_exec_1.runGitChecked)(['add', '-A'], 'Failed to stage resolved files');
|
|
68
|
-
// Build gate (authoritative). Same configured command wp-upsert-pr uses — fast on a big monorepo.
|
|
69
|
-
const buildCommand = (0, build_affected_1.resolveBuildCommand)(repoRoot);
|
|
70
|
-
process.stdout.write(`\nRunning the build gate. To get it passing, run the SAME command yourself and fix everything it reports:\n\n` +
|
|
71
|
-
` ${buildCommand}\n\n`);
|
|
72
|
-
const buildCode = (0, build_affected_1.runConfiguredBuildGate)(repoRoot);
|
|
73
|
-
if (buildCode !== 0) {
|
|
74
|
-
process.stderr.write(`\n❌ Build failed.\n\n` +
|
|
75
|
-
`Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-git-merge-complete:\n\n` +
|
|
76
|
-
` ${buildCommand}\n\n`);
|
|
77
|
-
process.exit(buildCode);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
process.stdout.write('\n✅ Build passed.\n');
|
|
81
|
-
// Commit the resolved squash merge.
|
|
82
|
-
const nothingStaged = (0, child_process_1.spawnSync)('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;
|
|
83
|
-
if (!nothingStaged) {
|
|
84
|
-
(0, git_exec_1.runGitChecked)(['commit', '-m', `Squash merge of ${marker.currentBranch} (conflicts resolved)`], 'Failed to commit resolved merge');
|
|
85
|
-
}
|
|
86
|
-
// Flip the marker to validated + drop the proof file → unblocks the guard.
|
|
87
|
-
marker.validated = true;
|
|
88
|
-
(0, merge_state_1.writeMergeMarker)(mergeDir, marker);
|
|
89
|
-
fs.writeFileSync(path.join(mergeDir, 'conflicts-resolved'), '');
|
|
90
|
-
process.stdout.write('\n' + SEP + '✅ Merge validated and committed\n' + SEP + '\n');
|
|
91
|
-
process.stdout.write('Finalize & push by running: pnpm wp-upsert-pr (or pnpm wp-git-update)\n\n');
|
|
92
|
-
}
|
|
93
|
-
if (require.main === module) {
|
|
94
|
-
main().catch((err) => {
|
|
95
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
96
|
-
process.stderr.write(message + '\n');
|
|
97
|
-
process.exit(1);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
//# sourceMappingURL=git-mergeComplete.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"git-mergeComplete.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-mergeComplete.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 { getFeatureName } from './workflow/git-readAiBranchName';\nimport { runGitChecked } from './workflow/git-exec';\nimport { runConfiguredBuildGate, resolveBuildCommand } from './workflow/build-affected';\nimport { MERGE_EXPLANATION_FILE } from '@webpieces/rules-config';\nimport {\n mergeDirFor,\n perFileContextDir,\n readMergeMarker,\n writeMergeMarker,\n scanConflictMarkers,\n scanMergeExplanations,\n} from './workflow/merge-state';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\n// Validate the AI's resolution of the conflicted files — the part of the process the AI owns\n// (branch creation/finalization is the script's job, so it is not re-checked here). Exits the\n// process with a fix instruction on any failure; returns only when all three checks pass.\nfunction validateResolution(repoRoot: string, mergeDir: string, conflictedFiles: string[]): void {\n // 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).\n const scan = scanConflictMarkers(repoRoot, conflictedFiles);\n if (!scan.clean) {\n process.stderr.write('❌ Unresolved conflict markers (<<<<<<< / ======= / >>>>>>>) remain in:\\n');\n for (const file of scan.filesWithMarkers) process.stderr.write(` - ${file}\\n`);\n process.stderr.write('\\nResolve them, then re-run: pnpm wp-git-merge-complete\\n');\n process.exit(1);\n }\n\n // 2. Ensure git itself has no remaining unmerged entries.\n const unmerged = execSync('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();\n if (unmerged !== '') {\n process.stderr.write('❌ Git still reports unmerged files:\\n' + unmerged + '\\n');\n process.stderr.write('\\nResolve and `git add` them, then re-run: pnpm wp-git-merge-complete\\n');\n process.exit(1);\n }\n process.stdout.write('✅ No conflict markers in resolved files.\\n');\n\n // 3. Explanation check — every conflicted file must have a non-empty merge-explanation.md in\n // its per-file context dir, proving the AI deliberately 3-point merged it (and recording how)\n // rather than blindly taking one side. A sidecar file works for any type, incl. JSON/deletes.\n const explanations = scanMergeExplanations(mergeDir, conflictedFiles);\n if (!explanations.clean) {\n process.stderr.write(`❌ Missing/empty merge explanation (${MERGE_EXPLANATION_FILE}) for:\\n`);\n for (const file of explanations.filesWithMarkers) {\n process.stderr.write(` - ${file}\\n → ${path.join(perFileContextDir(mergeDir, file), MERGE_EXPLANATION_FILE)}\\n`);\n }\n process.stderr.write(\n '\\nWrite a few sentences on how you resolved each (which side, what you combined, why),\\n' +\n 'then re-run: pnpm wp-git-merge-complete\\n',\n );\n process.exit(1);\n }\n process.stdout.write('✅ Merge explanations present for all resolved files.\\n');\n}\n\nexport async function main(): Promise<void> {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const featureName = getFeatureName();\n const mergeDir = mergeDirFor(repoRoot, featureName);\n\n const marker = readMergeMarker(mergeDir);\n if (!marker) {\n process.stderr.write('❌ No merge in progress (no marker found). Nothing to complete.\\n');\n process.exit(1);\n return;\n }\n if (marker.validated) {\n process.stdout.write('✅ Merge already validated. Run pnpm wp-git-update (or wp-upsert-pr) to finalize.\\n');\n return;\n }\n\n process.stdout.write('\\n' + SEP + '🔎 Validating Merge Resolution\\n' + SEP + '\\n');\n\n validateResolution(repoRoot, mergeDir, marker.conflictedFiles);\n runGitChecked(['add', '-A'], 'Failed to stage resolved files');\n\n // Build gate (authoritative). Same configured command wp-upsert-pr uses — fast on a big monorepo.\n const buildCommand = resolveBuildCommand(repoRoot);\n process.stdout.write(\n `\\nRunning 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 process.stderr.write(\n `\\n❌ Build failed.\\n\\n` +\n `Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-git-merge-complete:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n process.exit(buildCode);\n return;\n }\n process.stdout.write('\\n✅ Build passed.\\n');\n\n // Commit the resolved squash merge.\n const nothingStaged = spawnSync('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;\n if (!nothingStaged) {\n runGitChecked(\n ['commit', '-m', `Squash merge of ${marker.currentBranch} (conflicts resolved)`],\n 'Failed to commit resolved merge',\n );\n }\n\n // Flip the marker to validated + drop the proof file → unblocks the guard.\n marker.validated = true;\n writeMergeMarker(mergeDir, marker);\n fs.writeFileSync(path.join(mergeDir, 'conflicts-resolved'), '');\n\n process.stdout.write('\\n' + SEP + '✅ Merge validated and committed\\n' + SEP + '\\n');\n process.stdout.write('Finalize & push by running: pnpm wp-upsert-pr (or pnpm wp-git-update)\\n\\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,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.main = main;
|
|
5
|
-
const tslib_1 = require("tslib");
|
|
6
|
-
const child_process_1 = require("child_process");
|
|
7
|
-
const fs = tslib_1.__importStar(require("fs"));
|
|
8
|
-
const path = tslib_1.__importStar(require("path"));
|
|
9
|
-
const rules_config_1 = require("@webpieces/rules-config");
|
|
10
|
-
const git_readAiBranchName_1 = require("./workflow/git-readAiBranchName");
|
|
11
|
-
const build_affected_1 = require("./workflow/build-affected");
|
|
12
|
-
const git_exec_1 = require("./workflow/git-exec");
|
|
13
|
-
const dashboard_1 = require("../dashboard/dashboard");
|
|
14
|
-
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
15
|
-
function gitOut(args) {
|
|
16
|
-
const result = (0, child_process_1.spawnSync)('git', args, { encoding: 'utf8' });
|
|
17
|
-
return result.status === 0 ? (result.stdout ?? '').trim() : '';
|
|
18
|
-
}
|
|
19
|
-
// Step A — bring the branch up to date with main via the 3-point engine (child process, so
|
|
20
|
-
// its conflict handback / guard interplay is unaffected by this command's hook context).
|
|
21
|
-
function runUpdateFromMain() {
|
|
22
|
-
process.stdout.write('\n' + SEP + '① Updating branch from main\n' + SEP + '\n');
|
|
23
|
-
const result = (0, child_process_1.spawnSync)('pnpm', ['wp-git-update'], { stdio: 'inherit' });
|
|
24
|
-
if (result.status === 2) {
|
|
25
|
-
process.stdout.write('\n⏸️ Conflicts — resolve them, run pnpm wp-git-merge-complete, then re-run pnpm wp-upsert-pr.\n');
|
|
26
|
-
process.exit(2);
|
|
27
|
-
}
|
|
28
|
-
if (result.status !== 0) {
|
|
29
|
-
process.stderr.write('\n❌ Branch update failed — see output above.\n');
|
|
30
|
-
process.exit(result.status ?? 1);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function ensurePushed(currentBranch) {
|
|
34
|
-
const remoteExists = (0, child_process_1.spawnSync)('git', ['ls-remote', '--exit-code', '--heads', 'origin', currentBranch]).status === 0;
|
|
35
|
-
if (remoteExists) {
|
|
36
|
-
(0, git_exec_1.runGitChecked)(['push', '--force-with-lease', 'origin', `HEAD:${currentBranch}`], 'Failed to push branch');
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
(0, git_exec_1.runGitChecked)(['push', '-u', 'origin', `HEAD:${currentBranch}`], 'Failed to push new branch');
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
function buildDashboard(repoRoot, buildPassed) {
|
|
43
|
-
const config = (0, rules_config_1.loadAndValidate)(repoRoot).prGate;
|
|
44
|
-
const forkPoint = gitOut(['merge-base', 'origin/main', 'HEAD']);
|
|
45
|
-
const featureHead = gitOut(['rev-parse', 'HEAD']);
|
|
46
|
-
const mainHead = gitOut(['rev-parse', 'origin/main']);
|
|
47
|
-
const range = `${forkPoint}..${featureHead}`;
|
|
48
|
-
const changedFiles = gitOut(['diff', range, '--name-only']).split('\n').filter((f) => f.trim() !== '');
|
|
49
|
-
const patch = gitOut(['diff', range]);
|
|
50
|
-
const title = gitOut(['log', '-1', '--format=%s']);
|
|
51
|
-
const gateResults = (0, dashboard_1.computeGateResults)(config.gates, changedFiles);
|
|
52
|
-
const disables = (0, dashboard_1.countAddedDisables)(patch);
|
|
53
|
-
const input = new dashboard_1.DashboardInput(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, '');
|
|
54
|
-
return (0, dashboard_1.renderDashboard)(input);
|
|
55
|
-
}
|
|
56
|
-
function upsertPr(repoRoot, currentBranch, body) {
|
|
57
|
-
const prDir = path.join(repoRoot, rules_config_1.WEBPIECES_TMP_DIR, `pr-${(0, git_readAiBranchName_1.getFeatureName)()}`);
|
|
58
|
-
fs.mkdirSync(prDir, { recursive: true });
|
|
59
|
-
const bodyFile = path.join(prDir, 'pr-body.md');
|
|
60
|
-
fs.writeFileSync(bodyFile, body + '\n');
|
|
61
|
-
const existing = gitOut(['log', '-1', '--format=%s']); // title fallback
|
|
62
|
-
const prNumber = (0, child_process_1.spawnSync)('gh', ['pr', 'list', '--head', currentBranch, '--json', 'number', '--jq', '.[0].number'], { encoding: 'utf8' });
|
|
63
|
-
const num = prNumber.status === 0 ? (prNumber.stdout ?? '').trim() : '';
|
|
64
|
-
if (num === '') {
|
|
65
|
-
process.stdout.write('Creating PR...\n');
|
|
66
|
-
const create = (0, child_process_1.spawnSync)('gh', ['pr', 'create', '--head', currentBranch, '--base', 'main', '--title', existing, '--body-file', bodyFile], { stdio: 'inherit' });
|
|
67
|
-
if (create.status !== 0) {
|
|
68
|
-
process.stderr.write('⚠️ gh pr create failed — create the PR manually with the body in:\n ' + bodyFile + '\n');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
process.stdout.write(`Updating PR #${num}...\n`);
|
|
74
|
-
(0, child_process_1.spawnSync)('gh', ['pr', 'edit', num, '--body-file', bodyFile], { stdio: 'inherit' });
|
|
75
|
-
}
|
|
76
|
-
(0, child_process_1.spawnSync)('gh', ['pr', 'merge', currentBranch, '--auto', '--squash'], { stdio: 'inherit' });
|
|
77
|
-
}
|
|
78
|
-
function main() {
|
|
79
|
-
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
80
|
-
runUpdateFromMain();
|
|
81
|
-
ensurePushed((0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim());
|
|
82
|
-
const buildCommand = (0, build_affected_1.resolveBuildCommand)(repoRoot);
|
|
83
|
-
process.stdout.write('\n' + SEP + '② Build gate (nx affected)\n' + SEP + '\n');
|
|
84
|
-
process.stdout.write(`This gate runs the build command below. To get it passing BEFORE this command runs it,\n` +
|
|
85
|
-
`run the SAME command yourself first and fix everything it reports:\n\n` +
|
|
86
|
-
` ${buildCommand}\n\n`);
|
|
87
|
-
const buildCode = (0, build_affected_1.runConfiguredBuildGate)(repoRoot);
|
|
88
|
-
if (buildCode !== 0) {
|
|
89
|
-
process.stderr.write(`\n❌ Build failed — no PR created/updated.\n\n` +
|
|
90
|
-
`Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-upsert-pr:\n\n` +
|
|
91
|
-
` ${buildCommand}\n\n`);
|
|
92
|
-
process.exit(buildCode);
|
|
93
|
-
}
|
|
94
|
-
process.stdout.write('\n' + SEP + '③ Dashboard + PR\n' + SEP + '\n');
|
|
95
|
-
const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
96
|
-
const body = buildDashboard(repoRoot, true);
|
|
97
|
-
upsertPr(repoRoot, currentBranch, body);
|
|
98
|
-
process.stdout.write('\n✅ Done.\n');
|
|
99
|
-
}
|
|
100
|
-
if (require.main === module) {
|
|
101
|
-
main();
|
|
102
|
-
}
|
|
103
|
-
//# sourceMappingURL=wp-upsert-pr.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wp-upsert-pr.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/wp-upsert-pr.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 { loadAndValidate, WEBPIECES_TMP_DIR } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { runConfiguredBuildGate, resolveBuildCommand } from './workflow/build-affected';\nimport { runGitChecked } from './workflow/git-exec';\nimport {\n computeGateResults,\n countAddedDisables,\n renderDashboard,\n DashboardInput,\n} from '../dashboard/dashboard';\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\n// Step A — bring the branch up to date with main via the 3-point engine (child process, so\n// its conflict handback / guard interplay is unaffected by this command's hook context).\nfunction runUpdateFromMain(): void {\n process.stdout.write('\\n' + SEP + '① Updating branch from main\\n' + SEP + '\\n');\n const result = spawnSync('pnpm', ['wp-git-update'], { stdio: 'inherit' });\n if (result.status === 2) {\n process.stdout.write('\\n⏸️ Conflicts — resolve them, run pnpm wp-git-merge-complete, then re-run pnpm wp-upsert-pr.\\n');\n process.exit(2);\n }\n if (result.status !== 0) {\n process.stderr.write('\\n❌ Branch update failed — see output above.\\n');\n process.exit(result.status ?? 1);\n }\n}\n\nfunction 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\nfunction buildDashboard(repoRoot: string, buildPassed: boolean): 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, '');\n return renderDashboard(input);\n}\n\nfunction upsertPr(repoRoot: string, currentBranch: string, body: string): void {\n const prDir = path.join(repoRoot, WEBPIECES_TMP_DIR, `pr-${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', currentBranch, '--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', currentBranch, '--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', currentBranch, '--auto', '--squash'], { stdio: 'inherit' });\n}\n\nexport function main(): void {\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n\n runUpdateFromMain();\n ensurePushed(execSync('git branch --show-current', { encoding: 'utf8' }).trim());\n\n const buildCommand = resolveBuildCommand(repoRoot);\n process.stdout.write('\\n' + SEP + '② Build gate (nx affected)\\n' + SEP + '\\n');\n process.stdout.write(\n `This gate runs the build command below. To get it passing BEFORE this command runs it,\\n` +\n `run the SAME command yourself first and fix everything it reports:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n const buildCode = runConfiguredBuildGate(repoRoot);\n if (buildCode !== 0) {\n process.stderr.write(\n `\\n❌ Build failed — no PR created/updated.\\n\\n` +\n `Run THIS exact command to reproduce and fix all errors, then re-run pnpm wp-upsert-pr:\\n\\n` +\n ` ${buildCommand}\\n\\n`,\n );\n process.exit(buildCode);\n }\n\n process.stdout.write('\\n' + SEP + '③ Dashboard + PR\\n' + SEP + '\\n');\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const body = buildDashboard(repoRoot, true);\n upsertPr(repoRoot, currentBranch, body);\n process.stdout.write('\\n✅ Done.\\n');\n}\n\nif (require.main === module) {\n main();\n}\n"]}
|
|
File without changes
|
|
File without changes
|