@webpieces/pr-gate 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +31 -0
- package/src/dashboard/dashboard.d.ts +27 -0
- package/src/dashboard/dashboard.js +155 -0
- package/src/dashboard/dashboard.js.map +1 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +17 -0
- package/src/index.js.map +1 -0
- package/src/scripts/git-gatherInfo.d.ts +2 -0
- package/src/scripts/git-gatherInfo.js +98 -0
- package/src/scripts/git-gatherInfo.js.map +1 -0
- package/src/scripts/git-mergeComplete.d.ts +2 -0
- package/src/scripts/git-mergeComplete.js +77 -0
- package/src/scripts/git-mergeComplete.js.map +1 -0
- package/src/scripts/git-updateFromMain.d.ts +2 -0
- package/src/scripts/git-updateFromMain.js +157 -0
- package/src/scripts/git-updateFromMain.js.map +1 -0
- package/src/scripts/workflow/build-affected.d.ts +7 -0
- package/src/scripts/workflow/build-affected.js +22 -0
- package/src/scripts/workflow/build-affected.js.map +1 -0
- package/src/scripts/workflow/cleanTmp.d.ts +1 -0
- package/src/scripts/workflow/cleanTmp.js +57 -0
- package/src/scripts/workflow/cleanTmp.js.map +1 -0
- package/src/scripts/workflow/git-exec.d.ts +6 -0
- package/src/scripts/workflow/git-exec.js +17 -0
- package/src/scripts/workflow/git-exec.js.map +1 -0
- package/src/scripts/workflow/git-findForkPoint.d.ts +2 -0
- package/src/scripts/workflow/git-findForkPoint.js +120 -0
- package/src/scripts/workflow/git-findForkPoint.js.map +1 -0
- package/src/scripts/workflow/git-readAiBranchName.d.ts +2 -0
- package/src/scripts/workflow/git-readAiBranchName.js +20 -0
- package/src/scripts/workflow/git-readAiBranchName.js.map +1 -0
- package/src/scripts/workflow/git-validateUpToDate.d.ts +1 -0
- package/src/scripts/workflow/git-validateUpToDate.js +72 -0
- package/src/scripts/workflow/git-validateUpToDate.js.map +1 -0
- package/src/scripts/workflow/merge-state.d.ts +27 -0
- package/src/scripts/workflow/merge-state.js +88 -0
- package/src/scripts/workflow/merge-state.js.map +1 -0
- package/src/scripts/wp-build-affected.d.ts +2 -0
- package/src/scripts/wp-build-affected.js +20 -0
- package/src/scripts/wp-build-affected.js.map +1 -0
- package/src/scripts/wp-upsert-pr.d.ts +2 -0
- package/src/scripts/wp-upsert-pr.js +98 -0
- package/src/scripts/wp-upsert-pr.js.map +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webpieces/pr-gate",
|
|
3
|
+
"version": "0.0.0",
|
|
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
|
+
"type": "commonjs",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"wp-git-update": "./src/scripts/git-updateFromMain.js",
|
|
9
|
+
"wp-git-gather": "./src/scripts/git-gatherInfo.js",
|
|
10
|
+
"wp-git-merge-complete": "./src/scripts/git-mergeComplete.js",
|
|
11
|
+
"wp-build-affected": "./src/scripts/wp-build-affected.js",
|
|
12
|
+
"wp-upsert-pr": "./src/scripts/wp-upsert-pr.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"src/**/*"
|
|
16
|
+
],
|
|
17
|
+
"author": "Dean Hiller",
|
|
18
|
+
"license": "Apache-2.0",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/deanhiller/webpieces-ts.git",
|
|
22
|
+
"directory": "packages/tooling/pr-gate"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@webpieces/rules-config": "0.3.0"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"types": "./src/index.d.ts"
|
|
31
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { GateDefinition } from '@webpieces/rules-config';
|
|
2
|
+
export declare class GateResult {
|
|
3
|
+
name: string;
|
|
4
|
+
severity: string;
|
|
5
|
+
matchedFiles: string[];
|
|
6
|
+
constructor(name: string, severity: string, matchedFiles: string[]);
|
|
7
|
+
}
|
|
8
|
+
export declare function computeGateResults(gates: GateDefinition[], changedFiles: string[]): GateResult[];
|
|
9
|
+
export declare class DisableCounts {
|
|
10
|
+
webpiecesCount: number;
|
|
11
|
+
eslintCount: number;
|
|
12
|
+
webpiecesRules: string[];
|
|
13
|
+
constructor(webpiecesCount: number, eslintCount: number, webpiecesRules: string[]);
|
|
14
|
+
}
|
|
15
|
+
export declare function countAddedDisables(patch: string): DisableCounts;
|
|
16
|
+
export declare class DashboardInput {
|
|
17
|
+
title: string;
|
|
18
|
+
gateResults: GateResult[];
|
|
19
|
+
disables: DisableCounts;
|
|
20
|
+
buildPassed: boolean;
|
|
21
|
+
forkPoint: string;
|
|
22
|
+
featureHead: string;
|
|
23
|
+
mainHead: string;
|
|
24
|
+
summary: string;
|
|
25
|
+
constructor(title: string, gateResults: GateResult[], disables: DisableCounts, buildPassed: boolean, forkPoint: string, featureHead: string, mainHead: string, summary: string);
|
|
26
|
+
}
|
|
27
|
+
export declare function renderDashboard(input: DashboardInput): string;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DashboardInput = exports.DisableCounts = exports.GateResult = void 0;
|
|
4
|
+
exports.computeGateResults = computeGateResults;
|
|
5
|
+
exports.countAddedDisables = countAddedDisables;
|
|
6
|
+
exports.renderDashboard = renderDashboard;
|
|
7
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
8
|
+
// Self-contained glob matcher (** , * , ?) so pr-gate needs no extra runtime dependency.
|
|
9
|
+
function globToRegex(pattern) {
|
|
10
|
+
let re = '';
|
|
11
|
+
let i = 0;
|
|
12
|
+
while (i < pattern.length) {
|
|
13
|
+
const ch = pattern[i];
|
|
14
|
+
if (ch === '*' && pattern[i + 1] === '*') {
|
|
15
|
+
re += '.*';
|
|
16
|
+
i += 2;
|
|
17
|
+
if (pattern[i] === '/')
|
|
18
|
+
i += 1;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (ch === '*') {
|
|
22
|
+
re += '[^/]*';
|
|
23
|
+
i += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (ch === '?') {
|
|
27
|
+
re += '[^/]';
|
|
28
|
+
i += 1;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if ('.+^$(){}|[]\\'.includes(ch)) {
|
|
32
|
+
re += '\\' + ch;
|
|
33
|
+
i += 1;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
re += ch;
|
|
37
|
+
i += 1;
|
|
38
|
+
}
|
|
39
|
+
return new RegExp('^' + re + '$');
|
|
40
|
+
}
|
|
41
|
+
function matchesAny(patterns, file) {
|
|
42
|
+
for (const pattern of patterns) {
|
|
43
|
+
if (globToRegex(pattern).test(file))
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
class GateResult {
|
|
49
|
+
name;
|
|
50
|
+
severity;
|
|
51
|
+
matchedFiles;
|
|
52
|
+
constructor(name, severity, matchedFiles) {
|
|
53
|
+
this.name = name;
|
|
54
|
+
this.severity = severity;
|
|
55
|
+
this.matchedFiles = matchedFiles;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.GateResult = GateResult;
|
|
59
|
+
function computeGateResults(gates, changedFiles) {
|
|
60
|
+
return gates.map((gate) => {
|
|
61
|
+
const matched = changedFiles.filter((file) => matchesAny(gate.patterns, file));
|
|
62
|
+
return new GateResult(gate.name, gate.severity, matched);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
class DisableCounts {
|
|
66
|
+
webpiecesCount;
|
|
67
|
+
eslintCount;
|
|
68
|
+
webpiecesRules;
|
|
69
|
+
constructor(webpiecesCount, eslintCount, webpiecesRules) {
|
|
70
|
+
this.webpiecesCount = webpiecesCount;
|
|
71
|
+
this.eslintCount = eslintCount;
|
|
72
|
+
this.webpiecesRules = webpiecesRules;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.DisableCounts = DisableCounts;
|
|
76
|
+
// Count disables ADDED in this PR by scanning added (`+`) lines of the diff patch. Rule-aware:
|
|
77
|
+
// reports which webpieces rules were disabled, using the canonical RULE_NAMES vocabulary.
|
|
78
|
+
function countAddedDisables(patch) {
|
|
79
|
+
let webpiecesCount = 0;
|
|
80
|
+
let eslintCount = 0;
|
|
81
|
+
const rules = new Set();
|
|
82
|
+
const allRuleTokens = Object.keys(rules_config_1.RULE_NAMES).map((key) => rules_config_1.RULE_NAMES[key]);
|
|
83
|
+
for (const line of patch.split('\n')) {
|
|
84
|
+
if (!line.startsWith('+') || line.startsWith('+++'))
|
|
85
|
+
continue;
|
|
86
|
+
if (line.includes(rules_config_1.WEBPIECES_DISABLE)) {
|
|
87
|
+
webpiecesCount += 1;
|
|
88
|
+
for (const token of allRuleTokens) {
|
|
89
|
+
if (line.includes(token))
|
|
90
|
+
rules.add(token);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (line.includes('eslint-disable'))
|
|
94
|
+
eslintCount += 1;
|
|
95
|
+
}
|
|
96
|
+
return new DisableCounts(webpiecesCount, eslintCount, Array.from(rules).sort());
|
|
97
|
+
}
|
|
98
|
+
class DashboardInput {
|
|
99
|
+
title;
|
|
100
|
+
gateResults;
|
|
101
|
+
disables;
|
|
102
|
+
buildPassed;
|
|
103
|
+
forkPoint;
|
|
104
|
+
featureHead;
|
|
105
|
+
mainHead;
|
|
106
|
+
summary;
|
|
107
|
+
constructor(title, gateResults, disables, buildPassed, forkPoint, featureHead, mainHead, summary) {
|
|
108
|
+
this.title = title;
|
|
109
|
+
this.gateResults = gateResults;
|
|
110
|
+
this.disables = disables;
|
|
111
|
+
this.buildPassed = buildPassed;
|
|
112
|
+
this.forkPoint = forkPoint;
|
|
113
|
+
this.featureHead = featureHead;
|
|
114
|
+
this.mainHead = mainHead;
|
|
115
|
+
this.summary = summary;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.DashboardInput = DashboardInput;
|
|
119
|
+
function gateLine(result) {
|
|
120
|
+
if (result.matchedFiles.length === 0)
|
|
121
|
+
return `**${result.name}:** 🟢 No`;
|
|
122
|
+
const emoji = result.severity === 'block' ? '🔴' : '🟡';
|
|
123
|
+
return `**${result.name}:** ${emoji} Yes (${result.matchedFiles.length} file(s))`;
|
|
124
|
+
}
|
|
125
|
+
function disableLine(disables) {
|
|
126
|
+
if (disables.webpiecesCount === 0)
|
|
127
|
+
return '**Webpieces Disables Added:** 🟢 No';
|
|
128
|
+
const which = disables.webpiecesRules.length > 0 ? ` — ${disables.webpiecesRules.join(', ')}` : '';
|
|
129
|
+
return `**Webpieces Disables Added:** 🟡 ${disables.webpiecesCount} line(s)${which}`;
|
|
130
|
+
}
|
|
131
|
+
function renderDashboard(input) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
lines.push('## 🚦 PR Gate Dashboard');
|
|
134
|
+
lines.push('');
|
|
135
|
+
lines.push(`**Build (nx affected):** ${input.buildPassed ? '🟢 Passed' : '🔴 Failed'}`);
|
|
136
|
+
for (const result of input.gateResults)
|
|
137
|
+
lines.push(gateLine(result));
|
|
138
|
+
lines.push(disableLine(input.disables));
|
|
139
|
+
const eslintEmoji = input.disables.eslintCount === 0 ? '🟢 No' : `🟡 ${input.disables.eslintCount} line(s)`;
|
|
140
|
+
lines.push(`**ESLint Disables Added:** ${eslintEmoji}`);
|
|
141
|
+
lines.push('');
|
|
142
|
+
if (input.summary.trim() !== '') {
|
|
143
|
+
lines.push('### Summary');
|
|
144
|
+
lines.push(input.summary.trim());
|
|
145
|
+
lines.push('');
|
|
146
|
+
}
|
|
147
|
+
lines.push('### 🔍 3-Point Hash Points');
|
|
148
|
+
lines.push(`- Fork point (A): \`${input.forkPoint.slice(0, 12)}\``);
|
|
149
|
+
lines.push(`- Feature HEAD (B): \`${input.featureHead.slice(0, 12)}\``);
|
|
150
|
+
lines.push(`- Main HEAD (C): \`${input.mainHead.slice(0, 12)}\``);
|
|
151
|
+
lines.push('');
|
|
152
|
+
lines.push('<sub>🤖 Generated by `pnpm wp-upsert-pr` (build ran via nx affected — not self-attested).</sub>');
|
|
153
|
+
return lines.join('\n');
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +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"]}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { GateResult, DisableCounts, DashboardInput, computeGateResults, countAddedDisables, renderDashboard, } from './dashboard/dashboard';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// @webpieces/pr-gate — gated PR system.
|
|
3
|
+
//
|
|
4
|
+
// Public surface is intentionally small for now. The package mainly ships bin
|
|
5
|
+
// commands (wp-git-update, wp-git-gather, wp-git-merge-complete, and — added in a
|
|
6
|
+
// later phase — wp-upsert-pr / wp-build-affected). Shared library exports (dashboard
|
|
7
|
+
// gate computation, etc.) are added as the dashboard lands.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.renderDashboard = exports.countAddedDisables = exports.computeGateResults = exports.DashboardInput = exports.DisableCounts = exports.GateResult = void 0;
|
|
10
|
+
var dashboard_1 = require("./dashboard/dashboard");
|
|
11
|
+
Object.defineProperty(exports, "GateResult", { enumerable: true, get: function () { return dashboard_1.GateResult; } });
|
|
12
|
+
Object.defineProperty(exports, "DisableCounts", { enumerable: true, get: function () { return dashboard_1.DisableCounts; } });
|
|
13
|
+
Object.defineProperty(exports, "DashboardInput", { enumerable: true, get: function () { return dashboard_1.DashboardInput; } });
|
|
14
|
+
Object.defineProperty(exports, "computeGateResults", { enumerable: true, get: function () { return dashboard_1.computeGateResults; } });
|
|
15
|
+
Object.defineProperty(exports, "countAddedDisables", { enumerable: true, get: function () { return dashboard_1.countAddedDisables; } });
|
|
16
|
+
Object.defineProperty(exports, "renderDashboard", { enumerable: true, get: function () { return dashboard_1.renderDashboard; } });
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/pr-gate/src/index.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,EAAE;AACF,8EAA8E;AAC9E,kFAAkF;AAClF,qFAAqF;AACrF,4DAA4D;;;AAE5D,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// Public surface is intentionally small for now. The package mainly ships bin\n// commands (wp-git-update, wp-git-gather, wp-git-merge-complete, and — added in a\n// later phase — wp-upsert-pr / wp-build-affected). Shared library exports (dashboard\n// gate computation, etc.) are added as the dashboard lands.\n\nexport {\n GateResult,\n DisableCounts,\n DashboardInput,\n computeGateResults,\n countAddedDisables,\n renderDashboard,\n} from './dashboard/dashboard';\n"]}
|
|
@@ -0,0 +1,98 @@
|
|
|
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_findForkPoint_1 = require("./workflow/git-findForkPoint");
|
|
11
|
+
const SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
12
|
+
function validateCleanTree(currentBranch) {
|
|
13
|
+
if (currentBranch === 'main') {
|
|
14
|
+
process.stderr.write('❌ Error: Already on main branch. No need to update from main.\n');
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
process.stderr.write(`Current branch: ${currentBranch}\n`);
|
|
18
|
+
const hasDirtyFiles = (0, child_process_1.spawnSync)('git', ['diff-index', '--quiet', 'HEAD', '--']).status !== 0;
|
|
19
|
+
if (!hasDirtyFiles)
|
|
20
|
+
return;
|
|
21
|
+
const changedFiles = (0, child_process_1.execSync)('git diff --name-only HEAD', { encoding: 'utf8' }).trim();
|
|
22
|
+
process.stderr.write('\n');
|
|
23
|
+
process.stderr.write(SEP);
|
|
24
|
+
process.stderr.write('❌ ERROR: You have uncommitted changes\n');
|
|
25
|
+
process.stderr.write(SEP);
|
|
26
|
+
process.stderr.write('\n');
|
|
27
|
+
process.stderr.write('Please commit or stash your changes before updating from main.\n');
|
|
28
|
+
process.stderr.write('\n');
|
|
29
|
+
process.stderr.write('Files with changes:\n');
|
|
30
|
+
process.stderr.write(changedFiles + '\n');
|
|
31
|
+
process.stderr.write('\n');
|
|
32
|
+
process.stderr.write('\x1b[1;31mTo commit your changes, run:\n');
|
|
33
|
+
process.stderr.write(' git add -A && git commit -m "your message"\x1b[0m\n');
|
|
34
|
+
process.stderr.write('\n');
|
|
35
|
+
process.stderr.write('Or to stash them temporarily:\n');
|
|
36
|
+
process.stderr.write(' git stash\n');
|
|
37
|
+
process.stderr.write(' pnpm wp-git-update\n');
|
|
38
|
+
process.stderr.write(' git stash pop\n');
|
|
39
|
+
process.stderr.write('\n');
|
|
40
|
+
process.stderr.write(SEP);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
function printHashPoints(hashes, currentBranch, mergeDir) {
|
|
44
|
+
process.stderr.write('📍 The 3 Hash Points:\n');
|
|
45
|
+
process.stderr.write(` 1. Fork point (A): ${hashes.hashForkPoint}\n`);
|
|
46
|
+
process.stderr.write(` (where ${currentBranch} diverged from main)\n`);
|
|
47
|
+
process.stderr.write('\n');
|
|
48
|
+
process.stderr.write(` 2. Feature HEAD (B): ${hashes.hashFeatureHead}\n`);
|
|
49
|
+
process.stderr.write(` (tip of ${currentBranch})\n`);
|
|
50
|
+
process.stderr.write('\n');
|
|
51
|
+
process.stderr.write(` 3. Main HEAD (C): ${hashes.hashMainHead}\n`);
|
|
52
|
+
process.stderr.write(' (current origin/main)\n');
|
|
53
|
+
process.stderr.write('\n');
|
|
54
|
+
process.stderr.write(`Merge directory: ${mergeDir}\n`);
|
|
55
|
+
process.stderr.write('\n');
|
|
56
|
+
}
|
|
57
|
+
async function main() {
|
|
58
|
+
const currentBranch = (0, child_process_1.execSync)('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
59
|
+
const featureName = (0, git_readAiBranchName_1.getFeatureName)();
|
|
60
|
+
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
61
|
+
const mergeDir = path.join(repoRoot, 'webpiecesTmp', `merge-${featureName}`);
|
|
62
|
+
fs.mkdirSync(mergeDir, { recursive: true });
|
|
63
|
+
validateCleanTree(currentBranch);
|
|
64
|
+
process.stderr.write('\n');
|
|
65
|
+
process.stderr.write(SEP);
|
|
66
|
+
process.stderr.write('📍 Gathering Merge Context\n');
|
|
67
|
+
process.stderr.write(SEP);
|
|
68
|
+
process.stderr.write('\n');
|
|
69
|
+
process.stderr.write('Fetching latest changes from origin/main...\n');
|
|
70
|
+
(0, child_process_1.spawnSync)('git', ['fetch', 'origin', 'main'], { stdio: 'inherit' });
|
|
71
|
+
await (0, git_findForkPoint_1.findForkPoint)('merge');
|
|
72
|
+
const hashesFile = path.join(mergeDir, 'updatemain-hashes.json');
|
|
73
|
+
const hashes = JSON.parse(fs.readFileSync(hashesFile, 'utf8'));
|
|
74
|
+
printHashPoints(hashes, currentBranch, mergeDir);
|
|
75
|
+
if (hashes.hashForkPoint === hashes.hashMainHead) {
|
|
76
|
+
process.stderr.write(SEP);
|
|
77
|
+
process.stderr.write('✅ Already up to date with main!\n');
|
|
78
|
+
process.stderr.write(SEP);
|
|
79
|
+
process.stderr.write('\n');
|
|
80
|
+
process.stderr.write('Your branch has not diverged from main.\n');
|
|
81
|
+
process.stderr.write('There are no new changes from main to merge.\n');
|
|
82
|
+
process.stderr.write('\n');
|
|
83
|
+
process.stderr.write(SEP);
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
process.stderr.write('Main has advanced. Merge will be needed.\n');
|
|
87
|
+
process.stderr.write('\n');
|
|
88
|
+
process.stderr.write(SEP);
|
|
89
|
+
process.stderr.write('\n');
|
|
90
|
+
}
|
|
91
|
+
if (require.main === module) {
|
|
92
|
+
main().catch((err) => {
|
|
93
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
94
|
+
process.stderr.write(message + '\n');
|
|
95
|
+
process.exit(1);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=git-gatherInfo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-gatherInfo.js","sourceRoot":"","sources":["../../../../../../packages/tooling/pr-gate/src/scripts/git-gatherInfo.ts"],"names":[],"mappings":"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","sourcesContent":["#!/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 { findForkPoint } from './workflow/git-findForkPoint';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\n';\n\ninterface HashPoints {\n hashForkPoint: string;\n hashFeatureHead: string;\n hashMainHead: string;\n}\n\nfunction validateCleanTree(currentBranch: string): void {\n if (currentBranch === 'main') {\n process.stderr.write('❌ Error: Already on main branch. No need to update from main.\\n');\n process.exit(1);\n }\n\n process.stderr.write(`Current branch: ${currentBranch}\\n`);\n\n const hasDirtyFiles = spawnSync('git', ['diff-index', '--quiet', 'HEAD', '--']).status !== 0;\n if (!hasDirtyFiles) return;\n\n const changedFiles = execSync('git diff --name-only HEAD', { encoding: 'utf8' }).trim();\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.stderr.write('❌ ERROR: You have uncommitted changes\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n process.stderr.write('Please commit or stash your changes before updating from main.\\n');\n process.stderr.write('\\n');\n process.stderr.write('Files with changes:\\n');\n process.stderr.write(changedFiles + '\\n');\n process.stderr.write('\\n');\n process.stderr.write('\\x1b[1;31mTo commit your changes, run:\\n');\n process.stderr.write(' git add -A && git commit -m \"your message\"\\x1b[0m\\n');\n process.stderr.write('\\n');\n process.stderr.write('Or to stash them temporarily:\\n');\n process.stderr.write(' git stash\\n');\n process.stderr.write(' pnpm wp-git-update\\n');\n process.stderr.write(' git stash pop\\n');\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.exit(1);\n}\n\nfunction printHashPoints(hashes: HashPoints, currentBranch: string, mergeDir: string): void {\n process.stderr.write('📍 The 3 Hash Points:\\n');\n process.stderr.write(` 1. Fork point (A): ${hashes.hashForkPoint}\\n`);\n process.stderr.write(` (where ${currentBranch} diverged from main)\\n`);\n process.stderr.write('\\n');\n process.stderr.write(` 2. Feature HEAD (B): ${hashes.hashFeatureHead}\\n`);\n process.stderr.write(` (tip of ${currentBranch})\\n`);\n process.stderr.write('\\n');\n process.stderr.write(` 3. Main HEAD (C): ${hashes.hashMainHead}\\n`);\n process.stderr.write(' (current origin/main)\\n');\n process.stderr.write('\\n');\n process.stderr.write(`Merge directory: ${mergeDir}\\n`);\n process.stderr.write('\\n');\n}\n\nexport async function main(): Promise<void> {\n const currentBranch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n const featureName = getFeatureName();\n const repoRoot = execSync('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();\n const mergeDir = path.join(repoRoot, 'webpiecesTmp', `merge-${featureName}`);\n fs.mkdirSync(mergeDir, { recursive: true });\n\n validateCleanTree(currentBranch);\n\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.stderr.write('📍 Gathering Merge Context\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n process.stderr.write('Fetching latest changes from origin/main...\\n');\n spawnSync('git', ['fetch', 'origin', 'main'], { stdio: 'inherit' });\n\n await findForkPoint('merge');\n\n const hashesFile = path.join(mergeDir, 'updatemain-hashes.json');\n const hashes = JSON.parse(fs.readFileSync(hashesFile, 'utf8')) as HashPoints;\n\n printHashPoints(hashes, currentBranch, mergeDir);\n\n if (hashes.hashForkPoint === hashes.hashMainHead) {\n process.stderr.write(SEP);\n process.stderr.write('✅ Already up to date with main!\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n process.stderr.write('Your branch has not diverged from main.\\n');\n process.stderr.write('There are no new changes from main to merge.\\n');\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.exit(0);\n }\n\n process.stderr.write('Main has advanced. Merge will be needed.\\n');\n process.stderr.write('\\n');\n process.stderr.write(SEP);\n process.stderr.write('\\n');\n}\n\nif (require.main === module) {\n main().catch((err) => {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(message + '\\n');\n process.exit(1);\n });\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
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 SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n';
|
|
15
|
+
async function main() {
|
|
16
|
+
const repoRoot = (0, child_process_1.execSync)('git rev-parse --show-toplevel', { encoding: 'utf8' }).trim();
|
|
17
|
+
const featureName = (0, git_readAiBranchName_1.getFeatureName)();
|
|
18
|
+
const mergeDir = (0, merge_state_1.mergeDirFor)(repoRoot, featureName);
|
|
19
|
+
const marker = (0, merge_state_1.readMergeMarker)(mergeDir);
|
|
20
|
+
if (!marker) {
|
|
21
|
+
process.stderr.write('❌ No merge in progress (no marker found). Nothing to complete.\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (marker.validated) {
|
|
26
|
+
process.stdout.write('✅ Merge already validated. Run pnpm wp-git-update (or wp-upsert-pr) to finalize.\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
process.stdout.write('\n' + SEP + '🔎 Validating Merge Resolution\n' + SEP + '\n');
|
|
30
|
+
// 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).
|
|
31
|
+
const scan = (0, merge_state_1.scanConflictMarkers)(repoRoot, marker.conflictedFiles);
|
|
32
|
+
if (!scan.clean) {
|
|
33
|
+
process.stderr.write('❌ Unresolved conflict markers (<<<<<<< / ======= / >>>>>>>) remain in:\n');
|
|
34
|
+
for (const file of scan.filesWithMarkers)
|
|
35
|
+
process.stderr.write(` - ${file}\n`);
|
|
36
|
+
process.stderr.write('\nResolve them, then re-run: pnpm wp-git-merge-complete\n');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// 2. Ensure git itself has no remaining unmerged entries.
|
|
41
|
+
const unmerged = (0, child_process_1.execSync)('git diff --name-only --diff-filter=U', { encoding: 'utf8' }).trim();
|
|
42
|
+
if (unmerged !== '') {
|
|
43
|
+
process.stderr.write('❌ Git still reports unmerged files:\n' + unmerged + '\n');
|
|
44
|
+
process.stderr.write('\nResolve and `git add` them, then re-run: pnpm wp-git-merge-complete\n');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
process.stdout.write('✅ No conflict markers in resolved files.\n');
|
|
49
|
+
(0, git_exec_1.runGitChecked)(['add', '-A'], 'Failed to stage resolved files');
|
|
50
|
+
// 3. Build gate (authoritative). nx affected — fast on a big monorepo.
|
|
51
|
+
const buildCode = (0, build_affected_1.runBuildAffected)(repoRoot, (0, rules_config_1.loadPrGateConfig)(repoRoot).buildCommand);
|
|
52
|
+
if (buildCode !== 0) {
|
|
53
|
+
process.stderr.write('\n❌ Build failed. Fix the build, then re-run: pnpm wp-git-merge-complete\n');
|
|
54
|
+
process.exit(buildCode);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
process.stdout.write('\n✅ Build passed.\n');
|
|
58
|
+
// 4. Commit the resolved squash merge.
|
|
59
|
+
const nothingStaged = (0, child_process_1.spawnSync)('git', ['diff-index', '--quiet', '--cached', 'HEAD', '--']).status === 0;
|
|
60
|
+
if (!nothingStaged) {
|
|
61
|
+
(0, git_exec_1.runGitChecked)(['commit', '-m', `Squash merge of ${marker.currentBranch} (conflicts resolved)`], 'Failed to commit resolved merge');
|
|
62
|
+
}
|
|
63
|
+
// 5. Flip the marker to validated + drop the proof file → unblocks the guard.
|
|
64
|
+
marker.validated = true;
|
|
65
|
+
(0, merge_state_1.writeMergeMarker)(mergeDir, marker);
|
|
66
|
+
fs.writeFileSync(path.join(mergeDir, 'conflicts-resolved'), '');
|
|
67
|
+
process.stdout.write('\n' + SEP + '✅ Merge validated and committed\n' + SEP + '\n');
|
|
68
|
+
process.stdout.write('Finalize & push by running: pnpm wp-upsert-pr (or pnpm wp-git-update)\n\n');
|
|
69
|
+
}
|
|
70
|
+
if (require.main === module) {
|
|
71
|
+
main().catch((err) => {
|
|
72
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
73
|
+
process.stderr.write(message + '\n');
|
|
74
|
+
process.exit(1);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=git-mergeComplete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
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 { loadPrGateConfig } from '@webpieces/rules-config';\nimport { getFeatureName } from './workflow/git-readAiBranchName';\nimport { runGitChecked } from './workflow/git-exec';\nimport { runBuildAffected } from './workflow/build-affected';\nimport {\n mergeDirFor,\n readMergeMarker,\n writeMergeMarker,\n scanConflictMarkers,\n} from './workflow/merge-state';\n\nconst SEP = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\\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 // 1. Scoped conflict-marker scan (only the conflicted files — O(conflicts), not O(repo)).\n const scan = scanConflictMarkers(repoRoot, marker.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 return;\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 return;\n }\n\n process.stdout.write('✅ No conflict markers in resolved files.\\n');\n runGitChecked(['add', '-A'], 'Failed to stage resolved files');\n\n // 3. Build gate (authoritative). nx affected — fast on a big monorepo.\n const buildCode = runBuildAffected(repoRoot, loadPrGateConfig(repoRoot).buildCommand);\n if (buildCode !== 0) {\n process.stderr.write('\\n❌ Build failed. Fix the build, then re-run: pnpm wp-git-merge-complete\\n');\n process.exit(buildCode);\n return;\n }\n process.stdout.write('\\n✅ Build passed.\\n');\n\n // 4. 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 // 5. 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"]}
|