@getmonoceros/workbench 1.13.3 → 1.14.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/dist/bin.js +495 -126
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -254,25 +254,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
254
254
|
const separatorIdx = argv.indexOf("--");
|
|
255
255
|
if (helpIdx === -1) return null;
|
|
256
256
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
257
|
-
const
|
|
257
|
+
const path21 = [];
|
|
258
258
|
const tokens = argv.slice(
|
|
259
259
|
0,
|
|
260
260
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
261
261
|
);
|
|
262
262
|
let cursor = main2;
|
|
263
263
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
264
|
-
|
|
264
|
+
path21.push(mainName);
|
|
265
265
|
for (const tok of tokens) {
|
|
266
266
|
if (tok.startsWith("-")) continue;
|
|
267
267
|
const subs = cursor.subCommands ?? {};
|
|
268
268
|
if (tok in subs) {
|
|
269
269
|
cursor = subs[tok];
|
|
270
|
-
|
|
270
|
+
path21.push(tok);
|
|
271
271
|
continue;
|
|
272
272
|
}
|
|
273
273
|
break;
|
|
274
274
|
}
|
|
275
|
-
return { path:
|
|
275
|
+
return { path: path21, cmd: cursor };
|
|
276
276
|
}
|
|
277
277
|
async function maybeRenderHelp(argv, main2) {
|
|
278
278
|
const hit = detectHelpRequest(argv, main2);
|
|
@@ -622,6 +622,9 @@ function containersDir(home = monocerosHome()) {
|
|
|
622
622
|
function containerDir(name, home = monocerosHome()) {
|
|
623
623
|
return path.join(containersDir(home), name);
|
|
624
624
|
}
|
|
625
|
+
function containerLogsDir(name, home = monocerosHome()) {
|
|
626
|
+
return path.join(containerDir(name, home), "logs");
|
|
627
|
+
}
|
|
625
628
|
function monocerosConfigPath(home = monocerosHome()) {
|
|
626
629
|
return path.join(home, "monoceros-config.yml");
|
|
627
630
|
}
|
|
@@ -736,25 +739,20 @@ function resolveGitUserFields(user, vars) {
|
|
|
736
739
|
};
|
|
737
740
|
return { name: resolve(user.name), email: resolve(user.email) };
|
|
738
741
|
}
|
|
739
|
-
function
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const
|
|
744
|
-
for (const [key, value] of Object.entries(options)) {
|
|
742
|
+
function interpolateFeatureOptions(features, vars) {
|
|
743
|
+
return features.map((f) => {
|
|
744
|
+
if (!f.options) return f;
|
|
745
|
+
const opts = {};
|
|
746
|
+
for (const [key, value] of Object.entries(f.options)) {
|
|
745
747
|
if (typeof value !== "string") {
|
|
746
|
-
|
|
748
|
+
opts[key] = value;
|
|
747
749
|
continue;
|
|
748
750
|
}
|
|
749
751
|
const r = interpolate(value, vars);
|
|
750
|
-
|
|
751
|
-
missing.push({ location: `features.${ref}.${key}`, name });
|
|
752
|
-
}
|
|
753
|
-
next[key] = r.value;
|
|
752
|
+
opts[key] = r.missing.length > 0 ? "" : r.value.trim();
|
|
754
753
|
}
|
|
755
|
-
|
|
756
|
-
}
|
|
757
|
-
return { features: out, missing };
|
|
754
|
+
return { ...f, options: opts };
|
|
755
|
+
});
|
|
758
756
|
}
|
|
759
757
|
function buildEnvStub(name) {
|
|
760
758
|
return `# Secrets and values for \${VAR} references in ${name}.yml.
|
|
@@ -985,6 +983,10 @@ var ANSI_UNDERLINE2 = `${ESC}4m`;
|
|
|
985
983
|
var ANSI_CYAN2 = `${ESC}36m`;
|
|
986
984
|
var ANSI_GREY2 = `${ESC}90m`;
|
|
987
985
|
var ANSI_RESET2 = `${ESC}0m`;
|
|
986
|
+
var ANSI_RE2 = /\x1b\[[0-9;]*m/g;
|
|
987
|
+
function stripAnsi(s) {
|
|
988
|
+
return s.replace(ANSI_RE2, "");
|
|
989
|
+
}
|
|
988
990
|
function makeWrap(isTty2) {
|
|
989
991
|
return (s, ...codes) => isTty2 ? codes.join("") + s + ANSI_RESET2 : s;
|
|
990
992
|
}
|
|
@@ -3013,13 +3015,17 @@ function addInstallUrlToDoc(doc, url) {
|
|
|
3013
3015
|
function addFeatureToDoc(doc, ref, options = {}, displayName) {
|
|
3014
3016
|
const seq = ensureSeq(doc, "features");
|
|
3015
3017
|
const label = displayName ?? ref;
|
|
3018
|
+
const summary = loadFeatureManifestSummary(ref);
|
|
3019
|
+
const hints = featureOptionHints(summary, ref, Object.keys(options));
|
|
3020
|
+
const mergedOptions = { ...options };
|
|
3021
|
+
for (const h of hints) mergedOptions[h.key] = h.placeholder;
|
|
3016
3022
|
for (const item of seq.items) {
|
|
3017
3023
|
if (!isMap2(item)) continue;
|
|
3018
3024
|
const itemRef = item.get("ref");
|
|
3019
3025
|
if (itemRef !== ref) continue;
|
|
3020
3026
|
const itemJs = item.toJS(doc);
|
|
3021
3027
|
const existingJs = itemJs.options ?? {};
|
|
3022
|
-
if (JSON.stringify(existingJs) === JSON.stringify(
|
|
3028
|
+
if (JSON.stringify(existingJs) === JSON.stringify(mergedOptions)) {
|
|
3023
3029
|
return false;
|
|
3024
3030
|
}
|
|
3025
3031
|
throw new Error(
|
|
@@ -3028,10 +3034,9 @@ function addFeatureToDoc(doc, ref, options = {}, displayName) {
|
|
|
3028
3034
|
}
|
|
3029
3035
|
const entry2 = new YAMLMap2();
|
|
3030
3036
|
entry2.set("ref", ref);
|
|
3031
|
-
if (Object.keys(
|
|
3032
|
-
entry2.set("options",
|
|
3037
|
+
if (Object.keys(mergedOptions).length > 0) {
|
|
3038
|
+
entry2.set("options", mergedOptions);
|
|
3033
3039
|
}
|
|
3034
|
-
const summary = loadFeatureManifestSummary(ref);
|
|
3035
3040
|
const headerBefore = buildFeatureHeaderCommentBefore(
|
|
3036
3041
|
summary,
|
|
3037
3042
|
FEATURE_HEADER_WIDTH
|
|
@@ -3040,12 +3045,6 @@ function addFeatureToDoc(doc, ref, options = {}, displayName) {
|
|
|
3040
3045
|
entry2.commentBefore = headerBefore;
|
|
3041
3046
|
entry2.spaceBefore = true;
|
|
3042
3047
|
}
|
|
3043
|
-
const hints = featureOptionHints(summary, ref, Object.keys(options));
|
|
3044
|
-
if (hints.length > 0) {
|
|
3045
|
-
const commentLines = [" options:"];
|
|
3046
|
-
for (const h of hints) commentLines.push(` ${h.key}: ${h.placeholder}`);
|
|
3047
|
-
entry2.comment = commentLines.join("\n");
|
|
3048
|
-
}
|
|
3049
3048
|
seq.add(entry2);
|
|
3050
3049
|
return true;
|
|
3051
3050
|
}
|
|
@@ -3170,8 +3169,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
|
|
|
3170
3169
|
if (!isMap2(item)) return false;
|
|
3171
3170
|
const url = item.get("url");
|
|
3172
3171
|
if (url === urlOrPath) return true;
|
|
3173
|
-
const
|
|
3174
|
-
const effectivePath = typeof
|
|
3172
|
+
const path21 = item.get("path");
|
|
3173
|
+
const effectivePath = typeof path21 === "string" ? path21 : typeof url === "string" ? deriveRepoName(url) : void 0;
|
|
3175
3174
|
return effectivePath === urlOrPath;
|
|
3176
3175
|
});
|
|
3177
3176
|
if (idx < 0) return false;
|
|
@@ -3264,7 +3263,7 @@ async function runAddRepo(input) {
|
|
|
3264
3263
|
"Missing repo URL. Usage: monoceros add-repo <containername> <url>."
|
|
3265
3264
|
);
|
|
3266
3265
|
}
|
|
3267
|
-
const
|
|
3266
|
+
const path21 = (input.path ?? deriveRepoName(url)).trim();
|
|
3268
3267
|
const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
|
|
3269
3268
|
const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
|
|
3270
3269
|
if (hasName !== hasEmail) {
|
|
@@ -3301,7 +3300,7 @@ async function runAddRepo(input) {
|
|
|
3301
3300
|
const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
|
|
3302
3301
|
const entry2 = {
|
|
3303
3302
|
url,
|
|
3304
|
-
path:
|
|
3303
|
+
path: path21,
|
|
3305
3304
|
...hasName && hasEmail ? {
|
|
3306
3305
|
gitUser: {
|
|
3307
3306
|
name: input.gitName.trim(),
|
|
@@ -4240,10 +4239,292 @@ function solutionConfigToCreateOptions(config, featureDefaults = {}) {
|
|
|
4240
4239
|
return result;
|
|
4241
4240
|
}
|
|
4242
4241
|
|
|
4242
|
+
// src/apply/apply-log.ts
|
|
4243
|
+
import { createWriteStream, mkdirSync } from "fs";
|
|
4244
|
+
import path11 from "path";
|
|
4245
|
+
import { Writable } from "stream";
|
|
4246
|
+
function safeIsoStamp(d) {
|
|
4247
|
+
return d.toISOString().replace(/[:.]/g, "-");
|
|
4248
|
+
}
|
|
4249
|
+
function createApplyLog(opts) {
|
|
4250
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
4251
|
+
const dir = containerLogsDir(opts.name, opts.home);
|
|
4252
|
+
mkdirSync(dir, { recursive: true });
|
|
4253
|
+
const file = `apply-${opts.name}-${safeIsoStamp(now)}.log`;
|
|
4254
|
+
const fullPath = path11.join(dir, file);
|
|
4255
|
+
const stream = createWriteStream(fullPath, { flags: "w" });
|
|
4256
|
+
const header = [
|
|
4257
|
+
`# monoceros apply log`,
|
|
4258
|
+
`# command: monoceros apply ${opts.name}`,
|
|
4259
|
+
`# started: ${now.toISOString()}`,
|
|
4260
|
+
`# cli-version: ${opts.cliVersion}`,
|
|
4261
|
+
`# config: ${opts.configPath}`,
|
|
4262
|
+
`# host: ${process.platform}/${process.arch} node ${process.version}`,
|
|
4263
|
+
``,
|
|
4264
|
+
``
|
|
4265
|
+
].join("\n");
|
|
4266
|
+
stream.write(header);
|
|
4267
|
+
const sink = new Writable({
|
|
4268
|
+
write(chunk, _enc, cb) {
|
|
4269
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
4270
|
+
stream.write(stripAnsi(text), cb);
|
|
4271
|
+
}
|
|
4272
|
+
});
|
|
4273
|
+
let closed = false;
|
|
4274
|
+
return {
|
|
4275
|
+
path: fullPath,
|
|
4276
|
+
stream,
|
|
4277
|
+
sink,
|
|
4278
|
+
close: () => new Promise((resolve) => {
|
|
4279
|
+
if (closed) {
|
|
4280
|
+
resolve();
|
|
4281
|
+
return;
|
|
4282
|
+
}
|
|
4283
|
+
closed = true;
|
|
4284
|
+
sink.end(() => {
|
|
4285
|
+
stream.end(() => resolve());
|
|
4286
|
+
});
|
|
4287
|
+
})
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
function teeApplyLogger(base, sink) {
|
|
4291
|
+
const write = (level, msg) => {
|
|
4292
|
+
sink.write(`[${level}] ${msg}
|
|
4293
|
+
`);
|
|
4294
|
+
};
|
|
4295
|
+
const wrapped = {
|
|
4296
|
+
info: (msg) => {
|
|
4297
|
+
base.info(msg);
|
|
4298
|
+
write("info", msg);
|
|
4299
|
+
},
|
|
4300
|
+
success: (msg) => {
|
|
4301
|
+
base.success(msg);
|
|
4302
|
+
write("ok", msg);
|
|
4303
|
+
},
|
|
4304
|
+
warn: (msg) => {
|
|
4305
|
+
(base.warn ?? base.info)(msg);
|
|
4306
|
+
write("warn", msg);
|
|
4307
|
+
}
|
|
4308
|
+
};
|
|
4309
|
+
if (base.section) wrapped.section = base.section.bind(base);
|
|
4310
|
+
return wrapped;
|
|
4311
|
+
}
|
|
4312
|
+
|
|
4313
|
+
// src/apply/apply-progress.ts
|
|
4314
|
+
import { Writable as Writable2 } from "stream";
|
|
4315
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
4316
|
+
var FRAME_INTERVAL_MS = 80;
|
|
4317
|
+
var TAIL_LINES = 15;
|
|
4318
|
+
var PHASE_TRIGGERS = [
|
|
4319
|
+
// Compose mode triggers a feature/layer build before the container
|
|
4320
|
+
// is created — distinct phase, often the longest single step.
|
|
4321
|
+
{ pattern: /Start: Run: docker build/i, label: "building feature layers\u2026" },
|
|
4322
|
+
// Image mode jumps straight from "preparing…" into the docker run
|
|
4323
|
+
// that pulls (if needed) + creates + starts the container.
|
|
4324
|
+
{ pattern: /Start: Run: docker run/i, label: "starting container\u2026" },
|
|
4325
|
+
{ pattern: /Running the postCreateCommand/i, label: "running postCreate\u2026" }
|
|
4326
|
+
];
|
|
4327
|
+
function createApplyProgress(opts) {
|
|
4328
|
+
const out = opts.out;
|
|
4329
|
+
const now = opts.now ?? (() => Date.now());
|
|
4330
|
+
const startedAt = now();
|
|
4331
|
+
let phase = "preparing\u2026";
|
|
4332
|
+
let frameIdx = 0;
|
|
4333
|
+
let timer = null;
|
|
4334
|
+
let stopped = false;
|
|
4335
|
+
const tail = [];
|
|
4336
|
+
let lineBuf = "";
|
|
4337
|
+
const writeSpinner = () => {
|
|
4338
|
+
if (!opts.interactive || stopped) return;
|
|
4339
|
+
out.write(`\r\x1B[K${FRAMES[frameIdx]} ${phase}`);
|
|
4340
|
+
};
|
|
4341
|
+
const clearLine = () => {
|
|
4342
|
+
if (!opts.interactive) return;
|
|
4343
|
+
out.write("\r\x1B[K");
|
|
4344
|
+
};
|
|
4345
|
+
const setPhase = (label) => {
|
|
4346
|
+
if (phase === label) return;
|
|
4347
|
+
phase = label;
|
|
4348
|
+
if (opts.interactive) {
|
|
4349
|
+
writeSpinner();
|
|
4350
|
+
} else {
|
|
4351
|
+
out.write(`> ${label}
|
|
4352
|
+
`);
|
|
4353
|
+
}
|
|
4354
|
+
};
|
|
4355
|
+
const println = (line) => {
|
|
4356
|
+
clearLine();
|
|
4357
|
+
const withNewline = line.endsWith("\n") ? line : `${line}
|
|
4358
|
+
`;
|
|
4359
|
+
out.write(withNewline);
|
|
4360
|
+
writeSpinner();
|
|
4361
|
+
};
|
|
4362
|
+
const fmtElapsed = () => {
|
|
4363
|
+
const ms = now() - startedAt;
|
|
4364
|
+
const totalSec = Math.max(0, Math.round(ms / 1e3));
|
|
4365
|
+
const m = Math.floor(totalSec / 60);
|
|
4366
|
+
const s = totalSec % 60;
|
|
4367
|
+
return m > 0 ? `${m}m ${s}s` : `${s}s`;
|
|
4368
|
+
};
|
|
4369
|
+
const stop = () => {
|
|
4370
|
+
if (timer) {
|
|
4371
|
+
clearInterval(timer);
|
|
4372
|
+
timer = null;
|
|
4373
|
+
}
|
|
4374
|
+
if (!stopped) {
|
|
4375
|
+
stopped = true;
|
|
4376
|
+
clearLine();
|
|
4377
|
+
}
|
|
4378
|
+
};
|
|
4379
|
+
const succeed = (label) => {
|
|
4380
|
+
stop();
|
|
4381
|
+
const text = label ?? `container ready (${fmtElapsed()})`;
|
|
4382
|
+
out.write(`\u2714 ${text}
|
|
4383
|
+
`);
|
|
4384
|
+
};
|
|
4385
|
+
const fail = () => {
|
|
4386
|
+
stop();
|
|
4387
|
+
return { tailLines: [...tail] };
|
|
4388
|
+
};
|
|
4389
|
+
const streamSink = new Writable2({
|
|
4390
|
+
write(chunk, _enc, cb) {
|
|
4391
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
4392
|
+
lineBuf += stripAnsi(text);
|
|
4393
|
+
let nl;
|
|
4394
|
+
while ((nl = lineBuf.indexOf("\n")) !== -1) {
|
|
4395
|
+
const line = lineBuf.slice(0, nl);
|
|
4396
|
+
lineBuf = lineBuf.slice(nl + 1);
|
|
4397
|
+
if (line.length === 0) continue;
|
|
4398
|
+
tail.push(line);
|
|
4399
|
+
if (tail.length > TAIL_LINES) tail.shift();
|
|
4400
|
+
for (const trig of PHASE_TRIGGERS) {
|
|
4401
|
+
if (trig.pattern.test(line)) {
|
|
4402
|
+
setPhase(trig.label);
|
|
4403
|
+
break;
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
cb();
|
|
4408
|
+
}
|
|
4409
|
+
});
|
|
4410
|
+
if (opts.interactive) {
|
|
4411
|
+
writeSpinner();
|
|
4412
|
+
timer = setInterval(() => {
|
|
4413
|
+
frameIdx = (frameIdx + 1) % FRAMES.length;
|
|
4414
|
+
writeSpinner();
|
|
4415
|
+
}, FRAME_INTERVAL_MS);
|
|
4416
|
+
timer.unref?.();
|
|
4417
|
+
}
|
|
4418
|
+
return {
|
|
4419
|
+
setPhase,
|
|
4420
|
+
println,
|
|
4421
|
+
succeed,
|
|
4422
|
+
fail,
|
|
4423
|
+
streamSink
|
|
4424
|
+
};
|
|
4425
|
+
}
|
|
4426
|
+
function progressTeeLogger(progress, sink) {
|
|
4427
|
+
const fileLine = (level, msg) => {
|
|
4428
|
+
sink.write(`[${level}] ${msg}
|
|
4429
|
+
`);
|
|
4430
|
+
};
|
|
4431
|
+
return {
|
|
4432
|
+
info: (msg) => {
|
|
4433
|
+
progress.println(msg);
|
|
4434
|
+
fileLine("info", msg);
|
|
4435
|
+
},
|
|
4436
|
+
success: (msg) => {
|
|
4437
|
+
progress.println(`\u2714 ${msg}`);
|
|
4438
|
+
fileLine("ok", msg);
|
|
4439
|
+
},
|
|
4440
|
+
warn: (msg) => {
|
|
4441
|
+
progress.println(`! ${msg}`);
|
|
4442
|
+
fileLine("warn", msg);
|
|
4443
|
+
}
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4446
|
+
function logFileOnlyLogger(sink) {
|
|
4447
|
+
const fileLine = (level, msg) => {
|
|
4448
|
+
sink.write(`[${level}] ${msg}
|
|
4449
|
+
`);
|
|
4450
|
+
};
|
|
4451
|
+
return {
|
|
4452
|
+
info: (msg) => fileLine("info", msg),
|
|
4453
|
+
success: (msg) => fileLine("ok", msg),
|
|
4454
|
+
warn: (msg) => fileLine("warn", msg)
|
|
4455
|
+
};
|
|
4456
|
+
}
|
|
4457
|
+
function createSigintAbort(deps) {
|
|
4458
|
+
let aborted = false;
|
|
4459
|
+
return () => {
|
|
4460
|
+
if (aborted) return;
|
|
4461
|
+
aborted = true;
|
|
4462
|
+
if (deps.progress) deps.progress.fail();
|
|
4463
|
+
deps.out.write("\n\u23F9 aborted\n");
|
|
4464
|
+
deps.log.stream.write("\n[abort] SIGINT received\n");
|
|
4465
|
+
void deps.log.close().finally(() => {
|
|
4466
|
+
deps.out.write(`
|
|
4467
|
+
${deps.formatLogPointer(deps.log.path)}
|
|
4468
|
+
`);
|
|
4469
|
+
deps.onExit();
|
|
4470
|
+
});
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
// src/apply/apply-summary.ts
|
|
4475
|
+
function shortFeatureName(ref) {
|
|
4476
|
+
const withoutTag = ref.replace(/:[^:/@]+$/, "");
|
|
4477
|
+
const idx = withoutTag.lastIndexOf("/");
|
|
4478
|
+
return idx >= 0 ? withoutTag.slice(idx + 1) : withoutTag;
|
|
4479
|
+
}
|
|
4480
|
+
function shortRepoName(repo) {
|
|
4481
|
+
const last = repo.path.split("/").filter(Boolean).pop();
|
|
4482
|
+
return last && last.length > 0 ? last : repo.url;
|
|
4483
|
+
}
|
|
4484
|
+
function buildApplySummary(opts) {
|
|
4485
|
+
const lines = [];
|
|
4486
|
+
if (opts.languages.length > 0) {
|
|
4487
|
+
lines.push({ label: "Languages", values: opts.languages });
|
|
4488
|
+
}
|
|
4489
|
+
if (opts.services.length > 0) {
|
|
4490
|
+
lines.push({
|
|
4491
|
+
label: "Services",
|
|
4492
|
+
values: opts.services.map((s) => s.name)
|
|
4493
|
+
});
|
|
4494
|
+
}
|
|
4495
|
+
if (opts.features && Object.keys(opts.features).length > 0) {
|
|
4496
|
+
lines.push({
|
|
4497
|
+
label: "Features",
|
|
4498
|
+
values: Object.keys(opts.features).map(shortFeatureName)
|
|
4499
|
+
});
|
|
4500
|
+
}
|
|
4501
|
+
if (opts.repos && opts.repos.length > 0) {
|
|
4502
|
+
lines.push({
|
|
4503
|
+
label: "Repositories",
|
|
4504
|
+
values: opts.repos.map(shortRepoName)
|
|
4505
|
+
});
|
|
4506
|
+
}
|
|
4507
|
+
if (opts.ports && opts.ports.length > 0) {
|
|
4508
|
+
lines.push({ label: "Ports", values: opts.ports.map(String) });
|
|
4509
|
+
}
|
|
4510
|
+
if (opts.aptPackages && opts.aptPackages.length > 0) {
|
|
4511
|
+
lines.push({ label: "APT packages", values: opts.aptPackages });
|
|
4512
|
+
}
|
|
4513
|
+
if (opts.installUrls && opts.installUrls.length > 0) {
|
|
4514
|
+
lines.push({ label: "Install URLs", values: opts.installUrls });
|
|
4515
|
+
}
|
|
4516
|
+
return lines;
|
|
4517
|
+
}
|
|
4518
|
+
function formatApplySummary(lines) {
|
|
4519
|
+
if (lines.length === 0) return "";
|
|
4520
|
+
const labelWidth = Math.max(...lines.map((l) => l.label.length));
|
|
4521
|
+
return lines.map((l) => ` ${l.label.padEnd(labelWidth)} ${cyan2(l.values.join(", "))}`).join("\n");
|
|
4522
|
+
}
|
|
4523
|
+
|
|
4243
4524
|
// src/devcontainer/compose.ts
|
|
4244
4525
|
import { spawn as spawn5 } from "child_process";
|
|
4245
4526
|
import { existsSync as existsSync6 } from "fs";
|
|
4246
|
-
import
|
|
4527
|
+
import path13 from "path";
|
|
4247
4528
|
import { consola as consola9 } from "consola";
|
|
4248
4529
|
|
|
4249
4530
|
// src/util/mask-secrets.ts
|
|
@@ -4306,7 +4587,7 @@ function createSecretMaskStream() {
|
|
|
4306
4587
|
import { spawn as spawn4 } from "child_process";
|
|
4307
4588
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4308
4589
|
import { createRequire } from "module";
|
|
4309
|
-
import
|
|
4590
|
+
import path12 from "path";
|
|
4310
4591
|
|
|
4311
4592
|
// src/devcontainer/runtime-pull-hint.ts
|
|
4312
4593
|
import { Transform as Transform2 } from "stream";
|
|
@@ -4357,7 +4638,7 @@ function devcontainerCliPath() {
|
|
|
4357
4638
|
if (!binEntry) {
|
|
4358
4639
|
throw new Error("Could not resolve @devcontainers/cli bin entry.");
|
|
4359
4640
|
}
|
|
4360
|
-
cachedBinaryPath =
|
|
4641
|
+
cachedBinaryPath = path12.resolve(path12.dirname(pkgJsonPath), binEntry);
|
|
4361
4642
|
return cachedBinaryPath;
|
|
4362
4643
|
}
|
|
4363
4644
|
var spawnDevcontainer = (args, cwd, options = {}) => {
|
|
@@ -4397,8 +4678,20 @@ var spawnDevcontainer = (args, cwd, options = {}) => {
|
|
|
4397
4678
|
return;
|
|
4398
4679
|
}
|
|
4399
4680
|
const pullHint = { hinted: false };
|
|
4400
|
-
child.stdout?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint))
|
|
4401
|
-
child.stderr?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint))
|
|
4681
|
+
const stdoutPipe = child.stdout?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint));
|
|
4682
|
+
const stderrPipe = child.stderr?.pipe(createSecretMaskStream()).pipe(createRuntimePullHintStream(pullHint));
|
|
4683
|
+
if (!options.silent) {
|
|
4684
|
+
stdoutPipe?.pipe(process.stdout);
|
|
4685
|
+
stderrPipe?.pipe(process.stderr);
|
|
4686
|
+
}
|
|
4687
|
+
if (options.logSink) {
|
|
4688
|
+
stdoutPipe?.pipe(options.logSink, { end: false });
|
|
4689
|
+
stderrPipe?.pipe(options.logSink, { end: false });
|
|
4690
|
+
}
|
|
4691
|
+
if (options.progressSink) {
|
|
4692
|
+
stdoutPipe?.pipe(options.progressSink, { end: false });
|
|
4693
|
+
stderrPipe?.pipe(options.progressSink, { end: false });
|
|
4694
|
+
}
|
|
4402
4695
|
child.on("error", reject);
|
|
4403
4696
|
child.on("exit", (code) => resolve(code ?? 0));
|
|
4404
4697
|
});
|
|
@@ -4475,15 +4768,15 @@ async function cleanupDockerObjects(opts) {
|
|
|
4475
4768
|
return { exitCode: rmExit, removedIds: ids };
|
|
4476
4769
|
}
|
|
4477
4770
|
function composeProjectName(root) {
|
|
4478
|
-
return `${
|
|
4771
|
+
return `${path13.basename(root)}_devcontainer`;
|
|
4479
4772
|
}
|
|
4480
4773
|
function resolveCompose(root) {
|
|
4481
|
-
if (!existsSync6(
|
|
4774
|
+
if (!existsSync6(path13.join(root, ".devcontainer"))) {
|
|
4482
4775
|
throw new Error(
|
|
4483
4776
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
4484
4777
|
);
|
|
4485
4778
|
}
|
|
4486
|
-
const composeFile =
|
|
4779
|
+
const composeFile = path13.join(root, ".devcontainer", "compose.yaml");
|
|
4487
4780
|
if (!existsSync6(composeFile)) {
|
|
4488
4781
|
throw new Error(
|
|
4489
4782
|
`No compose.yaml at ${composeFile}. \`start\` / \`stop\` / \`status\` / \`logs\` require services configured via \`monoceros add-service <name> <svc>\`. Use \`monoceros shell <name>\` to enter the container directly.`
|
|
@@ -4504,9 +4797,17 @@ async function runStart(opts) {
|
|
|
4504
4797
|
logger.info(`Bringing devcontainer up at ${opts.root}\u2026`);
|
|
4505
4798
|
return spawnFn(
|
|
4506
4799
|
["up", "--workspace-folder", opts.root, "--mount-workspace-git-root=false"],
|
|
4507
|
-
opts.root
|
|
4800
|
+
opts.root,
|
|
4801
|
+
buildSpawnOptions(opts)
|
|
4508
4802
|
);
|
|
4509
4803
|
}
|
|
4804
|
+
function buildSpawnOptions(opts) {
|
|
4805
|
+
const out = {};
|
|
4806
|
+
if (opts.logSink) out.logSink = opts.logSink;
|
|
4807
|
+
if (opts.progressSink) out.progressSink = opts.progressSink;
|
|
4808
|
+
if (opts.silent) out.silent = true;
|
|
4809
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
4810
|
+
}
|
|
4510
4811
|
async function runContainerCycle(root, opts) {
|
|
4511
4812
|
const { hasCompose, logger } = opts;
|
|
4512
4813
|
if (hasCompose) {
|
|
@@ -4542,6 +4843,9 @@ and retry \`monoceros apply\`.`
|
|
|
4542
4843
|
return runStart({
|
|
4543
4844
|
root,
|
|
4544
4845
|
...opts.devcontainerSpawn ? { spawn: opts.devcontainerSpawn } : {},
|
|
4846
|
+
...opts.logSink ? { logSink: opts.logSink } : {},
|
|
4847
|
+
...opts.progressSink ? { progressSink: opts.progressSink } : {},
|
|
4848
|
+
...opts.silent ? { silent: true } : {},
|
|
4545
4849
|
logger
|
|
4546
4850
|
});
|
|
4547
4851
|
}
|
|
@@ -4555,7 +4859,8 @@ and retry \`monoceros apply\`.`
|
|
|
4555
4859
|
"--mount-workspace-git-root=false",
|
|
4556
4860
|
"--remove-existing-container"
|
|
4557
4861
|
],
|
|
4558
|
-
root
|
|
4862
|
+
root,
|
|
4863
|
+
buildSpawnOptions(opts)
|
|
4559
4864
|
);
|
|
4560
4865
|
}
|
|
4561
4866
|
function runStop(opts) {
|
|
@@ -4647,7 +4952,7 @@ function formatRootlessNotSupportedError() {
|
|
|
4647
4952
|
// src/devcontainer/identity.ts
|
|
4648
4953
|
import { spawn as spawn7 } from "child_process";
|
|
4649
4954
|
import { promises as fs10 } from "fs";
|
|
4650
|
-
import
|
|
4955
|
+
import path14 from "path";
|
|
4651
4956
|
import { consola as consola10 } from "consola";
|
|
4652
4957
|
var realGitConfigGet = (key) => {
|
|
4653
4958
|
return new Promise((resolve, reject) => {
|
|
@@ -4761,8 +5066,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
4761
5066
|
};
|
|
4762
5067
|
}
|
|
4763
5068
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
4764
|
-
const gitconfigDir =
|
|
4765
|
-
const gitconfigPath =
|
|
5069
|
+
const gitconfigDir = path14.join(devContainerRoot, ".monoceros");
|
|
5070
|
+
const gitconfigPath = path14.join(gitconfigDir, "gitconfig");
|
|
4766
5071
|
const logger = options.logger ?? { info: () => {
|
|
4767
5072
|
}, warn: () => {
|
|
4768
5073
|
} };
|
|
@@ -4863,26 +5168,26 @@ ${sectionLine(label)}
|
|
|
4863
5168
|
const parsed = await readConfig(ymlPath);
|
|
4864
5169
|
const globalConfig = await readMonocerosConfig({ monocerosHome: home });
|
|
4865
5170
|
warnOnDeprecatedFeatureRefs(parsed.config.features, globalConfig, logger);
|
|
5171
|
+
const envPath = containerEnvPath(opts.name, home);
|
|
5172
|
+
await ensureEnvGitignored(containerConfigsDir(home));
|
|
5173
|
+
const envVars = readEnvFile(envPath);
|
|
5174
|
+
const resolvedFeatures = interpolateFeatureOptions(
|
|
5175
|
+
parsed.config.features,
|
|
5176
|
+
envVars
|
|
5177
|
+
);
|
|
4866
5178
|
const createOpts = normalizeOptions(
|
|
4867
5179
|
solutionConfigToCreateOptions(
|
|
4868
|
-
parsed.config,
|
|
5180
|
+
{ ...parsed.config, features: resolvedFeatures },
|
|
4869
5181
|
globalConfig?.defaults?.features ?? {}
|
|
4870
5182
|
)
|
|
4871
5183
|
);
|
|
4872
|
-
const envPath = containerEnvPath(opts.name, home);
|
|
4873
|
-
await ensureEnvGitignored(containerConfigsDir(home));
|
|
4874
|
-
const envVars = readEnvFile(envPath);
|
|
4875
5184
|
const interpServices = interpolateServices(createOpts.services, envVars);
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
const missingVars = [...interpServices.missing, ...interpFeatures.missing];
|
|
4881
|
-
if (missingVars.length > 0) {
|
|
4882
|
-
throw new Error(formatMissingVarsError(missingVars, prettyPath(envPath)));
|
|
5185
|
+
if (interpServices.missing.length > 0) {
|
|
5186
|
+
throw new Error(
|
|
5187
|
+
formatMissingVarsError(interpServices.missing, prettyPath(envPath))
|
|
5188
|
+
);
|
|
4883
5189
|
}
|
|
4884
5190
|
createOpts.services = interpServices.services;
|
|
4885
|
-
if (createOpts.features) createOpts.features = interpFeatures.features;
|
|
4886
5191
|
const gitUserErrors = [];
|
|
4887
5192
|
let containerGitOverride;
|
|
4888
5193
|
if (parsed.config.git?.user) {
|
|
@@ -4977,48 +5282,111 @@ Fix the value in the env file (or the yml).`
|
|
|
4977
5282
|
);
|
|
4978
5283
|
logger.success(`materialized into ${prettyPath(targetDir)}`);
|
|
4979
5284
|
section("Container");
|
|
4980
|
-
const
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
);
|
|
4989
|
-
const
|
|
4990
|
-
const
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
5285
|
+
const applyLog = createApplyLog({
|
|
5286
|
+
name: opts.name,
|
|
5287
|
+
home,
|
|
5288
|
+
cliVersion: opts.cliVersion,
|
|
5289
|
+
configPath: ymlPath,
|
|
5290
|
+
...opts.now ? { now: opts.now } : {}
|
|
5291
|
+
});
|
|
5292
|
+
const progressOut = opts.progressOut ?? process.stderr;
|
|
5293
|
+
const interactive = (progressOut.isTTY ?? false) && !opts.verbose;
|
|
5294
|
+
const progress = interactive ? createApplyProgress({ out: progressOut, interactive: true }) : null;
|
|
5295
|
+
const containerLogger = progress ? progressTeeLogger(progress, applyLog.sink) : teeApplyLogger(logger, applyLog.sink);
|
|
5296
|
+
const internalLogger = progress ? logFileOnlyLogger(applyLog.sink) : containerLogger;
|
|
5297
|
+
const onSigint = createSigintAbort({
|
|
5298
|
+
progress,
|
|
5299
|
+
out: progressOut,
|
|
5300
|
+
log: applyLog,
|
|
5301
|
+
formatLogPointer: (p) => dim(`log: ${prettyPath(p)}`),
|
|
5302
|
+
onExit: () => process.exit(130)
|
|
5303
|
+
});
|
|
5304
|
+
process.on("SIGINT", onSigint);
|
|
5305
|
+
let exitCode;
|
|
4996
5306
|
try {
|
|
5307
|
+
const pullWarning = 'Pulling runtime image and building feature layers. First apply takes ~1\u20132 min (Docker downloads the multi-arch base); subsequent applies are cached and fast. devcontainer-cli may log a "No manifest found" line \u2014 harmless, the pull continues.';
|
|
5308
|
+
if (progress) {
|
|
5309
|
+
applyLog.stream.write(`# note: ${pullWarning}
|
|
5310
|
+
|
|
5311
|
+
`);
|
|
5312
|
+
} else {
|
|
5313
|
+
containerLogger.info(dim(pullWarning));
|
|
5314
|
+
}
|
|
5315
|
+
const ports = createOpts.ports ?? [];
|
|
5316
|
+
const hasPorts = ports.length > 0;
|
|
4997
5317
|
if (hasPorts) {
|
|
4998
|
-
await
|
|
4999
|
-
|
|
5000
|
-
...opts.proxyDocker ? { docker: opts.proxyDocker } : {},
|
|
5001
|
-
monocerosHome: home,
|
|
5002
|
-
hostPort: proxyHostPort(globalConfig),
|
|
5003
|
-
logger
|
|
5318
|
+
await preflightHostPort(proxyHostPort(globalConfig), {
|
|
5319
|
+
...opts.proxyDocker ? { docker: opts.proxyDocker } : {}
|
|
5004
5320
|
});
|
|
5005
|
-
} else {
|
|
5006
|
-
await removeDynamicConfig(opts.name, { monocerosHome: home });
|
|
5007
5321
|
}
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5322
|
+
try {
|
|
5323
|
+
if (hasPorts) {
|
|
5324
|
+
await writeDynamicConfig(opts.name, ports, { monocerosHome: home });
|
|
5325
|
+
await ensureProxy({
|
|
5326
|
+
...opts.proxyDocker ? { docker: opts.proxyDocker } : {},
|
|
5327
|
+
monocerosHome: home,
|
|
5328
|
+
hostPort: proxyHostPort(globalConfig),
|
|
5329
|
+
logger: containerLogger
|
|
5330
|
+
});
|
|
5331
|
+
} else {
|
|
5332
|
+
await removeDynamicConfig(opts.name, { monocerosHome: home });
|
|
5333
|
+
}
|
|
5334
|
+
} catch (err) {
|
|
5335
|
+
containerLogger.warn?.(
|
|
5336
|
+
`Could not sync Traefik routes: ${err instanceof Error ? err.message : String(err)}. The container will start, but \`<name>.localhost\` routing may not work until the next \`monoceros apply\`.`
|
|
5337
|
+
);
|
|
5338
|
+
}
|
|
5339
|
+
if (progress) {
|
|
5340
|
+
progress.setPhase(
|
|
5341
|
+
needsCompose(createOpts) ? "cleaning up previous containers\u2026" : "starting container\u2026"
|
|
5342
|
+
);
|
|
5343
|
+
}
|
|
5344
|
+
exitCode = await runContainerCycle(targetDir, {
|
|
5345
|
+
hasCompose: needsCompose(createOpts),
|
|
5346
|
+
...opts.dockerExec !== void 0 ? { dockerExec: opts.dockerExec } : {},
|
|
5347
|
+
...opts.devcontainerSpawn !== void 0 ? { devcontainerSpawn: opts.devcontainerSpawn } : {},
|
|
5348
|
+
logSink: applyLog.sink,
|
|
5349
|
+
...progress ? { progressSink: progress.streamSink, silent: true } : {},
|
|
5350
|
+
logger: internalLogger
|
|
5351
|
+
});
|
|
5352
|
+
if (progress) {
|
|
5353
|
+
if (exitCode === 0) {
|
|
5354
|
+
progress.succeed();
|
|
5355
|
+
} else {
|
|
5356
|
+
const { tailLines } = progress.fail();
|
|
5357
|
+
progressOut.write(`
|
|
5358
|
+
\u2718 apply failed (exit ${exitCode})
|
|
5359
|
+
|
|
5360
|
+
`);
|
|
5361
|
+
for (const line of tailLines) {
|
|
5362
|
+
progressOut.write(` ${line}
|
|
5363
|
+
`);
|
|
5364
|
+
}
|
|
5365
|
+
if (tailLines.length > 0) progressOut.write("\n");
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
if (exitCode === 0) {
|
|
5369
|
+
const summaryLines = buildApplySummary(createOpts);
|
|
5370
|
+
if (summaryLines.length > 0) {
|
|
5371
|
+
const formatted = formatApplySummary(summaryLines);
|
|
5372
|
+
progressOut.write(`
|
|
5373
|
+
${formatted}
|
|
5374
|
+
`);
|
|
5375
|
+
applyLog.stream.write(`
|
|
5376
|
+
${stripAnsi(formatted)}
|
|
5377
|
+
`);
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
await applyLog.close();
|
|
5381
|
+
progressOut.write(`
|
|
5382
|
+
${dim(`log: ${prettyPath(applyLog.path)}`)}
|
|
5383
|
+
`);
|
|
5384
|
+
if (exitCode === 0) {
|
|
5385
|
+
section("Next steps");
|
|
5386
|
+
logger.info(` ${cyan2(`monoceros shell ${opts.name}`)}`);
|
|
5387
|
+
}
|
|
5388
|
+
} finally {
|
|
5389
|
+
process.off("SIGINT", onSigint);
|
|
5022
5390
|
}
|
|
5023
5391
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
5024
5392
|
}
|
|
@@ -5116,7 +5484,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
5116
5484
|
}
|
|
5117
5485
|
|
|
5118
5486
|
// src/version.ts
|
|
5119
|
-
var CLI_VERSION = true ? "1.
|
|
5487
|
+
var CLI_VERSION = true ? "1.14.1" : "dev";
|
|
5120
5488
|
|
|
5121
5489
|
// src/commands/_dispatch.ts
|
|
5122
5490
|
import { consola as consola12 } from "consola";
|
|
@@ -5142,13 +5510,19 @@ var applyCommand = defineCommand8({
|
|
|
5142
5510
|
type: "positional",
|
|
5143
5511
|
description: "Config name. Resolves to $MONOCEROS_HOME/container-configs/<name>.yml.",
|
|
5144
5512
|
required: true
|
|
5513
|
+
},
|
|
5514
|
+
verbose: {
|
|
5515
|
+
type: "boolean",
|
|
5516
|
+
description: "Stream the raw @devcontainers/cli output to stderr instead of showing a phase spinner. Auto-enabled when stderr is not a TTY.",
|
|
5517
|
+
default: false
|
|
5145
5518
|
}
|
|
5146
5519
|
},
|
|
5147
5520
|
run({ args }) {
|
|
5148
5521
|
return dispatch(async () => {
|
|
5149
5522
|
const result = await runApply({
|
|
5150
5523
|
name: args.name,
|
|
5151
|
-
cliVersion: CLI_VERSION
|
|
5524
|
+
cliVersion: CLI_VERSION,
|
|
5525
|
+
verbose: args.verbose
|
|
5152
5526
|
});
|
|
5153
5527
|
return result.containerExitCode;
|
|
5154
5528
|
});
|
|
@@ -5281,7 +5655,7 @@ import { defineCommand as defineCommand10 } from "citty";
|
|
|
5281
5655
|
|
|
5282
5656
|
// src/completion/resolve.ts
|
|
5283
5657
|
import { existsSync as existsSync8, promises as fs12 } from "fs";
|
|
5284
|
-
import
|
|
5658
|
+
import path15 from "path";
|
|
5285
5659
|
async function resolveCompletions(line, point, opts = {}) {
|
|
5286
5660
|
const { prev, current } = parseCompletionLine(line, point);
|
|
5287
5661
|
const ctx = { prev, current, opts };
|
|
@@ -5429,7 +5803,7 @@ function filterPrefix(values, fragment) {
|
|
|
5429
5803
|
}
|
|
5430
5804
|
async function listContainerNames(ctx) {
|
|
5431
5805
|
const home = ctx.opts.monocerosHome ?? monocerosHome();
|
|
5432
|
-
const dir =
|
|
5806
|
+
const dir = path15.join(home, "container-configs");
|
|
5433
5807
|
if (!existsSync8(dir)) return [];
|
|
5434
5808
|
const entries = await fs12.readdir(dir);
|
|
5435
5809
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length)).sort();
|
|
@@ -5689,7 +6063,7 @@ import { consola as consola14 } from "consola";
|
|
|
5689
6063
|
|
|
5690
6064
|
// src/init/index.ts
|
|
5691
6065
|
import { existsSync as existsSync9, promises as fs13 } from "fs";
|
|
5692
|
-
import
|
|
6066
|
+
import path16 from "path";
|
|
5693
6067
|
import { consola as consola13 } from "consola";
|
|
5694
6068
|
|
|
5695
6069
|
// src/init/generator.ts
|
|
@@ -5992,17 +6366,12 @@ function renderFeatureBlock(out, feature, summary, commented) {
|
|
|
5992
6366
|
}
|
|
5993
6367
|
return;
|
|
5994
6368
|
}
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
out.push(` ${key}: ${renderScalarValue(value)}`);
|
|
5999
|
-
}
|
|
6369
|
+
out.push(` options:`);
|
|
6370
|
+
for (const [key, value] of activeKeys) {
|
|
6371
|
+
out.push(` ${key}: ${renderScalarValue(value)}`);
|
|
6000
6372
|
}
|
|
6001
|
-
|
|
6002
|
-
out.push(`
|
|
6003
|
-
for (const hint of hints) {
|
|
6004
|
-
out.push(` # ${hint.key}: ${hint.placeholder}`);
|
|
6005
|
-
}
|
|
6373
|
+
for (const hint of hints) {
|
|
6374
|
+
out.push(` ${hint.key}: ${hint.placeholder}`);
|
|
6006
6375
|
}
|
|
6007
6376
|
}
|
|
6008
6377
|
function pushHeader(out, header, name) {
|
|
@@ -6151,8 +6520,8 @@ async function runInit(opts) {
|
|
|
6151
6520
|
}
|
|
6152
6521
|
await ensureEnvVars(envPath, opts.name, seedVars);
|
|
6153
6522
|
const documented = !anyComposed;
|
|
6154
|
-
const ymlRel =
|
|
6155
|
-
const envRel =
|
|
6523
|
+
const ymlRel = path16.relative(home, dest);
|
|
6524
|
+
const envRel = path16.relative(home, envPath);
|
|
6156
6525
|
if (documented) {
|
|
6157
6526
|
logger.success(`Wrote documented default to ${ymlRel} and ${envRel}.`);
|
|
6158
6527
|
logger.info(
|
|
@@ -6688,7 +7057,7 @@ import { createInterface } from "readline/promises";
|
|
|
6688
7057
|
|
|
6689
7058
|
// src/remove/index.ts
|
|
6690
7059
|
import { existsSync as existsSync10, promises as fs14 } from "fs";
|
|
6691
|
-
import
|
|
7060
|
+
import path17 from "path";
|
|
6692
7061
|
import { consola as consola19 } from "consola";
|
|
6693
7062
|
async function runRemove(opts) {
|
|
6694
7063
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -6731,16 +7100,16 @@ async function runRemove(opts) {
|
|
|
6731
7100
|
let backupPath = null;
|
|
6732
7101
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
6733
7102
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6734
|
-
backupPath =
|
|
7103
|
+
backupPath = path17.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
6735
7104
|
await fs14.mkdir(backupPath, { recursive: true });
|
|
6736
7105
|
if (hasYml) {
|
|
6737
|
-
await fs14.copyFile(ymlPath,
|
|
7106
|
+
await fs14.copyFile(ymlPath, path17.join(backupPath, `${opts.name}.yml`));
|
|
6738
7107
|
}
|
|
6739
7108
|
if (hasEnv) {
|
|
6740
|
-
await fs14.copyFile(envPath,
|
|
7109
|
+
await fs14.copyFile(envPath, path17.join(backupPath, `${opts.name}.env`));
|
|
6741
7110
|
}
|
|
6742
7111
|
if (hasContainer) {
|
|
6743
|
-
await fs14.cp(containerPath,
|
|
7112
|
+
await fs14.cp(containerPath, path17.join(backupPath, "container"), {
|
|
6744
7113
|
recursive: true
|
|
6745
7114
|
});
|
|
6746
7115
|
}
|
|
@@ -6884,7 +7253,7 @@ import { consola as consola22 } from "consola";
|
|
|
6884
7253
|
|
|
6885
7254
|
// src/restore/index.ts
|
|
6886
7255
|
import { existsSync as existsSync11, promises as fs15 } from "fs";
|
|
6887
|
-
import
|
|
7256
|
+
import path18 from "path";
|
|
6888
7257
|
import { consola as consola21 } from "consola";
|
|
6889
7258
|
async function runRestore(opts) {
|
|
6890
7259
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -6892,7 +7261,7 @@ async function runRestore(opts) {
|
|
|
6892
7261
|
info: (msg) => consola21.info(msg),
|
|
6893
7262
|
success: (msg) => consola21.success(msg)
|
|
6894
7263
|
};
|
|
6895
|
-
const backup =
|
|
7264
|
+
const backup = path18.resolve(opts.backupPath);
|
|
6896
7265
|
if (!existsSync11(backup)) {
|
|
6897
7266
|
throw new Error(`Backup not found: ${backup}.`);
|
|
6898
7267
|
}
|
|
@@ -6914,9 +7283,9 @@ async function runRestore(opts) {
|
|
|
6914
7283
|
}
|
|
6915
7284
|
const ymlFile = ymlFiles[0];
|
|
6916
7285
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
6917
|
-
const containerInBackup =
|
|
7286
|
+
const containerInBackup = path18.join(backup, "container");
|
|
6918
7287
|
const hasContainer = existsSync11(containerInBackup);
|
|
6919
|
-
const envInBackup =
|
|
7288
|
+
const envInBackup = path18.join(backup, `${name}.env`);
|
|
6920
7289
|
const hasEnv = existsSync11(envInBackup);
|
|
6921
7290
|
const destYml = containerConfigPath(name, home);
|
|
6922
7291
|
const destContainer = containerDir(name, home);
|
|
@@ -6931,7 +7300,7 @@ async function runRestore(opts) {
|
|
|
6931
7300
|
);
|
|
6932
7301
|
}
|
|
6933
7302
|
await fs15.mkdir(containerConfigsDir(home), { recursive: true });
|
|
6934
|
-
await fs15.copyFile(
|
|
7303
|
+
await fs15.copyFile(path18.join(backup, ymlFile), destYml);
|
|
6935
7304
|
if (hasEnv) {
|
|
6936
7305
|
await fs15.copyFile(envInBackup, containerEnvPath(name, home));
|
|
6937
7306
|
}
|
|
@@ -7195,7 +7564,7 @@ import { consola as consola28 } from "consola";
|
|
|
7195
7564
|
|
|
7196
7565
|
// src/devcontainer/shell.ts
|
|
7197
7566
|
import { existsSync as existsSync12 } from "fs";
|
|
7198
|
-
import
|
|
7567
|
+
import path19 from "path";
|
|
7199
7568
|
async function runShell(opts) {
|
|
7200
7569
|
assertContainerExists(opts.root);
|
|
7201
7570
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
@@ -7218,7 +7587,7 @@ async function runShell(opts) {
|
|
|
7218
7587
|
);
|
|
7219
7588
|
}
|
|
7220
7589
|
function assertContainerExists(root) {
|
|
7221
|
-
if (!existsSync12(
|
|
7590
|
+
if (!existsSync12(path19.join(root, ".devcontainer"))) {
|
|
7222
7591
|
throw new Error(
|
|
7223
7592
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
7224
7593
|
);
|
|
@@ -7435,7 +7804,7 @@ import { consola as consola32 } from "consola";
|
|
|
7435
7804
|
|
|
7436
7805
|
// src/tunnel/resolve.ts
|
|
7437
7806
|
import { existsSync as existsSync13 } from "fs";
|
|
7438
|
-
import
|
|
7807
|
+
import path20 from "path";
|
|
7439
7808
|
async function resolveTunnelTarget(opts) {
|
|
7440
7809
|
const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
|
|
7441
7810
|
if (!existsSync13(ymlPath)) {
|
|
@@ -7451,7 +7820,7 @@ async function resolveTunnelTarget(opts) {
|
|
|
7451
7820
|
`Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
|
|
7452
7821
|
);
|
|
7453
7822
|
}
|
|
7454
|
-
const composePath =
|
|
7823
|
+
const composePath = path20.join(containerRoot, ".devcontainer", "compose.yaml");
|
|
7455
7824
|
const isCompose = existsSync13(composePath);
|
|
7456
7825
|
const parsedTarget = parseTargetArg(opts.target, config);
|
|
7457
7826
|
const docker = opts.docker ?? defaultDockerExec;
|