@attalabs/vinaya 0.2.0 → 0.3.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/aeg-root/enforcement.md +2 -2
- package/aeg-root/roles/archivist.md +1 -1
- package/aeg-root/roles/developer.md +2 -2
- package/aeg-root/state-machine.md +17 -3
- package/aeg-root/tranche-model.md +5 -3
- package/dist/checks/bin/check-branch-topology.js +69 -11
- package/dist/checks/bin/check-brief-shape.js +69 -11
- package/dist/checks/bin/check-closes-n.js +69 -11
- package/dist/checks/bin/check-coherence.js +69 -11
- package/dist/checks/bin/check-dead-branch-push.js +69 -11
- package/dist/checks/bin/check-dispatch-readiness.js +69 -11
- package/dist/checks/bin/check-doc-coverage-push.js +69 -11
- package/dist/checks/bin/check-doc-coverage.js +69 -11
- package/dist/checks/bin/check-first-push-dispatch.js +69 -11
- package/dist/checks/bin/check-issue-assignment.js +69 -11
- package/dist/checks/bin/check-no-disk-state.js +69 -11
- package/dist/checks/bin/check-reader-resolvable-prose.js +69 -11
- package/dist/checks/bin/check-registry-gates.js +69 -11
- package/dist/checks/bin/check-review-gate.js +69 -11
- package/dist/checks/bin/check-single-plan-pr.js +69 -11
- package/dist/checks/bin/check-test-plan.js +69 -11
- package/dist/index.js +104 -38
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1470,11 +1470,79 @@ function installSignalForwarding() {
|
|
|
1470
1470
|
process.on("SIGINT", () => forward("SIGINT", 130));
|
|
1471
1471
|
process.on("SIGTERM", () => forward("SIGTERM", 143));
|
|
1472
1472
|
}
|
|
1473
|
-
|
|
1473
|
+
var ENV_BASELINE_KEYS = ["PATH", "LANG", "HOME", "HTTPS_PROXY", "HTTP_PROXY", "NO_PROXY", "TMPDIR"];
|
|
1474
|
+
function buildCheckEnv(env, callerEnv = process.env) {
|
|
1475
|
+
const out = {};
|
|
1476
|
+
for (const key of ENV_BASELINE_KEYS) {
|
|
1477
|
+
const value = callerEnv[key];
|
|
1478
|
+
if (value !== undefined)
|
|
1479
|
+
out[key] = value;
|
|
1480
|
+
}
|
|
1481
|
+
if (!env)
|
|
1482
|
+
return out;
|
|
1483
|
+
for (const [key, decl] of Object.entries(env)) {
|
|
1484
|
+
if (decl === true || typeof decl === "object" && "optional" in decl) {
|
|
1485
|
+
const value = callerEnv[key];
|
|
1486
|
+
if (value !== undefined)
|
|
1487
|
+
out[key] = value;
|
|
1488
|
+
} else if (typeof decl === "object" && "anyOf" in decl) {
|
|
1489
|
+
for (const member of decl.anyOf) {
|
|
1490
|
+
const value = callerEnv[member];
|
|
1491
|
+
if (value !== undefined)
|
|
1492
|
+
out[member] = value;
|
|
1493
|
+
}
|
|
1494
|
+
} else if (typeof decl === "string") {
|
|
1495
|
+
out[key] = decl;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
return out;
|
|
1499
|
+
}
|
|
1500
|
+
function missingEnvErrors(spec, callerEnv) {
|
|
1501
|
+
if (!spec.env)
|
|
1502
|
+
return [];
|
|
1503
|
+
const errors = [];
|
|
1504
|
+
for (const [key, decl] of Object.entries(spec.env)) {
|
|
1505
|
+
if (decl === true) {
|
|
1506
|
+
if (callerEnv[key] === undefined) {
|
|
1507
|
+
errors.push({
|
|
1508
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
1509
|
+
check: spec.name,
|
|
1510
|
+
severity: "error",
|
|
1511
|
+
message: `Could not run check "${spec.name}": required environment variable \`${key}\` is not set.`,
|
|
1512
|
+
agent_recovery_prompt: `Set \`${key}\` in the environment before re-running \`vinaya check ${spec.name}\`, or relax its declaration to \`{ optional: true }\` in its \`env\` registration if the check can tolerate absence.`
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
} else if (typeof decl === "object" && decl !== null && "anyOf" in decl) {
|
|
1516
|
+
const satisfied = decl.anyOf.some((member) => callerEnv[member] !== undefined);
|
|
1517
|
+
if (!satisfied) {
|
|
1518
|
+
errors.push({
|
|
1519
|
+
schema: CHECK_SCHEMA_VERSION,
|
|
1520
|
+
check: spec.name,
|
|
1521
|
+
severity: "error",
|
|
1522
|
+
message: `Could not run check "${spec.name}": none of its required environment variables (${decl.anyOf.join(", ")}) are set.`,
|
|
1523
|
+
agent_recovery_prompt: `Set one of ${decl.anyOf.join(", ")} in the environment before re-running \`vinaya check ${spec.name}\`.`
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
return errors;
|
|
1529
|
+
}
|
|
1530
|
+
async function runOne(spec, timeoutMs, callerEnv) {
|
|
1474
1531
|
const start = performance.now();
|
|
1532
|
+
const envErrors = missingEnvErrors(spec, callerEnv);
|
|
1533
|
+
if (envErrors.length > 0) {
|
|
1534
|
+
return {
|
|
1535
|
+
name: spec.name,
|
|
1536
|
+
status: "error",
|
|
1537
|
+
exitCode: null,
|
|
1538
|
+
errors: envErrors,
|
|
1539
|
+
durationMs: performance.now() - start
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1475
1542
|
const proc = spawn(spec.run, spec.args ?? [], {
|
|
1476
1543
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1477
|
-
detached: true
|
|
1544
|
+
detached: true,
|
|
1545
|
+
env: buildCheckEnv(spec.env, callerEnv)
|
|
1478
1546
|
});
|
|
1479
1547
|
function killTree(signal) {
|
|
1480
1548
|
const pid = proc.pid;
|
|
@@ -1590,6 +1658,7 @@ async function runOne(spec, timeoutMs) {
|
|
|
1590
1658
|
return { name: spec.name, status, exitCode, errors, durationMs };
|
|
1591
1659
|
}
|
|
1592
1660
|
async function runChecks(specs, opts) {
|
|
1661
|
+
const callerEnv = opts.callerEnv ?? process.env;
|
|
1593
1662
|
const results = new Array(specs.length);
|
|
1594
1663
|
const toRun = [];
|
|
1595
1664
|
for (let i = 0;i < specs.length; i++) {
|
|
@@ -1606,7 +1675,7 @@ async function runChecks(specs, opts) {
|
|
|
1606
1675
|
const idx = toRun[cursor];
|
|
1607
1676
|
cursor += 1;
|
|
1608
1677
|
const spec = specs[idx];
|
|
1609
|
-
results[idx] = await runOne(spec, spec.timeoutMs ?? opts.defaultTimeoutMs);
|
|
1678
|
+
results[idx] = await runOne(spec, spec.timeoutMs ?? opts.defaultTimeoutMs, callerEnv);
|
|
1610
1679
|
}
|
|
1611
1680
|
};
|
|
1612
1681
|
const workerCount = Math.max(1, Math.min(opts.parallel, toRun.length));
|
|
@@ -1781,31 +1850,6 @@ function loadConfigChecked() {
|
|
|
1781
1850
|
return { ok: true, config: parsed.data };
|
|
1782
1851
|
}
|
|
1783
1852
|
|
|
1784
|
-
// src/lib/env-lint.ts
|
|
1785
|
-
import { existsSync as existsSync5, readFileSync as readFileSync2 } from "node:fs";
|
|
1786
|
-
var ENV_READ_PATTERN = /\b(?:process\.env|Bun\.env|Deno\.env)\b/;
|
|
1787
|
-
function checksMissingEnvDeclaration(specs) {
|
|
1788
|
-
const names = [];
|
|
1789
|
-
for (const spec of specs) {
|
|
1790
|
-
if (spec.env)
|
|
1791
|
-
continue;
|
|
1792
|
-
if (!existsSync5(spec.run))
|
|
1793
|
-
continue;
|
|
1794
|
-
let source;
|
|
1795
|
-
try {
|
|
1796
|
-
source = readFileSync2(spec.run, "utf8");
|
|
1797
|
-
} catch {
|
|
1798
|
-
continue;
|
|
1799
|
-
}
|
|
1800
|
-
if (ENV_READ_PATTERN.test(source))
|
|
1801
|
-
names.push(spec.name);
|
|
1802
|
-
}
|
|
1803
|
-
return names;
|
|
1804
|
-
}
|
|
1805
|
-
function envDeclarationWarning(name) {
|
|
1806
|
-
return `check "${name}" reads environment variables directly but declares no \`env\` — it will lose environment access at the next minor. Declare \`env\` for it (CheckSpec['env'] in src/checks/contract.ts, or vinaya.config.json's checks.<name>.env).`;
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
1853
|
// src/commands/check.ts
|
|
1810
1854
|
function git(args) {
|
|
1811
1855
|
try {
|
|
@@ -1868,9 +1912,6 @@ async function checkCommand(args) {
|
|
|
1868
1912
|
console.error(`Unknown check: ${requestedName}`);
|
|
1869
1913
|
process.exit(2);
|
|
1870
1914
|
}
|
|
1871
|
-
for (const name of checksMissingEnvDeclaration(specsToRun)) {
|
|
1872
|
-
console.error(`⚠ ${envDeclarationWarning(name)}`);
|
|
1873
|
-
}
|
|
1874
1915
|
const changed = diffOnly ? changedFiles() : null;
|
|
1875
1916
|
const outcomes = specsToRun.length > 0 ? await runChecks(specsToRun, {
|
|
1876
1917
|
parallel: requestedParallel ?? defaultParallelism(),
|
|
@@ -1901,7 +1942,7 @@ async function checkCommand(args) {
|
|
|
1901
1942
|
|
|
1902
1943
|
// src/commands/demo.ts
|
|
1903
1944
|
import { execFileSync as execFileSync5, spawnSync } from "node:child_process";
|
|
1904
|
-
import { existsSync as
|
|
1945
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, rmSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1905
1946
|
import { isAbsolute, join as join5 } from "node:path";
|
|
1906
1947
|
var DEMO_BRANCH_PREFIX = "vinaya/demo-break-";
|
|
1907
1948
|
var FIXTURE_PATH = ".vinaya-demo-brief.md";
|
|
@@ -1944,9 +1985,9 @@ function gitDirAbs(repoRoot) {
|
|
|
1944
1985
|
function recoverFromCrash(repoRoot, statePath) {
|
|
1945
1986
|
const currentBranch = currentBranchName(repoRoot);
|
|
1946
1987
|
let state = null;
|
|
1947
|
-
if (
|
|
1988
|
+
if (existsSync5(statePath)) {
|
|
1948
1989
|
try {
|
|
1949
|
-
state = JSON.parse(
|
|
1990
|
+
state = JSON.parse(readFileSync2(statePath, "utf-8"));
|
|
1950
1991
|
} catch {
|
|
1951
1992
|
state = null;
|
|
1952
1993
|
}
|
|
@@ -2016,14 +2057,14 @@ function cleanup(repoRoot, originalBranch, demoBranch, statePath) {
|
|
|
2016
2057
|
gitCapture(repoRoot, ["clean", "-fd"]);
|
|
2017
2058
|
gitCapture(repoRoot, ["checkout", originalBranch]);
|
|
2018
2059
|
gitCapture(repoRoot, ["branch", "-D", demoBranch]);
|
|
2019
|
-
if (
|
|
2060
|
+
if (existsSync5(statePath))
|
|
2020
2061
|
rmSync(statePath, { force: true });
|
|
2021
2062
|
}
|
|
2022
2063
|
async function runDemoBreak(repoRoot, args) {
|
|
2023
2064
|
const keep = args.includes("--keep");
|
|
2024
2065
|
const hookDir = resolveHookDir(repoRoot);
|
|
2025
2066
|
const hookPath = join5(repoRoot, hookDir, "pre-commit");
|
|
2026
|
-
if (!
|
|
2067
|
+
if (!existsSync5(hookPath)) {
|
|
2027
2068
|
console.error("Vinaya hooks are not installed in this repo. Run `vinaya init` first.");
|
|
2028
2069
|
return 1;
|
|
2029
2070
|
}
|
|
@@ -2081,7 +2122,7 @@ Attempting to commit…
|
|
|
2081
2122
|
process.stdout.write(`✓ Commit passed — the fix worked.
|
|
2082
2123
|
`);
|
|
2083
2124
|
if (keep) {
|
|
2084
|
-
if (
|
|
2125
|
+
if (existsSync5(statePath))
|
|
2085
2126
|
unlinkSync(statePath);
|
|
2086
2127
|
process.stdout.write(`
|
|
2087
2128
|
--keep: leaving \`${demoBranch}\` checked out for you to inspect. Clean up with:
|
|
@@ -2477,6 +2518,31 @@ function buildInitProductOps(name) {
|
|
|
2477
2518
|
];
|
|
2478
2519
|
}
|
|
2479
2520
|
|
|
2521
|
+
// src/lib/env-lint.ts
|
|
2522
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
|
|
2523
|
+
var ENV_READ_PATTERN = /\b(?:process\.env|Bun\.env|Deno\.env)\b/;
|
|
2524
|
+
function checksMissingEnvDeclaration(specs) {
|
|
2525
|
+
const names = [];
|
|
2526
|
+
for (const spec of specs) {
|
|
2527
|
+
if (spec.env)
|
|
2528
|
+
continue;
|
|
2529
|
+
if (!existsSync6(spec.run))
|
|
2530
|
+
continue;
|
|
2531
|
+
let source;
|
|
2532
|
+
try {
|
|
2533
|
+
source = readFileSync3(spec.run, "utf8");
|
|
2534
|
+
} catch {
|
|
2535
|
+
continue;
|
|
2536
|
+
}
|
|
2537
|
+
if (ENV_READ_PATTERN.test(source))
|
|
2538
|
+
names.push(spec.name);
|
|
2539
|
+
}
|
|
2540
|
+
return names;
|
|
2541
|
+
}
|
|
2542
|
+
function envDeclarationWarning(name) {
|
|
2543
|
+
return `check "${name}" reads environment variables directly but declares no \`env\` — it will lose environment access at the next minor. Declare \`env\` for it (CheckSpec['env'] in src/checks/contract.ts, or vinaya.config.json's checks.<name>.env).`;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2480
2546
|
// src/lib/ops.ts
|
|
2481
2547
|
import { chmodSync, existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2482
2548
|
import { dirname as dirname3, join as join7, resolve, sep } from "node:path";
|
|
@@ -4300,7 +4366,7 @@ var COMMANDS = [
|
|
|
4300
4366
|
{ flag: "--parallel[=n]", description: "Concurrency cap (default: cpu-derived)" }
|
|
4301
4367
|
],
|
|
4302
4368
|
details: [
|
|
4303
|
-
"
|
|
4369
|
+
"Each spawned check's child process sees only a fixed baseline (`PATH`, `LANG`, `HOME`, `HTTPS_PROXY`, `HTTP_PROXY`, `NO_PROXY`, `TMPDIR`) plus whatever its `CheckSpec['env']` declaration explicitly forwards — never the full parent environment. A required (`true`) or unsatisfied `anyOf` declaration missing from the caller's environment synthesizes a `CheckError` before the check ever spawns. Declare `env` (a core check's own registration, or `vinaya.config.json`'s `checks.<name>.env` for a custom one) for any check that reads `process.env`/`Bun.env`/`Deno.env` directly — `vinaya doctor` carries the permanent diagnostic for one that doesn't."
|
|
4304
4370
|
],
|
|
4305
4371
|
status: "shipped"
|
|
4306
4372
|
},
|
package/package.json
CHANGED