@rness/cli 0.5.0 → 0.5.1
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/README.md +51 -6
- package/dist/bin/rness.js +26 -4
- package/dist/bin/rness.js.map +1 -1
- package/dist/{chunk-WSGPDTPQ.js → chunk-C6HBGV5V.js} +266 -83
- package/dist/chunk-C6HBGV5V.js.map +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/scaffold/.github/dependabot.yml +12 -0
- package/scaffold/.github/workflows/validate.yml +4 -1
- package/dist/chunk-WSGPDTPQ.js.map +0 -1
|
@@ -774,6 +774,18 @@ async function probeRemote(url, timeoutMs = 6e4, credentials) {
|
|
|
774
774
|
import { readFile as readFile3 } from "fs/promises";
|
|
775
775
|
import { basename as basename2, join as join3 } from "path";
|
|
776
776
|
var NAME = /^(?!\.{1,2}$)[a-z0-9._][a-z0-9._-]*$/;
|
|
777
|
+
var CONTEXT_REPO = ".rness";
|
|
778
|
+
var EXCLUDED_NAMES = /* @__PURE__ */ new Set([
|
|
779
|
+
CONTEXT_REPO,
|
|
780
|
+
".git",
|
|
781
|
+
"node_modules"
|
|
782
|
+
]);
|
|
783
|
+
function isMemberRepo(repo) {
|
|
784
|
+
return !repo.archived && !EXCLUDED_NAMES.has(repo.name.toLowerCase());
|
|
785
|
+
}
|
|
786
|
+
function isWorkspaceClone(name) {
|
|
787
|
+
return NAME.test(name) && !EXCLUDED_NAMES.has(name);
|
|
788
|
+
}
|
|
777
789
|
var NAME_RULE = "lowercase letters, digits, '.', '_' and '-', not starting with '-'";
|
|
778
790
|
var ORG_NAME = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
|
|
779
791
|
var KEYS = ["contract", "org", "repos", "scopes"];
|
|
@@ -1035,7 +1047,7 @@ function paint(tone, text, stream = process.stdout) {
|
|
|
1035
1047
|
if (noColor()) return text;
|
|
1036
1048
|
return styleText(FORMAT[tone], text, { stream, validateStream: true });
|
|
1037
1049
|
}
|
|
1038
|
-
var IDLE = /* @__PURE__ */ new Set(["unchanged", "skipped", "not cloned"]);
|
|
1050
|
+
var IDLE = /* @__PURE__ */ new Set(["unchanged", "skipped", "not cloned", "current"]);
|
|
1039
1051
|
var DONE = /* @__PURE__ */ new Set([
|
|
1040
1052
|
"created",
|
|
1041
1053
|
"cloned",
|
|
@@ -1067,11 +1079,20 @@ function bold(text, stream = process.stdout) {
|
|
|
1067
1079
|
if (noColor()) return text;
|
|
1068
1080
|
return styleText("bold", text, { stream, validateStream: true });
|
|
1069
1081
|
}
|
|
1070
|
-
|
|
1082
|
+
var BACKGROUND = {
|
|
1083
|
+
intro: ["bgCyan", "black", "bold"],
|
|
1084
|
+
done: ["bgGreen", "black", "bold"],
|
|
1085
|
+
idle: ["bgCyan", "black", "bold"],
|
|
1086
|
+
info: ["bgCyan", "black", "bold"],
|
|
1087
|
+
warn: ["bgYellow", "black", "bold"],
|
|
1088
|
+
error: ["bgRed", "white", "bold"]
|
|
1089
|
+
};
|
|
1090
|
+
function badge(text, tone = "intro", stream = process.stdout) {
|
|
1091
|
+
const [t, s] = typeof tone === "string" ? [tone, stream] : ["intro", tone];
|
|
1071
1092
|
if (noColor()) return text;
|
|
1072
1093
|
const padded = ` ${text} `;
|
|
1073
|
-
const painted = styleText([
|
|
1074
|
-
stream,
|
|
1094
|
+
const painted = styleText(BACKGROUND[t], padded, {
|
|
1095
|
+
stream: s,
|
|
1075
1096
|
validateStream: true
|
|
1076
1097
|
});
|
|
1077
1098
|
return painted === padded ? text : painted;
|
|
@@ -1214,6 +1235,14 @@ var CHECKING = [
|
|
|
1214
1235
|
function usingRest(access2, login) {
|
|
1215
1236
|
return access2.ok ? `ssh (github.com as ${access2.login})` : `https (ssh to github.com unavailable: ${access2.reason}${loggedInAs(login)})`;
|
|
1216
1237
|
}
|
|
1238
|
+
function agentHintLines(platform = process.platform) {
|
|
1239
|
+
const mac = platform === "darwin";
|
|
1240
|
+
return [
|
|
1241
|
+
"your key works but is not loaded; ssh asks again in every child:",
|
|
1242
|
+
` ssh-add ${mac ? "--apple-use-keychain " : ""}~/.ssh/id_ed25519`,
|
|
1243
|
+
` ~/.ssh/config: AddKeysToAgent yes${mac ? ", UseKeychain yes" : ""}`
|
|
1244
|
+
];
|
|
1245
|
+
}
|
|
1217
1246
|
function sshWorkspaceLines(reason3) {
|
|
1218
1247
|
return [
|
|
1219
1248
|
`this workspace clones over SSH (rness.json) but ssh to github.com fails: ${reason3}`,
|
|
@@ -1229,6 +1258,7 @@ function httpsFlagLine(spec) {
|
|
|
1229
1258
|
}
|
|
1230
1259
|
|
|
1231
1260
|
// src/core/workspace.ts
|
|
1261
|
+
import { readdir } from "fs/promises";
|
|
1232
1262
|
import { dirname as dirname3, join as join5, parse, resolve as resolve2 } from "path";
|
|
1233
1263
|
async function findWorkspace(startDir) {
|
|
1234
1264
|
const start = resolve2(startDir);
|
|
@@ -1248,6 +1278,16 @@ async function findWorkspace(startDir) {
|
|
|
1248
1278
|
}
|
|
1249
1279
|
throw new Error(`no rness workspace found above ${startDir}`);
|
|
1250
1280
|
}
|
|
1281
|
+
async function clonesIn(root) {
|
|
1282
|
+
try {
|
|
1283
|
+
const entries = await readdir(join5(root, "org"), { withFileTypes: true });
|
|
1284
|
+
return new Set(
|
|
1285
|
+
entries.filter((e) => e.isDirectory() && isWorkspaceClone(e.name)).map((e) => e.name)
|
|
1286
|
+
);
|
|
1287
|
+
} catch {
|
|
1288
|
+
return /* @__PURE__ */ new Set();
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1251
1291
|
|
|
1252
1292
|
// src/report.ts
|
|
1253
1293
|
function reportError(e) {
|
|
@@ -1544,7 +1584,6 @@ async function ensureClaudeMd(dir) {
|
|
|
1544
1584
|
}
|
|
1545
1585
|
|
|
1546
1586
|
// src/commands/sync.ts
|
|
1547
|
-
import { readdir } from "fs/promises";
|
|
1548
1587
|
import { join as join9 } from "path";
|
|
1549
1588
|
|
|
1550
1589
|
// src/core/progress.ts
|
|
@@ -1642,12 +1681,17 @@ var plainUi = {
|
|
|
1642
1681
|
return result;
|
|
1643
1682
|
}
|
|
1644
1683
|
};
|
|
1684
|
+
var IDLE_SYMBOL = "\xB7";
|
|
1645
1685
|
function sessionUi(p) {
|
|
1646
1686
|
const say = (verb, text) => {
|
|
1647
1687
|
const tone = toneOf(verb);
|
|
1648
|
-
if (tone === "done") p.log.
|
|
1688
|
+
if (tone === "done") p.log.success(text);
|
|
1649
1689
|
else if (tone === "warn") p.log.warn(text);
|
|
1650
|
-
else if (tone === "
|
|
1690
|
+
else if (tone === "error") p.log.error(text);
|
|
1691
|
+
else if (tone === "idle")
|
|
1692
|
+
p.log.message(paint("idle", text), {
|
|
1693
|
+
symbol: paint("idle", IDLE_SYMBOL)
|
|
1694
|
+
});
|
|
1651
1695
|
else p.log.info(text);
|
|
1652
1696
|
};
|
|
1653
1697
|
return {
|
|
@@ -1657,10 +1701,10 @@ function sessionUi(p) {
|
|
|
1657
1701
|
p.intro(badge(title));
|
|
1658
1702
|
},
|
|
1659
1703
|
outro: (message, tone = "done") => {
|
|
1660
|
-
p.outro(
|
|
1704
|
+
p.outro(badge(message, tone));
|
|
1661
1705
|
},
|
|
1662
1706
|
line: (verb, rest, sentence) => {
|
|
1663
|
-
say(verb, sentence ??
|
|
1707
|
+
say(verb, sentence ?? `${rest} ${verb}`);
|
|
1664
1708
|
},
|
|
1665
1709
|
warn: (text) => {
|
|
1666
1710
|
p.log.warn(text);
|
|
@@ -1679,22 +1723,21 @@ function sessionUi(p) {
|
|
|
1679
1723
|
},
|
|
1680
1724
|
async step(options, work, done) {
|
|
1681
1725
|
const label = options.sentence ?? tidy(options.doing);
|
|
1682
|
-
const finish = (result) =>
|
|
1683
|
-
|
|
1684
|
-
return line === null ? null : tidy(`${line[0]} ${line[1]}`);
|
|
1685
|
-
};
|
|
1726
|
+
const finish = (result) => done?.(result) ?? null;
|
|
1727
|
+
const label_ = (line) => `${line[1]} ${line[0]}`;
|
|
1686
1728
|
if (options.git === true && !this.gitIsSilent) {
|
|
1687
1729
|
p.log.message(paint("idle", `${label}\u2026`));
|
|
1688
1730
|
const result = await work();
|
|
1689
|
-
const
|
|
1690
|
-
if (
|
|
1731
|
+
const line = finish(result);
|
|
1732
|
+
if (line !== null) say(line[0], label_(line));
|
|
1691
1733
|
return result;
|
|
1692
1734
|
}
|
|
1693
1735
|
const spin = p.spinner();
|
|
1694
1736
|
spin.start(label);
|
|
1695
1737
|
try {
|
|
1696
1738
|
const result = await work();
|
|
1697
|
-
|
|
1739
|
+
const line = finish(result);
|
|
1740
|
+
spin.stop(line === null ? label : label_(line));
|
|
1698
1741
|
return result;
|
|
1699
1742
|
} catch (e) {
|
|
1700
1743
|
spin.error(`${label} \u2014 failed`);
|
|
@@ -1738,16 +1781,6 @@ function targetsOf(manifest, root, only) {
|
|
|
1738
1781
|
return targets;
|
|
1739
1782
|
}
|
|
1740
1783
|
var SELECT_ALL = "*";
|
|
1741
|
-
async function clonesIn(root) {
|
|
1742
|
-
try {
|
|
1743
|
-
const entries = await readdir(join9(root, "org"), { withFileTypes: true });
|
|
1744
|
-
return new Set(
|
|
1745
|
-
entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name)
|
|
1746
|
-
);
|
|
1747
|
-
} catch {
|
|
1748
|
-
return /* @__PURE__ */ new Set();
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
1784
|
async function askWhatToSync(input) {
|
|
1752
1785
|
if (!input.terminal.isTty()) {
|
|
1753
1786
|
process.stderr.write(
|
|
@@ -1978,7 +2011,7 @@ async function syncCommand(opts, deps = {}) {
|
|
|
1978
2011
|
}
|
|
1979
2012
|
|
|
1980
2013
|
// src/commands/add.ts
|
|
1981
|
-
import { join as join10 } from "path";
|
|
2014
|
+
import { basename as basename3, join as join10 } from "path";
|
|
1982
2015
|
function printDeclared(ui, result, already) {
|
|
1983
2016
|
for (const name of result.declared) {
|
|
1984
2017
|
if (already.has(name)) continue;
|
|
@@ -2013,6 +2046,13 @@ async function addCommand(spec, opts, deps = {}) {
|
|
|
2013
2046
|
`);
|
|
2014
2047
|
return 2;
|
|
2015
2048
|
}
|
|
2049
|
+
if (parsed.name === CONTEXT_REPO) {
|
|
2050
|
+
process.stderr.write(
|
|
2051
|
+
`${CONTEXT_REPO} is the workspace context, not a member repository \u2014 it is already cloned at ${basename3(ws.root)}/${CONTEXT_REPO}
|
|
2052
|
+
`
|
|
2053
|
+
);
|
|
2054
|
+
return 2;
|
|
2055
|
+
}
|
|
2016
2056
|
const interactive = opts.yes !== true;
|
|
2017
2057
|
const ownUi = deps.ui === void 0;
|
|
2018
2058
|
if (interactive && !terminal.isTty()) {
|
|
@@ -2214,6 +2254,11 @@ async function readPinnedCli(rnessDir) {
|
|
|
2214
2254
|
version: typeof pkg.version === "string" ? pkg.version : null
|
|
2215
2255
|
};
|
|
2216
2256
|
}
|
|
2257
|
+
async function pinnedBinPath(rnessDir) {
|
|
2258
|
+
const pinned = await readPinnedCli(rnessDir);
|
|
2259
|
+
if (pinned === null) return null;
|
|
2260
|
+
return join11(pinned.dir, ...pinned.bin.split("/"));
|
|
2261
|
+
}
|
|
2217
2262
|
async function findDelegate(cwd, ownVersion) {
|
|
2218
2263
|
let root;
|
|
2219
2264
|
let rnessDir;
|
|
@@ -2264,13 +2309,23 @@ function installErrorLine(stderr) {
|
|
|
2264
2309
|
const lines = stderr.split("\n").map((l) => l.trim()).filter((l) => l !== "");
|
|
2265
2310
|
return lines.find((l) => !BARE_NPM_CODE.test(l)) ?? firstLine3(stderr);
|
|
2266
2311
|
}
|
|
2267
|
-
|
|
2312
|
+
var FROZEN = {
|
|
2313
|
+
npm: ["ci"],
|
|
2314
|
+
pnpm: ["install", "--frozen-lockfile"],
|
|
2315
|
+
yarn: ["install", "--immutable"],
|
|
2316
|
+
bun: ["install", "--frozen-lockfile"]
|
|
2317
|
+
};
|
|
2318
|
+
function frozenArgs(pm) {
|
|
2319
|
+
return [...FROZEN[pm]];
|
|
2320
|
+
}
|
|
2321
|
+
async function installDependencies(pm, dir, opts = {}) {
|
|
2322
|
+
const args = opts.frozen === true ? frozenArgs(pm) : ["install"];
|
|
2268
2323
|
try {
|
|
2269
|
-
await execFileP2(pm,
|
|
2324
|
+
await execFileP2(pm, args, { cwd: dir, maxBuffer: 16 * 1024 * 1024 });
|
|
2270
2325
|
} catch (e) {
|
|
2271
2326
|
const err = e;
|
|
2272
2327
|
throw new Error(
|
|
2273
|
-
`${pm}
|
|
2328
|
+
`${pm} ${args.join(" ")} failed in ${dir}: ${installErrorLine(err.stderr ?? err.message)}`,
|
|
2274
2329
|
{ cause: e }
|
|
2275
2330
|
);
|
|
2276
2331
|
}
|
|
@@ -2279,7 +2334,7 @@ async function installDependencies(pm, dir) {
|
|
|
2279
2334
|
// src/core/pinned.ts
|
|
2280
2335
|
import { execFile as execFile3 } from "child_process";
|
|
2281
2336
|
import { readFile as readFile7 } from "fs/promises";
|
|
2282
|
-
import { basename as
|
|
2337
|
+
import { basename as basename4, join as join12 } from "path";
|
|
2283
2338
|
import { promisify as promisify3 } from "util";
|
|
2284
2339
|
var execFileP3 = promisify3(execFile3);
|
|
2285
2340
|
var PACKAGE = "@rness/cli";
|
|
@@ -2349,6 +2404,11 @@ async function workspacePackageManager(rnessDir) {
|
|
|
2349
2404
|
if (await exists(join12(rnessDir, lockfile))) return pm;
|
|
2350
2405
|
return "npm";
|
|
2351
2406
|
}
|
|
2407
|
+
async function hasLockfile(rnessDir, pm) {
|
|
2408
|
+
for (const [lockfile, owner] of LOCKFILES)
|
|
2409
|
+
if (owner === pm && await exists(join12(rnessDir, lockfile))) return true;
|
|
2410
|
+
return false;
|
|
2411
|
+
}
|
|
2352
2412
|
async function pinDrift(rnessDir) {
|
|
2353
2413
|
const pin = await readPin(rnessDir);
|
|
2354
2414
|
if (pin === null || !EXACT_VERSION.test(pin.spec)) return null;
|
|
@@ -2369,9 +2429,38 @@ async function driftWarning(cwd) {
|
|
|
2369
2429
|
if (drift === null) return null;
|
|
2370
2430
|
const pm = await workspacePackageManager(rnessDir);
|
|
2371
2431
|
return warn(
|
|
2372
|
-
`${
|
|
2432
|
+
`${basename4(root)}/.rness pins @rness/cli ${drift.pin} but ${drift.installed ?? "nothing"} is installed \u2014 run ${pm} install in .rness`
|
|
2373
2433
|
);
|
|
2374
2434
|
}
|
|
2435
|
+
var CHILD_DEADLINE_MS = 12e4;
|
|
2436
|
+
function childEnv(env = process.env) {
|
|
2437
|
+
return {
|
|
2438
|
+
...env,
|
|
2439
|
+
GIT_SSH_COMMAND: `${env["GIT_SSH_COMMAND"] ?? "ssh"} -o BatchMode=yes`,
|
|
2440
|
+
GIT_TERMINAL_PROMPT: "0"
|
|
2441
|
+
};
|
|
2442
|
+
}
|
|
2443
|
+
async function ensureInstalled(rnessDir, opts = {}) {
|
|
2444
|
+
const drift = await pinDrift(rnessDir);
|
|
2445
|
+
if (drift === null) return null;
|
|
2446
|
+
const env = opts.env ?? process.env;
|
|
2447
|
+
if (env["RNESS_NO_INSTALL"] === "1")
|
|
2448
|
+
return { installed: false, pin: drift.pin, reason: "RNESS_NO_INSTALL=1" };
|
|
2449
|
+
const pm = await workspacePackageManager(rnessDir);
|
|
2450
|
+
const frozen = await hasLockfile(rnessDir, pm);
|
|
2451
|
+
const install = opts.install ?? installDependencies;
|
|
2452
|
+
opts.announce?.(drift.pin);
|
|
2453
|
+
try {
|
|
2454
|
+
await install(pm, rnessDir, { frozen });
|
|
2455
|
+
return { installed: true, pin: drift.pin, reason: null };
|
|
2456
|
+
} catch (e) {
|
|
2457
|
+
return {
|
|
2458
|
+
installed: false,
|
|
2459
|
+
pin: drift.pin,
|
|
2460
|
+
reason: e instanceof Error ? e.message : String(e)
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2375
2464
|
async function runPinnedSync(root) {
|
|
2376
2465
|
const installed = await readPinnedCli(join12(root, ".rness"));
|
|
2377
2466
|
if (installed === null) return null;
|
|
@@ -2387,15 +2476,23 @@ async function runPinnedSync(root) {
|
|
|
2387
2476
|
const { stdout, stderr } = await execFileP3(
|
|
2388
2477
|
process.execPath,
|
|
2389
2478
|
[pinned, "sync", "--yes"],
|
|
2390
|
-
{
|
|
2479
|
+
{
|
|
2480
|
+
cwd: root,
|
|
2481
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
2482
|
+
env: childEnv(),
|
|
2483
|
+
timeout: CHILD_DEADLINE_MS,
|
|
2484
|
+
killSignal: "SIGKILL"
|
|
2485
|
+
}
|
|
2391
2486
|
);
|
|
2392
2487
|
return { code: 0, stdout, stderr };
|
|
2393
2488
|
} catch (e) {
|
|
2394
2489
|
const err = e;
|
|
2490
|
+
const deadline = err.killed === true ? `rness sync in .rness did not finish within ${CHILD_DEADLINE_MS / 1e3}s and was stopped
|
|
2491
|
+
` : "";
|
|
2395
2492
|
return {
|
|
2396
2493
|
code: typeof err.code === "number" ? err.code : 1,
|
|
2397
2494
|
stdout: err.stdout ?? "",
|
|
2398
|
-
stderr: err.stderr ?? ""
|
|
2495
|
+
stderr: `${err.stderr ?? ""}${deadline}`
|
|
2399
2496
|
};
|
|
2400
2497
|
}
|
|
2401
2498
|
}
|
|
@@ -2415,6 +2512,19 @@ function summariseSync(stdout) {
|
|
|
2415
2512
|
others
|
|
2416
2513
|
};
|
|
2417
2514
|
}
|
|
2515
|
+
function syncOutcome(result, opts = {}) {
|
|
2516
|
+
const { others } = summariseSync(result.stdout);
|
|
2517
|
+
const wrote = /^(updated|unchanged|stale)\s/m.test(result.stdout);
|
|
2518
|
+
const stderr = result.stderr.split("\n").map((l) => l.trimEnd()).filter((l) => l.trim() !== "");
|
|
2519
|
+
const hints = wrote ? [...others, ...stderr] : others;
|
|
2520
|
+
if (opts.sshWorks === true && /Permission denied \(publickey\)/.test(result.stderr))
|
|
2521
|
+
hints.push(...agentHintLines());
|
|
2522
|
+
return {
|
|
2523
|
+
code: wrote ? 0 : result.code,
|
|
2524
|
+
hints,
|
|
2525
|
+
errors: wrote ? [] : stderr
|
|
2526
|
+
};
|
|
2527
|
+
}
|
|
2418
2528
|
async function syncPinned(root, shown, fallback) {
|
|
2419
2529
|
const rnessDir = join12(root, ".rness");
|
|
2420
2530
|
const installed = await readPinnedCli(rnessDir);
|
|
@@ -2438,7 +2548,13 @@ async function syncPinned(root, shown, fallback) {
|
|
|
2438
2548
|
const { stdout, stderr } = await execFileP3(
|
|
2439
2549
|
process.execPath,
|
|
2440
2550
|
[pinned, "sync", "--yes"],
|
|
2441
|
-
{
|
|
2551
|
+
{
|
|
2552
|
+
cwd: root,
|
|
2553
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
2554
|
+
env: childEnv(),
|
|
2555
|
+
timeout: CHILD_DEADLINE_MS,
|
|
2556
|
+
killSignal: "SIGKILL"
|
|
2557
|
+
}
|
|
2442
2558
|
);
|
|
2443
2559
|
process.stdout.write(stdout);
|
|
2444
2560
|
if (stderr !== "") process.stderr.write(stderr);
|
|
@@ -2467,6 +2583,7 @@ var SCAFFOLD_FILES = [
|
|
|
2467
2583
|
"_gitignore",
|
|
2468
2584
|
".githooks/pre-commit",
|
|
2469
2585
|
".github/workflows/validate.yml",
|
|
2586
|
+
".github/dependabot.yml",
|
|
2470
2587
|
"pnpm-workspace.yaml",
|
|
2471
2588
|
"adr/0000-template.md",
|
|
2472
2589
|
"standards/.gitkeep",
|
|
@@ -2511,7 +2628,7 @@ async function copyScaffold(dest, tokens) {
|
|
|
2511
2628
|
}
|
|
2512
2629
|
|
|
2513
2630
|
// src/version.ts
|
|
2514
|
-
var VERSION = "0.5.
|
|
2631
|
+
var VERSION = "0.5.1";
|
|
2515
2632
|
|
|
2516
2633
|
// src/commands/login.ts
|
|
2517
2634
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -2668,7 +2785,7 @@ import { tmpdir } from "os";
|
|
|
2668
2785
|
import { dirname as dirname6, join as join15, relative as relative2, resolve as resolve3 } from "path";
|
|
2669
2786
|
|
|
2670
2787
|
// src/core/sync-blocks.ts
|
|
2671
|
-
async function syncBlocks(ui, root, shown, inPlace) {
|
|
2788
|
+
async function syncBlocks(ui, root, shown, inPlace, sshWorks) {
|
|
2672
2789
|
if (!ui.session)
|
|
2673
2790
|
return ui.step(
|
|
2674
2791
|
{ doing: "syncing the blocks", git: true },
|
|
@@ -2689,10 +2806,10 @@ async function syncBlocks(ui, root, shown, inPlace) {
|
|
|
2689
2806
|
);
|
|
2690
2807
|
return 1;
|
|
2691
2808
|
}
|
|
2692
|
-
|
|
2693
|
-
for (const line of
|
|
2694
|
-
|
|
2695
|
-
return
|
|
2809
|
+
const outcome = syncOutcome(result, { sshWorks: sshWorks === true });
|
|
2810
|
+
for (const line of outcome.hints) ui.hint(line);
|
|
2811
|
+
for (const line of outcome.errors) ui.error(line);
|
|
2812
|
+
return outcome.code;
|
|
2696
2813
|
}
|
|
2697
2814
|
|
|
2698
2815
|
// src/commands/create.ts
|
|
@@ -2760,7 +2877,12 @@ async function pickRepositories(input) {
|
|
|
2760
2877
|
quiet: true
|
|
2761
2878
|
},
|
|
2762
2879
|
() => provider.listRepositories(org),
|
|
2763
|
-
|
|
2880
|
+
// What the listing offers, not what GitHub returned: the count and the
|
|
2881
|
+
// list below it must agree (spec 0009).
|
|
2882
|
+
(l) => [
|
|
2883
|
+
"listed",
|
|
2884
|
+
`${l.repositories.filter(isMemberRepo).length} repositories of ${org}`
|
|
2885
|
+
]
|
|
2764
2886
|
);
|
|
2765
2887
|
listed = listing.repositories;
|
|
2766
2888
|
if (listing.owner === "user")
|
|
@@ -2779,9 +2901,7 @@ async function pickRepositories(input) {
|
|
|
2779
2901
|
}
|
|
2780
2902
|
const inCatalogue = (name) => Object.hasOwn(input.catalogue, name);
|
|
2781
2903
|
const byLabel = (a, b) => a.label.localeCompare(b.label, "en", { sensitivity: "base" });
|
|
2782
|
-
const candidates = listed.filter(
|
|
2783
|
-
(r) => !r.archived && !r.name.startsWith(".")
|
|
2784
|
-
);
|
|
2904
|
+
const candidates = listed.filter(isMemberRepo);
|
|
2785
2905
|
const padlock = unicode();
|
|
2786
2906
|
const hint = (isPrivate, catalogued) => [isPrivate && !padlock ? "private" : null, catalogued ? "in .rness" : null].filter((h) => h !== null).join(" \xB7 ") || null;
|
|
2787
2907
|
const declarable = candidates.filter((r) => NAME.test(r.name.toLowerCase())).map((r) => {
|
|
@@ -2969,7 +3089,7 @@ async function createCommand(opts, deps = {}) {
|
|
|
2969
3089
|
);
|
|
2970
3090
|
return 2;
|
|
2971
3091
|
}
|
|
2972
|
-
|
|
3092
|
+
let pm = opts.pm !== void 0 && isPackageManager(opts.pm) ? opts.pm : detectPackageManager();
|
|
2973
3093
|
const interactive = opts.yes !== true && terminal.isTty();
|
|
2974
3094
|
const ui = interactive ? await makeUi(terminal) : { ...plainUi };
|
|
2975
3095
|
let sshTest;
|
|
@@ -2984,6 +3104,7 @@ async function createCommand(opts, deps = {}) {
|
|
|
2984
3104
|
}
|
|
2985
3105
|
return sshTest.access;
|
|
2986
3106
|
};
|
|
3107
|
+
const sshWorks = () => sshTest?.access.ok === true;
|
|
2987
3108
|
const chooseHost = async () => {
|
|
2988
3109
|
if (opts.host !== void 0 || opts.https === true) ui.gitIsSilent = true;
|
|
2989
3110
|
if (opts.host !== void 0) return opts.host;
|
|
@@ -3151,12 +3272,13 @@ async function createCommand(opts, deps = {}) {
|
|
|
3151
3272
|
const rnessDir = join15(root, ".rness");
|
|
3152
3273
|
const install = async () => {
|
|
3153
3274
|
if (opts.skipInstall === true) {
|
|
3154
|
-
ui.line("skipped",
|
|
3275
|
+
ui.line("skipped", `install with ${pm} (--skip-install)`);
|
|
3155
3276
|
return;
|
|
3156
3277
|
}
|
|
3278
|
+
const frozen = await hasLockfile(rnessDir, pm);
|
|
3157
3279
|
await ui.step(
|
|
3158
3280
|
{ doing: `installing dependencies with ${pm}` },
|
|
3159
|
-
() => installDependencies(pm, rnessDir),
|
|
3281
|
+
() => installDependencies(pm, rnessDir, { frozen }),
|
|
3160
3282
|
() => ["installed", `dependencies with ${pm}`]
|
|
3161
3283
|
);
|
|
3162
3284
|
};
|
|
@@ -3169,11 +3291,7 @@ async function createCommand(opts, deps = {}) {
|
|
|
3169
3291
|
gitProvider.credentialsFor(contextUrl)
|
|
3170
3292
|
);
|
|
3171
3293
|
ui.line("cloned", `${shown}/.rness (joined ${org})`);
|
|
3172
|
-
|
|
3173
|
-
if (pin !== void 0 && EXACT_VERSION.test(pin) && compareVersions(pin, VERSION) < 0)
|
|
3174
|
-
ui.hint(
|
|
3175
|
-
`${org}/.rness pins @rness/cli ${pin} \u2014 you run ${VERSION}; rness upgrade moves the workspace`
|
|
3176
|
-
);
|
|
3294
|
+
if (opts.pm === void 0) pm = await workspacePackageManager(rnessDir);
|
|
3177
3295
|
await install();
|
|
3178
3296
|
await mkdir3(join15(root, "org"), { recursive: true });
|
|
3179
3297
|
const failures2 = await cloneSelection({
|
|
@@ -3190,14 +3308,28 @@ async function createCommand(opts, deps = {}) {
|
|
|
3190
3308
|
ui,
|
|
3191
3309
|
root,
|
|
3192
3310
|
shown,
|
|
3193
|
-
() => syncCommand({ yes: true, cwd: root })
|
|
3311
|
+
() => syncCommand({ yes: true, cwd: root }),
|
|
3312
|
+
sshWorks()
|
|
3194
3313
|
);
|
|
3195
3314
|
if (code2 !== 0) return code2;
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3315
|
+
const cloned = await clonesIn(root);
|
|
3316
|
+
const remaining = Object.keys(catalogue.repos).filter(
|
|
3317
|
+
(name2) => !cloned.has(name2)
|
|
3318
|
+
);
|
|
3319
|
+
const next2 = [
|
|
3320
|
+
`cd ${shown}`,
|
|
3321
|
+
// The comment above its command, as the new-workspace note does: a
|
|
3322
|
+
// line that wraps in a narrow terminal is a line nobody reads.
|
|
3323
|
+
...remaining.length > 0 ? [
|
|
3324
|
+
`# clone the catalogue repositories you did not pick: ${remaining.join(", ")}`,
|
|
3325
|
+
"rness sync --all"
|
|
3326
|
+
] : [],
|
|
3327
|
+
...failures2.map((f) => `rness add ${f.spec} # failed: ${f.message}`)
|
|
3328
|
+
];
|
|
3329
|
+
if (ui.session) ui.note("Next steps", next2);
|
|
3330
|
+
else process.stdout.write(`
|
|
3331
|
+
${next2.map((l) => ` ${l}`).join("\n")}
|
|
3332
|
+
`);
|
|
3201
3333
|
ui.outro(`You joined ${org} \u2014 your workspace is in ${shown}/`);
|
|
3202
3334
|
return failures2.length > 0 ? 1 : 0;
|
|
3203
3335
|
}
|
|
@@ -3248,7 +3380,8 @@ async function createCommand(opts, deps = {}) {
|
|
|
3248
3380
|
ui,
|
|
3249
3381
|
root,
|
|
3250
3382
|
shown,
|
|
3251
|
-
() => syncCommand({ yes: true, cwd: root })
|
|
3383
|
+
() => syncCommand({ yes: true, cwd: root }),
|
|
3384
|
+
sshWorks()
|
|
3252
3385
|
);
|
|
3253
3386
|
if (code !== 0) return code;
|
|
3254
3387
|
const name = org;
|
|
@@ -3329,8 +3462,8 @@ async function logoutCommand() {
|
|
|
3329
3462
|
|
|
3330
3463
|
// src/commands/upgrade.ts
|
|
3331
3464
|
import { execFile as execFile5 } from "child_process";
|
|
3332
|
-
import { readFile as readFile9, readdir as readdir3 } from "fs/promises";
|
|
3333
|
-
import { basename as
|
|
3465
|
+
import { mkdir as mkdir4, readFile as readFile9, readdir as readdir3 } from "fs/promises";
|
|
3466
|
+
import { basename as basename5, dirname as dirname7, join as join16, relative as relative3 } from "path";
|
|
3334
3467
|
import { promisify as promisify5 } from "util";
|
|
3335
3468
|
var execFileP5 = promisify5(execFile5);
|
|
3336
3469
|
var NOTES = "https://github.com/rness-dev/rness/tree/main/packages/cli#readme";
|
|
@@ -3360,7 +3493,7 @@ async function upgradeCommand(version, opts, terminal = defaultTerminal) {
|
|
|
3360
3493
|
}
|
|
3361
3494
|
try {
|
|
3362
3495
|
const ws = await findWorkspace(cwd);
|
|
3363
|
-
const label = `${
|
|
3496
|
+
const label = `${basename5(ws.root)}/.rness`;
|
|
3364
3497
|
const pin = await readPin(ws.rnessDir);
|
|
3365
3498
|
if (pin === null) {
|
|
3366
3499
|
process.stderr.write(
|
|
@@ -3377,7 +3510,7 @@ async function upgradeCommand(version, opts, terminal = defaultTerminal) {
|
|
|
3377
3510
|
return 2;
|
|
3378
3511
|
}
|
|
3379
3512
|
const ui = interactive ? await makeUi(terminal) : { ...plainUi };
|
|
3380
|
-
ui.intro(`Upgrade workspace ${
|
|
3513
|
+
ui.intro(`Upgrade workspace ${basename5(ws.root)}`);
|
|
3381
3514
|
const target = version ?? await ui.step(
|
|
3382
3515
|
{ doing: "asking the registry for the latest release", quiet: true },
|
|
3383
3516
|
() => latestVersion(),
|
|
@@ -3453,6 +3586,20 @@ async function upgradeCommand(version, opts, terminal = defaultTerminal) {
|
|
|
3453
3586
|
ui.warn(`${shown} names @rness/cli@${pin.spec}; update it by hand`);
|
|
3454
3587
|
}
|
|
3455
3588
|
}
|
|
3589
|
+
const dependabot = join16(ws.rnessDir, ".github", "dependabot.yml");
|
|
3590
|
+
if (await exists(dependabot)) {
|
|
3591
|
+
ui.hint(".github/dependabot.yml is already there \u2014 left as it is");
|
|
3592
|
+
} else {
|
|
3593
|
+
await mkdir4(dirname7(dependabot), { recursive: true });
|
|
3594
|
+
await writeFileAtomic(
|
|
3595
|
+
dependabot,
|
|
3596
|
+
await readFile9(join16(scaffoldDir(), ".github", "dependabot.yml"), "utf8")
|
|
3597
|
+
);
|
|
3598
|
+
ui.line(
|
|
3599
|
+
"wrote",
|
|
3600
|
+
".rness/.github/dependabot.yml (@rness/cli now moves by pull request)"
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3456
3603
|
try {
|
|
3457
3604
|
await ui.step(
|
|
3458
3605
|
{ doing: `installing dependencies with ${pm}` },
|
|
@@ -3480,12 +3627,12 @@ async function upgradeCommand(version, opts, terminal = defaultTerminal) {
|
|
|
3480
3627
|
return 1;
|
|
3481
3628
|
}
|
|
3482
3629
|
ui.gitIsSilent = true;
|
|
3483
|
-
const code = await syncBlocks(ui, ws.root,
|
|
3630
|
+
const code = await syncBlocks(ui, ws.root, basename5(ws.root));
|
|
3484
3631
|
if (code !== 0) return code;
|
|
3485
3632
|
const rel = relative3(cwd, ws.rnessDir) || ".";
|
|
3486
3633
|
const next = [
|
|
3487
3634
|
`git -C ${rel} add -A && git -C ${rel} commit -m "chore: rness ${target}"`,
|
|
3488
|
-
|
|
3635
|
+
"# teammates: git pull \u2014 the next rness command installs it"
|
|
3489
3636
|
];
|
|
3490
3637
|
if (ui.session) {
|
|
3491
3638
|
ui.note("Next steps", next);
|
|
@@ -3509,15 +3656,23 @@ async function upgradeCommand(version, opts, terminal = defaultTerminal) {
|
|
|
3509
3656
|
|
|
3510
3657
|
// src/core/blocks.ts
|
|
3511
3658
|
import { join as join17 } from "path";
|
|
3659
|
+
var PROBLEM = /* @__PURE__ */ new Set(["stale", "malformed"]);
|
|
3512
3660
|
async function checkBlocks({
|
|
3513
3661
|
root,
|
|
3514
3662
|
rnessDir,
|
|
3515
3663
|
manifest,
|
|
3516
3664
|
org
|
|
3517
3665
|
}) {
|
|
3518
|
-
const
|
|
3519
|
-
const
|
|
3520
|
-
|
|
3666
|
+
const checks = [];
|
|
3667
|
+
const note = (label, status2, message = null) => {
|
|
3668
|
+
checks.push({ label, status: status2, message });
|
|
3669
|
+
};
|
|
3670
|
+
const derive = () => ({
|
|
3671
|
+
problems: checks.filter((c) => PROBLEM.has(c.status) && c.message !== null).map((c) => c.message),
|
|
3672
|
+
warnings: checks.filter((c) => !PROBLEM.has(c.status) && c.message !== null).map((c) => c.message),
|
|
3673
|
+
checks
|
|
3674
|
+
});
|
|
3675
|
+
if (!await exists(join17(root, "org"))) return derive();
|
|
3521
3676
|
const targets = [{ scope: null, dir: root, depth: 0, label: "AGENTS.md" }];
|
|
3522
3677
|
for (const [name, entry] of Object.entries(manifest.scopes)) {
|
|
3523
3678
|
const segments = entry.path.split("/");
|
|
@@ -3530,18 +3685,26 @@ async function checkBlocks({
|
|
|
3530
3685
|
}
|
|
3531
3686
|
for (const t of targets) {
|
|
3532
3687
|
if (!await exists(t.dir)) {
|
|
3533
|
-
|
|
3688
|
+
note(
|
|
3689
|
+
t.label,
|
|
3690
|
+
"missing-dir",
|
|
3691
|
+
`${t.label}: directory not present, block not checked`
|
|
3692
|
+
);
|
|
3534
3693
|
continue;
|
|
3535
3694
|
}
|
|
3536
3695
|
const text = await readOrNull(join17(t.dir, "AGENTS.md"));
|
|
3537
3696
|
const lines = text === null ? [] : text.split(/\r?\n/);
|
|
3538
3697
|
const found = findBlock(lines);
|
|
3539
3698
|
if (found.kind === "none") {
|
|
3540
|
-
|
|
3699
|
+
note(
|
|
3700
|
+
t.label,
|
|
3701
|
+
"no-block",
|
|
3702
|
+
`${t.label}: no rness block yet (run rness sync)`
|
|
3703
|
+
);
|
|
3541
3704
|
continue;
|
|
3542
3705
|
}
|
|
3543
3706
|
if (found.kind === "error") {
|
|
3544
|
-
|
|
3707
|
+
note(t.label, "malformed", `${t.label}: ${found.message}`);
|
|
3545
3708
|
continue;
|
|
3546
3709
|
}
|
|
3547
3710
|
const context = await assembleContext({
|
|
@@ -3555,11 +3718,12 @@ async function checkBlocks({
|
|
|
3555
3718
|
depth: t.depth,
|
|
3556
3719
|
context
|
|
3557
3720
|
});
|
|
3558
|
-
if (
|
|
3559
|
-
|
|
3560
|
-
|
|
3721
|
+
if (isCurrentBlock(lines.slice(found.begin, found.end + 1), fresh2.hash))
|
|
3722
|
+
note(t.label, "current");
|
|
3723
|
+
else
|
|
3724
|
+
note(t.label, "stale", `${t.label}: stale rness block (run rness sync)`);
|
|
3561
3725
|
}
|
|
3562
|
-
return
|
|
3726
|
+
return derive();
|
|
3563
3727
|
}
|
|
3564
3728
|
|
|
3565
3729
|
// src/core/contract.ts
|
|
@@ -3619,7 +3783,7 @@ async function checkContract(rnessDir) {
|
|
|
3619
3783
|
}
|
|
3620
3784
|
|
|
3621
3785
|
// src/commands/validate.ts
|
|
3622
|
-
async function validateCommand(opts) {
|
|
3786
|
+
async function validateCommand(opts, deps = {}) {
|
|
3623
3787
|
const cwd = opts.cwd ?? process.cwd();
|
|
3624
3788
|
try {
|
|
3625
3789
|
const ws = await findWorkspace(cwd);
|
|
@@ -3636,13 +3800,29 @@ async function validateCommand(opts) {
|
|
|
3636
3800
|
});
|
|
3637
3801
|
problems.push(...blocks.problems);
|
|
3638
3802
|
warnings.push(...blocks.warnings);
|
|
3639
|
-
|
|
3803
|
+
const ui = await makeUi(deps.terminal ?? defaultTerminal);
|
|
3804
|
+
if (!ui.session) {
|
|
3805
|
+
for (const p of problems) process.stderr.write(`${p}
|
|
3640
3806
|
`);
|
|
3641
|
-
|
|
3807
|
+
for (const w of warnings) process.stderr.write(`${warn(w)}
|
|
3642
3808
|
`);
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3809
|
+
if (problems.length > 0) return 1;
|
|
3810
|
+
process.stdout.write("context ok\n");
|
|
3811
|
+
return 0;
|
|
3812
|
+
}
|
|
3813
|
+
ui.intro(`Validate ${org}`);
|
|
3814
|
+
for (const c of blocks.checks)
|
|
3815
|
+
if (c.message === null) ui.line("current", c.label);
|
|
3816
|
+
else if (c.status === "stale" || c.status === "malformed")
|
|
3817
|
+
ui.error(c.message);
|
|
3818
|
+
else ui.warn(c.message);
|
|
3819
|
+
for (const p of problems) if (!blocks.problems.includes(p)) ui.error(p);
|
|
3820
|
+
for (const w of warnings) if (!blocks.warnings.includes(w)) ui.warn(w);
|
|
3821
|
+
ui.outro(
|
|
3822
|
+
problems.length > 0 ? "Context mismatch" : "Context OK",
|
|
3823
|
+
problems.length > 0 ? "error" : "done"
|
|
3824
|
+
);
|
|
3825
|
+
return problems.length > 0 ? 1 : 0;
|
|
3646
3826
|
} catch (e) {
|
|
3647
3827
|
return reportError(e);
|
|
3648
3828
|
}
|
|
@@ -3833,13 +4013,16 @@ export {
|
|
|
3833
4013
|
addCommand,
|
|
3834
4014
|
renderMarkdown,
|
|
3835
4015
|
contextCommand,
|
|
4016
|
+
pinnedBinPath,
|
|
3836
4017
|
findDelegate,
|
|
3837
4018
|
PACKAGE_MANAGERS,
|
|
3838
4019
|
isPackageManager,
|
|
3839
4020
|
detectPackageManager,
|
|
3840
4021
|
packageManagerVersion,
|
|
3841
4022
|
installDependencies,
|
|
4023
|
+
pinDrift,
|
|
3842
4024
|
driftWarning,
|
|
4025
|
+
ensureInstalled,
|
|
3843
4026
|
SCAFFOLD_FILES,
|
|
3844
4027
|
scaffoldDir,
|
|
3845
4028
|
copyScaffold,
|
|
@@ -3854,4 +4037,4 @@ export {
|
|
|
3854
4037
|
validateCommand,
|
|
3855
4038
|
run
|
|
3856
4039
|
};
|
|
3857
|
-
//# sourceMappingURL=chunk-
|
|
4040
|
+
//# sourceMappingURL=chunk-C6HBGV5V.js.map
|