@rtrentjones/greenlight 0.2.7 → 0.2.8
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/assets/skills/provider-vercel/SKILL.md +16 -0
- package/dist/bin.js +109 -8
- package/package.json +3 -3
|
@@ -26,6 +26,22 @@ Manages the **existing** project (nothing to import — it configures by id):
|
|
|
26
26
|
|
|
27
27
|
The DNS CNAME is the **cloudflare** `tool` module, unproxied (`proxied = false`) → `cname.vercel-dns.com`.
|
|
28
28
|
|
|
29
|
+
## The verify loop — tool-CI on `deployment_status`
|
|
30
|
+
|
|
31
|
+
Because Vercel deploys (not the wrapper), the verify gate runs in the **tool repo's own CI**, not a
|
|
32
|
+
wrapper deploy listener. `greenlight adopt … --target vercel` emits, into the tool repo:
|
|
33
|
+
- **`.github/workflows/greenlight-verify.yml`** — triggers on GitHub's **`deployment_status`** event
|
|
34
|
+
(Vercel posts a deployment + `target_url`); on `state == success` it runs
|
|
35
|
+
`npx @rtrentjones/greenlight verify --url <target_url> --spec verify/<name>.config.ts`. The result
|
|
36
|
+
is a check on the commit — no wrapper round-trip, no dispatch/status PATs (Vercel owns deploy + URL
|
|
37
|
+
+ its own statuses).
|
|
38
|
+
- **`verify/<name>.config.ts`** — a verifyAll array: `api` (deployed URL 200) + `test` (the tool's
|
|
39
|
+
suite) + `agent-web` (LLM drives the live UI), where agent-web is **config-gated on
|
|
40
|
+
`ANTHROPIC_API_KEY`** (omitted when unset → the gate stays green on api + test alone).
|
|
41
|
+
|
|
42
|
+
`greenlight verify --url <url> --spec <path>` is the **manifest-free** mode that makes this work
|
|
43
|
+
without carrying the wrapper's `greenlight.config.ts` into the tool repo.
|
|
44
|
+
|
|
29
45
|
## MCP
|
|
30
46
|
|
|
31
47
|
`.mcp.json` wires `vercel` (hosted, OAuth, read-only). Run `/mcp` and authenticate in the
|
package/dist/bin.js
CHANGED
|
@@ -390,7 +390,7 @@ function tokensForTool(tool) {
|
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
// src/version.ts
|
|
393
|
-
var MODULE_REF = "v0.2.
|
|
393
|
+
var MODULE_REF = "v0.2.8";
|
|
394
394
|
var MODULE_SOURCE_BASE = "git::https://github.com/RTrentJones/greenlight.git//infra/modules";
|
|
395
395
|
function moduleSource(module, ref = MODULE_REF) {
|
|
396
396
|
return `${MODULE_SOURCE_BASE}/${module}?ref=${ref}`;
|
|
@@ -1421,6 +1421,69 @@ jobs:
|
|
|
1421
1421
|
-f description="\${{ job.status }}"
|
|
1422
1422
|
`;
|
|
1423
1423
|
}
|
|
1424
|
+
function verifyWorkflowYml(name) {
|
|
1425
|
+
return `name: greenlight-verify
|
|
1426
|
+
|
|
1427
|
+
# Vercel deploys ${name} on push and posts a deployment_status to GitHub; we verify the exact
|
|
1428
|
+
# deployed URL. ANTHROPIC_API_KEY (optional) enables the agent-web scenarios \u2014 absent \u2192 omitted
|
|
1429
|
+
# (see verify/${name}.config.ts), so the gate stays green on api + test alone.
|
|
1430
|
+
on:
|
|
1431
|
+
deployment_status:
|
|
1432
|
+
|
|
1433
|
+
permissions:
|
|
1434
|
+
contents: read
|
|
1435
|
+
statuses: write
|
|
1436
|
+
|
|
1437
|
+
jobs:
|
|
1438
|
+
verify:
|
|
1439
|
+
if: \${{ github.event.deployment_status.state == 'success' }}
|
|
1440
|
+
runs-on: ubuntu-latest
|
|
1441
|
+
steps:
|
|
1442
|
+
- uses: actions/checkout@v4
|
|
1443
|
+
- uses: actions/setup-node@v4
|
|
1444
|
+
with:
|
|
1445
|
+
node-version: '24'
|
|
1446
|
+
- name: Install deps (for test-mode)
|
|
1447
|
+
run: |
|
|
1448
|
+
corepack enable || true
|
|
1449
|
+
if [ -f pnpm-lock.yaml ]; then pnpm install --frozen-lockfile;
|
|
1450
|
+
elif [ -f yarn.lock ]; then yarn install --frozen-lockfile;
|
|
1451
|
+
else npm ci; fi
|
|
1452
|
+
- name: Verify the deployment
|
|
1453
|
+
env:
|
|
1454
|
+
ANTHROPIC_API_KEY: \${{ secrets.ANTHROPIC_API_KEY }}
|
|
1455
|
+
run: npx -y @rtrentjones/greenlight@latest verify --url "\${{ github.event.deployment_status.target_url }}" --spec verify/${name}.config.ts
|
|
1456
|
+
`;
|
|
1457
|
+
}
|
|
1458
|
+
function nextVerifyConfig(name) {
|
|
1459
|
+
return `// Greenlight verify spec for ${name} (next/vercel) \u2014 run by .github/workflows/greenlight-verify.yml
|
|
1460
|
+
// after Vercel deploys (deployment_status). An array combines modes (allPass):
|
|
1461
|
+
// - api: the deployed URL serves (200).
|
|
1462
|
+
// - test: this tool's own suite \u2014 set the real command for your package manager.
|
|
1463
|
+
// - agent-web: an LLM drives the live UI; runs ONLY when ANTHROPIC_API_KEY is set (else omitted,
|
|
1464
|
+
// so the gate stays green). Replace the scenario with real user tasks + assertions.
|
|
1465
|
+
const agentWeb = process.env.ANTHROPIC_API_KEY
|
|
1466
|
+
? [
|
|
1467
|
+
{
|
|
1468
|
+
mode: 'agent-web',
|
|
1469
|
+
scenarios: [
|
|
1470
|
+
{
|
|
1471
|
+
name: 'home renders',
|
|
1472
|
+
task: 'Open the home page and confirm the app loads without an error screen.',
|
|
1473
|
+
asserts: [{ selector: 'body' }],
|
|
1474
|
+
},
|
|
1475
|
+
],
|
|
1476
|
+
},
|
|
1477
|
+
]
|
|
1478
|
+
: [];
|
|
1479
|
+
|
|
1480
|
+
export default [
|
|
1481
|
+
{ mode: 'api', checks: [{ path: '/', status: 200 }] },
|
|
1482
|
+
{ mode: 'test', command: 'npm test' },
|
|
1483
|
+
...agentWeb,
|
|
1484
|
+
];
|
|
1485
|
+
`;
|
|
1486
|
+
}
|
|
1424
1487
|
function writeIfAbsent(path, contents, label) {
|
|
1425
1488
|
if (existsSync7(path)) {
|
|
1426
1489
|
console.log(`\xB7 ${label} exists \u2014 left as-is`);
|
|
@@ -1498,11 +1561,13 @@ async function adoptWrapper(ctx) {
|
|
|
1498
1561
|
emitToolTf({ name, domain, lane, target, data, envs, slug, external: true }),
|
|
1499
1562
|
`infra/${name}.tf`
|
|
1500
1563
|
);
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1564
|
+
if (target !== "vercel") {
|
|
1565
|
+
writeIfAbsent(
|
|
1566
|
+
join2(cwd, `verify/${name}.config.ts`),
|
|
1567
|
+
starterVerifyConfig(lane),
|
|
1568
|
+
`verify/${name}.config.ts`
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1506
1571
|
const providers = providersForTool({ target, data });
|
|
1507
1572
|
if (existsSync7(join2(cwd, "infra/main.tf")) && providers.some((p) => p !== "cloudflare" && p !== "github")) {
|
|
1508
1573
|
console.log(`\xB7 ensure infra/main.tf declares provider(s): ${providers.join(", ")}`);
|
|
@@ -1522,6 +1587,18 @@ async function adoptWrapper(ctx) {
|
|
|
1522
1587
|
`${toolRel}/.github/workflows/greenlight-build.yml (provider-agnostic build \u2192 GHCR \u2192 dispatch)`
|
|
1523
1588
|
);
|
|
1524
1589
|
}
|
|
1590
|
+
if (target === "vercel") {
|
|
1591
|
+
writeIfAbsent(
|
|
1592
|
+
join2(dest, `verify/${name}.config.ts`),
|
|
1593
|
+
nextVerifyConfig(name),
|
|
1594
|
+
`${toolRel}/verify/${name}.config.ts (tool-CI verify spec)`
|
|
1595
|
+
);
|
|
1596
|
+
writeIfAbsent(
|
|
1597
|
+
join2(dest, ".github/workflows/greenlight-verify.yml"),
|
|
1598
|
+
verifyWorkflowYml(name),
|
|
1599
|
+
`${toolRel}/.github/workflows/greenlight-verify.yml (verify on Vercel deployment_status)`
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1525
1602
|
console.log(`
|
|
1526
1603
|
Next:
|
|
1527
1604
|
(in the tool repo) commit the Greenlight kit + build workflow so they travel with the submodule:
|
|
@@ -1531,7 +1608,10 @@ Next:
|
|
|
1531
1608
|
git commit && git push # CI (infra.yml) applies. Tool's CI builds; wrapper deploys.${target === "oci" ? `
|
|
1532
1609
|
Secrets (guided): greenlight secrets gather ${name} --repo <wrapper> # TF_VAR_OCI_* + GREENLIGHT_STATUS_TOKEN
|
|
1533
1610
|
greenlight secrets gather ${name} --repo ${slug} # GREENLIGHT_DISPATCH_TOKEN
|
|
1534
|
-
The instance OCID is auto-resolved by the deploy workflow (by display name) \u2014 nothing to set.` : ""
|
|
1611
|
+
The instance OCID is auto-resolved by the deploy workflow (by display name) \u2014 nothing to set.` : target === "vercel" ? `
|
|
1612
|
+
Deploy is Vercel's git integration (no wrapper deploy). The tool's greenlight-verify.yml verifies
|
|
1613
|
+
each deployment (deployment_status). Optional: add ANTHROPIC_API_KEY to ${slug} to enable the
|
|
1614
|
+
agent-web scenarios in verify/${name}.config.ts (absent \u2192 api + test gate alone).` : ""}`);
|
|
1535
1615
|
}
|
|
1536
1616
|
async function adoptStandalone(ctx) {
|
|
1537
1617
|
const { name, repoArg, lane, target, data, auth, envs, domain, reg, regPath } = ctx;
|
|
@@ -1936,9 +2016,30 @@ function flag6(args, name) {
|
|
|
1936
2016
|
return i >= 0 ? args[i + 1] : void 0;
|
|
1937
2017
|
}
|
|
1938
2018
|
async function verifyCommand(args) {
|
|
2019
|
+
const specPath = flag6(args, "--spec");
|
|
2020
|
+
if (specPath) {
|
|
2021
|
+
const url2 = flag6(args, "--url");
|
|
2022
|
+
if (!url2) throw new Error("verify --spec needs --url <deployed-url>");
|
|
2023
|
+
const loaded2 = await loadVerifySpecAt(specPath);
|
|
2024
|
+
if (!loaded2) throw new Error(`no verify spec at ${specPath}`);
|
|
2025
|
+
const specs2 = Array.isArray(loaded2) ? loaded2 : [loaded2];
|
|
2026
|
+
const waitMs = (flag6(args, "--wait") !== void 0 ? Number(flag6(args, "--wait")) : 0) * 1e3;
|
|
2027
|
+
const reports2 = await verifyAll(url2, specs2, {
|
|
2028
|
+
reachableTimeoutMs: waitMs,
|
|
2029
|
+
toolDir: process.cwd()
|
|
2030
|
+
});
|
|
2031
|
+
for (const report of reports2) printReport(report);
|
|
2032
|
+
const pass2 = allPass(reports2);
|
|
2033
|
+
if (reports2.length > 1)
|
|
2034
|
+
console.log(`
|
|
2035
|
+
${pass2 ? "\u2714 ALL PASS" : "\u2718 FAIL"} (${reports2.length} specs)`);
|
|
2036
|
+
process.exit(pass2 ? 0 : 1);
|
|
2037
|
+
}
|
|
1939
2038
|
const name = args[0];
|
|
1940
2039
|
if (!name || name.startsWith("-")) {
|
|
1941
|
-
throw new Error(
|
|
2040
|
+
throw new Error(
|
|
2041
|
+
"usage: greenlight verify <name> [--env <beta|prod> | --url <url>] | verify --url <url> --spec <path>"
|
|
2042
|
+
);
|
|
1942
2043
|
}
|
|
1943
2044
|
const { config } = await loadManifest();
|
|
1944
2045
|
const entry = resolveEntry(config, name);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rtrentjones/greenlight",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Greenlight CLI — setup and lifecycle for the harness.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,9 +32,9 @@
|
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@rtrentjones/greenlight-adapters": "0.2.4",
|
|
35
|
-
"@rtrentjones/greenlight-loop": "0.2.4",
|
|
36
35
|
"@rtrentjones/greenlight-shared": "0.2.4",
|
|
37
|
-
"@rtrentjones/greenlight-verify": "0.2.4"
|
|
36
|
+
"@rtrentjones/greenlight-verify": "0.2.4",
|
|
37
|
+
"@rtrentjones/greenlight-loop": "0.2.4"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "node scripts/copy-assets.mjs && tsup",
|