@rtrentjones/greenlight 0.2.13 → 0.2.15
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/dist/bin.js
CHANGED
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
loadConfig,
|
|
6
6
|
resolveUrl,
|
|
7
7
|
verifyAll
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AL6IKVZE.js";
|
|
9
9
|
import "./chunk-ADS6BJJ5.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-N3IKUCSF.js";
|
|
11
11
|
import "./chunk-KP3Y6WRU.js";
|
|
12
12
|
import "./chunk-UXHHLEYO.js";
|
|
13
13
|
import "./chunk-6N7MD6FR.js";
|
|
@@ -413,7 +413,7 @@ function tokensForTool(tool) {
|
|
|
413
413
|
}
|
|
414
414
|
|
|
415
415
|
// src/version.ts
|
|
416
|
-
var MODULE_REF = "v0.2.
|
|
416
|
+
var MODULE_REF = "v0.2.15";
|
|
417
417
|
var MODULE_SOURCE_BASE = "git::https://github.com/RTrentJones/greenlight.git//infra/modules";
|
|
418
418
|
function moduleSource(module, ref = MODULE_REF) {
|
|
419
419
|
return `${MODULE_SOURCE_BASE}/${module}?ref=${ref}`;
|
|
@@ -2414,16 +2414,24 @@ function git(repoDir, args) {
|
|
|
2414
2414
|
function gitOut(repoDir, args) {
|
|
2415
2415
|
return execFileSync5("git", args, { cwd: repoDir, encoding: "utf8" }).trim();
|
|
2416
2416
|
}
|
|
2417
|
+
function resolveRef(repoDir, branch) {
|
|
2418
|
+
for (const ref of [branch, `origin/${branch}`]) {
|
|
2419
|
+
try {
|
|
2420
|
+
git(repoDir, ["rev-parse", "--verify", "--quiet", ref]);
|
|
2421
|
+
return ref;
|
|
2422
|
+
} catch {
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
return null;
|
|
2426
|
+
}
|
|
2417
2427
|
function canPromote(repoDir, from = "develop", to = "main") {
|
|
2418
|
-
const
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
run2(["rev-parse", "--verify", "--quiet", to]);
|
|
2422
|
-
} catch {
|
|
2428
|
+
const fromRef = resolveRef(repoDir, from);
|
|
2429
|
+
const toRef = resolveRef(repoDir, to);
|
|
2430
|
+
if (!fromRef || !toRef) {
|
|
2423
2431
|
return { canPromote: false, reason: `branch "${from}" or "${to}" not found in ${repoDir}` };
|
|
2424
2432
|
}
|
|
2425
2433
|
try {
|
|
2426
|
-
|
|
2434
|
+
git(repoDir, ["merge-base", "--is-ancestor", toRef, fromRef]);
|
|
2427
2435
|
return { canPromote: true, reason: `"${to}" can fast-forward to "${from}"` };
|
|
2428
2436
|
} catch {
|
|
2429
2437
|
return {
|
|
@@ -2437,25 +2445,25 @@ function promote(repoDir, opts = {}) {
|
|
|
2437
2445
|
const to = opts.to ?? "main";
|
|
2438
2446
|
const check = canPromote(repoDir, from, to);
|
|
2439
2447
|
if (!check.canPromote) return { promoted: false, from, to, reason: check.reason };
|
|
2440
|
-
const
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
if (original && original !== "HEAD" && original !== to) {
|
|
2448
|
+
const fromRef = resolveRef(repoDir, from);
|
|
2449
|
+
const fromCommit = gitOut(repoDir, ["rev-parse", fromRef]);
|
|
2450
|
+
const current = gitOut(repoDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
2451
|
+
if (opts.push) {
|
|
2452
|
+
git(repoDir, ["push", "origin", `${fromCommit}:refs/heads/${to}`]);
|
|
2453
|
+
if (current !== to) {
|
|
2447
2454
|
try {
|
|
2448
|
-
git(repoDir, ["
|
|
2455
|
+
git(repoDir, ["update-ref", `refs/heads/${to}`, fromCommit]);
|
|
2449
2456
|
} catch {
|
|
2450
2457
|
}
|
|
2451
2458
|
}
|
|
2459
|
+
return { promoted: true, from, to, reason: `"${to}" fast-forwarded to "${from}" and pushed` };
|
|
2452
2460
|
}
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
to
|
|
2457
|
-
|
|
2458
|
-
};
|
|
2461
|
+
if (current === to) {
|
|
2462
|
+
git(repoDir, ["merge", "--ff-only", fromRef]);
|
|
2463
|
+
} else {
|
|
2464
|
+
git(repoDir, ["update-ref", `refs/heads/${to}`, fromCommit]);
|
|
2465
|
+
}
|
|
2466
|
+
return { promoted: true, from, to, reason: `"${to}" fast-forwarded to "${from}"` };
|
|
2459
2467
|
}
|
|
2460
2468
|
|
|
2461
2469
|
// src/commands/promote.ts
|
|
@@ -233,8 +233,8 @@ async function verify(baseUrl, spec, opts) {
|
|
|
233
233
|
return verifyMcp2(baseUrl, spec);
|
|
234
234
|
}
|
|
235
235
|
case "playwright": {
|
|
236
|
-
const { verifyPlaywright: verifyPlaywright2 } = await import("./playwright-
|
|
237
|
-
return verifyPlaywright2(baseUrl, spec);
|
|
236
|
+
const { verifyPlaywright: verifyPlaywright2 } = await import("./playwright-SGRK3I4I.js");
|
|
237
|
+
return verifyPlaywright2(baseUrl, spec, opts?.toolDir ?? process.cwd());
|
|
238
238
|
}
|
|
239
239
|
case "test": {
|
|
240
240
|
const { verifyTest: verifyTest2 } = await import("./test-7GMOU7I5.js");
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
msg,
|
|
3
|
+
report
|
|
4
|
+
} from "./chunk-QFKE5JKC.js";
|
|
5
|
+
|
|
6
|
+
// ../packages/verify/src/playwright.ts
|
|
7
|
+
import { spawnSync } from "child_process";
|
|
8
|
+
async function verifyPlaywright(baseUrl, spec, toolDir = process.cwd()) {
|
|
9
|
+
const checks = [];
|
|
10
|
+
if (spec.suite) checks.push(runSuite(baseUrl, spec.suite, toolDir));
|
|
11
|
+
if (spec.renders?.length) checks.push(...await runRenders(baseUrl, spec.renders));
|
|
12
|
+
if (checks.length === 0) {
|
|
13
|
+
checks.push({
|
|
14
|
+
name: "playwright spec",
|
|
15
|
+
pass: false,
|
|
16
|
+
detail: "nothing to run \u2014 set `renders` and/or `suite`"
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return report("playwright", baseUrl, checks);
|
|
20
|
+
}
|
|
21
|
+
function summarize(output) {
|
|
22
|
+
const lines = output.split("\n");
|
|
23
|
+
const hit = lines.find(
|
|
24
|
+
(l) => /\d+\s+(passed|failed|flaky|skipped)|Tests?\s+\d+\s+(passed|failed)|Tests:/.test(l)
|
|
25
|
+
);
|
|
26
|
+
if (hit) return hit.trim();
|
|
27
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
28
|
+
const l = lines[i];
|
|
29
|
+
if (l?.trim()) return l.trim();
|
|
30
|
+
}
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
function runSuite(baseUrl, suite, toolDir) {
|
|
34
|
+
const command = suite.command ?? "pnpm exec playwright test";
|
|
35
|
+
const cwd = suite.cwd ?? toolDir;
|
|
36
|
+
try {
|
|
37
|
+
const res = spawnSync(command, {
|
|
38
|
+
cwd,
|
|
39
|
+
shell: true,
|
|
40
|
+
encoding: "utf8",
|
|
41
|
+
timeout: suite.timeoutMs ?? 6e5,
|
|
42
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
43
|
+
env: {
|
|
44
|
+
...process.env,
|
|
45
|
+
// The deployed URL the suite must target. PLAYWRIGHT_BASE_URL is what a stock
|
|
46
|
+
// playwright.config reads for `use.baseURL`; GREENLIGHT_VERIFY_URL mirrors the rest of the
|
|
47
|
+
// harness so one convention works everywhere.
|
|
48
|
+
PLAYWRIGHT_BASE_URL: baseUrl,
|
|
49
|
+
GREENLIGHT_VERIFY_URL: baseUrl,
|
|
50
|
+
...suite.env
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
if (res.error) {
|
|
54
|
+
return { name: command, pass: false, detail: msg(res.error) };
|
|
55
|
+
}
|
|
56
|
+
const out = `${res.stdout ?? ""}${res.stderr ?? ""}`;
|
|
57
|
+
const summary = summarize(out);
|
|
58
|
+
const pass = res.status === 0;
|
|
59
|
+
return {
|
|
60
|
+
name: `suite: ${command}`,
|
|
61
|
+
pass,
|
|
62
|
+
detail: pass ? summary : `exit ${res.status ?? "signal"}${summary ? ` \u2014 ${summary}` : ""}`
|
|
63
|
+
};
|
|
64
|
+
} catch (e) {
|
|
65
|
+
return { name: command, pass: false, detail: msg(e) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function runRenders(baseUrl, renders) {
|
|
69
|
+
let chromium;
|
|
70
|
+
try {
|
|
71
|
+
({ chromium } = await import("playwright"));
|
|
72
|
+
} catch {
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
name: "playwright available",
|
|
76
|
+
pass: false,
|
|
77
|
+
detail: "playwright not installed \u2014 run `pnpm add playwright && pnpm exec playwright install chromium`"
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
82
|
+
const checks = [];
|
|
83
|
+
let browser;
|
|
84
|
+
try {
|
|
85
|
+
browser = await chromium.launch();
|
|
86
|
+
} catch (e) {
|
|
87
|
+
return [
|
|
88
|
+
{
|
|
89
|
+
name: "launch browser",
|
|
90
|
+
pass: false,
|
|
91
|
+
detail: `${msg(e)} (try \`playwright install chromium\`)`
|
|
92
|
+
}
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
for (const path of renders) {
|
|
97
|
+
const page = await browser.newPage();
|
|
98
|
+
try {
|
|
99
|
+
const res = await page.goto(base + path, { waitUntil: "domcontentloaded" });
|
|
100
|
+
const ok = res?.ok() ?? false;
|
|
101
|
+
const aria = await page.locator("body").ariaSnapshot();
|
|
102
|
+
const nonEmpty = aria.trim().length > 0;
|
|
103
|
+
checks.push({
|
|
104
|
+
name: `renders ${path}`,
|
|
105
|
+
pass: ok && nonEmpty,
|
|
106
|
+
detail: !ok ? `status ${res?.status() ?? "none"}` : nonEmpty ? void 0 : "empty accessibility tree"
|
|
107
|
+
});
|
|
108
|
+
} catch (e) {
|
|
109
|
+
checks.push({ name: `renders ${path}`, pass: false, detail: msg(e) });
|
|
110
|
+
} finally {
|
|
111
|
+
await page.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
} finally {
|
|
115
|
+
await browser.close();
|
|
116
|
+
}
|
|
117
|
+
return checks;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export {
|
|
121
|
+
verifyPlaywright
|
|
122
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
defineConfig,
|
|
3
3
|
defineVerify,
|
|
4
4
|
loadConfig
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AL6IKVZE.js";
|
|
6
6
|
import "./chunk-ADS6BJJ5.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-N3IKUCSF.js";
|
|
8
8
|
import "./chunk-KP3Y6WRU.js";
|
|
9
9
|
import "./chunk-UXHHLEYO.js";
|
|
10
10
|
import "./chunk-6N7MD6FR.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtrentjones/greenlight",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "Greenlight CLI — setup and lifecycle for the harness.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"@anthropic-ai/sdk": "^0.69.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@rtrentjones/greenlight-adapters": "0.2.4",
|
|
35
34
|
"@rtrentjones/greenlight-shared": "0.2.4",
|
|
36
|
-
"@rtrentjones/greenlight-
|
|
37
|
-
"@rtrentjones/greenlight-
|
|
35
|
+
"@rtrentjones/greenlight-loop": "0.2.4",
|
|
36
|
+
"@rtrentjones/greenlight-adapters": "0.2.4",
|
|
37
|
+
"@rtrentjones/greenlight-verify": "0.2.4"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "node scripts/copy-assets.mjs && tsup",
|
package/dist/chunk-WFZTRXBF.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
msg,
|
|
3
|
-
report
|
|
4
|
-
} from "./chunk-QFKE5JKC.js";
|
|
5
|
-
|
|
6
|
-
// ../packages/verify/src/playwright.ts
|
|
7
|
-
async function verifyPlaywright(baseUrl, spec) {
|
|
8
|
-
let chromium;
|
|
9
|
-
try {
|
|
10
|
-
({ chromium } = await import("playwright"));
|
|
11
|
-
} catch {
|
|
12
|
-
return report("playwright", baseUrl, [
|
|
13
|
-
{
|
|
14
|
-
name: "playwright available",
|
|
15
|
-
pass: false,
|
|
16
|
-
detail: "playwright not installed \u2014 run `pnpm add playwright && pnpm exec playwright install chromium`"
|
|
17
|
-
}
|
|
18
|
-
]);
|
|
19
|
-
}
|
|
20
|
-
const base = baseUrl.replace(/\/+$/, "");
|
|
21
|
-
const checks = [];
|
|
22
|
-
let browser;
|
|
23
|
-
try {
|
|
24
|
-
browser = await chromium.launch();
|
|
25
|
-
} catch (e) {
|
|
26
|
-
return report("playwright", baseUrl, [
|
|
27
|
-
{
|
|
28
|
-
name: "launch browser",
|
|
29
|
-
pass: false,
|
|
30
|
-
detail: `${msg(e)} (try \`playwright install chromium\`)`
|
|
31
|
-
}
|
|
32
|
-
]);
|
|
33
|
-
}
|
|
34
|
-
try {
|
|
35
|
-
for (const path of spec.renders) {
|
|
36
|
-
const page = await browser.newPage();
|
|
37
|
-
try {
|
|
38
|
-
const res = await page.goto(base + path, { waitUntil: "domcontentloaded" });
|
|
39
|
-
const ok = res?.ok() ?? false;
|
|
40
|
-
const aria = await page.locator("body").ariaSnapshot();
|
|
41
|
-
const nonEmpty = aria.trim().length > 0;
|
|
42
|
-
checks.push({
|
|
43
|
-
name: `renders ${path}`,
|
|
44
|
-
pass: ok && nonEmpty,
|
|
45
|
-
detail: !ok ? `status ${res?.status() ?? "none"}` : nonEmpty ? void 0 : "empty accessibility tree"
|
|
46
|
-
});
|
|
47
|
-
} catch (e) {
|
|
48
|
-
checks.push({ name: `renders ${path}`, pass: false, detail: msg(e) });
|
|
49
|
-
} finally {
|
|
50
|
-
await page.close();
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
} finally {
|
|
54
|
-
await browser.close();
|
|
55
|
-
}
|
|
56
|
-
return report("playwright", baseUrl, checks);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export {
|
|
60
|
-
verifyPlaywright
|
|
61
|
-
};
|