@getmonoceros/workbench 1.13.2 → 1.13.3
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 +96 -333
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -129,19 +129,19 @@ function wrapText(text, width, continuationIndent) {
|
|
|
129
129
|
if (current.length > 0) lines.push(current.replace(/\s+$/, ""));
|
|
130
130
|
return lines.map((l, i) => i === 0 ? l : continuationIndent + l).join("\n");
|
|
131
131
|
}
|
|
132
|
-
function alignTable(rows, indent) {
|
|
132
|
+
function alignTable(rows, indent, opts = {}) {
|
|
133
133
|
if (rows.length === 0) return "";
|
|
134
|
-
const labelWidth = Math.max(...rows.map((r) => visibleLen(r[0])));
|
|
134
|
+
const labelWidth = opts.fixedLabelWidth ?? Math.max(...rows.map((r) => visibleLen(r[0])));
|
|
135
135
|
const gutter = " ";
|
|
136
136
|
const descWidth = terminalWidth() - indent.length - labelWidth - gutter.length;
|
|
137
137
|
const continuationIndent = " ".repeat(
|
|
138
138
|
indent.length + labelWidth + gutter.length
|
|
139
139
|
);
|
|
140
140
|
return rows.map(([left, right]) => {
|
|
141
|
-
const pad = " ".repeat(labelWidth - visibleLen(left));
|
|
141
|
+
const pad = " ".repeat(Math.max(0, labelWidth - visibleLen(left)));
|
|
142
142
|
const wrapped = wrapText(right, descWidth, continuationIndent);
|
|
143
143
|
return `${indent}${left}${pad}${gutter}${wrapped}`;
|
|
144
|
-
}).join("\n");
|
|
144
|
+
}).join(opts.rowGap ? "\n\n" : "\n");
|
|
145
145
|
}
|
|
146
146
|
function collectSubCommands(cmd) {
|
|
147
147
|
const subs = cmd.subCommands ?? {};
|
|
@@ -167,6 +167,7 @@ function renderCommandsBlock(entries) {
|
|
|
167
167
|
arr.push(entry2);
|
|
168
168
|
byGroup.set(entry2.group, arr);
|
|
169
169
|
}
|
|
170
|
+
const labelWidth = Math.max(...entries.map((e) => visibleLen(cyan(e.name))));
|
|
170
171
|
const renderSection = (label, items) => {
|
|
171
172
|
if (items.length === 0) return;
|
|
172
173
|
lines.push("");
|
|
@@ -176,7 +177,9 @@ function renderCommandsBlock(entries) {
|
|
|
176
177
|
cyan(e.name),
|
|
177
178
|
e.description
|
|
178
179
|
]);
|
|
179
|
-
lines.push(
|
|
180
|
+
lines.push(
|
|
181
|
+
alignTable(rows, "", { fixedLabelWidth: labelWidth, rowGap: true })
|
|
182
|
+
);
|
|
180
183
|
};
|
|
181
184
|
for (const { key, label } of GROUPS) {
|
|
182
185
|
renderSection(label, byGroup.get(key) ?? []);
|
|
@@ -251,25 +254,25 @@ function detectHelpRequest(argv, main2) {
|
|
|
251
254
|
const separatorIdx = argv.indexOf("--");
|
|
252
255
|
if (helpIdx === -1) return null;
|
|
253
256
|
if (separatorIdx !== -1 && separatorIdx < helpIdx) return null;
|
|
254
|
-
const
|
|
257
|
+
const path20 = [];
|
|
255
258
|
const tokens = argv.slice(
|
|
256
259
|
0,
|
|
257
260
|
separatorIdx === -1 ? argv.length : separatorIdx
|
|
258
261
|
);
|
|
259
262
|
let cursor = main2;
|
|
260
263
|
const mainName = (main2.meta ?? {}).name ?? "monoceros";
|
|
261
|
-
|
|
264
|
+
path20.push(mainName);
|
|
262
265
|
for (const tok of tokens) {
|
|
263
266
|
if (tok.startsWith("-")) continue;
|
|
264
267
|
const subs = cursor.subCommands ?? {};
|
|
265
268
|
if (tok in subs) {
|
|
266
269
|
cursor = subs[tok];
|
|
267
|
-
|
|
270
|
+
path20.push(tok);
|
|
268
271
|
continue;
|
|
269
272
|
}
|
|
270
273
|
break;
|
|
271
274
|
}
|
|
272
|
-
return { path:
|
|
275
|
+
return { path: path20, cmd: cursor };
|
|
273
276
|
}
|
|
274
277
|
async function maybeRenderHelp(argv, main2) {
|
|
275
278
|
const hit = detectHelpRequest(argv, main2);
|
|
@@ -3167,8 +3170,8 @@ function removeRepoFromDoc(doc, urlOrPath) {
|
|
|
3167
3170
|
if (!isMap2(item)) return false;
|
|
3168
3171
|
const url = item.get("url");
|
|
3169
3172
|
if (url === urlOrPath) return true;
|
|
3170
|
-
const
|
|
3171
|
-
const effectivePath = typeof
|
|
3173
|
+
const path20 = item.get("path");
|
|
3174
|
+
const effectivePath = typeof path20 === "string" ? path20 : typeof url === "string" ? deriveRepoName(url) : void 0;
|
|
3172
3175
|
return effectivePath === urlOrPath;
|
|
3173
3176
|
});
|
|
3174
3177
|
if (idx < 0) return false;
|
|
@@ -3261,7 +3264,7 @@ async function runAddRepo(input) {
|
|
|
3261
3264
|
"Missing repo URL. Usage: monoceros add-repo <containername> <url>."
|
|
3262
3265
|
);
|
|
3263
3266
|
}
|
|
3264
|
-
const
|
|
3267
|
+
const path20 = (input.path ?? deriveRepoName(url)).trim();
|
|
3265
3268
|
const hasName = typeof input.gitName === "string" && input.gitName.trim().length > 0;
|
|
3266
3269
|
const hasEmail = typeof input.gitEmail === "string" && input.gitEmail.trim().length > 0;
|
|
3267
3270
|
if (hasName !== hasEmail) {
|
|
@@ -3298,7 +3301,7 @@ async function runAddRepo(input) {
|
|
|
3298
3301
|
const providerToWrite = !canonical && explicitProvider ? explicitProvider : void 0;
|
|
3299
3302
|
const entry2 = {
|
|
3300
3303
|
url,
|
|
3301
|
-
path:
|
|
3304
|
+
path: path20,
|
|
3302
3305
|
...hasName && hasEmail ? {
|
|
3303
3306
|
gitUser: {
|
|
3304
3307
|
name: input.gitName.trim(),
|
|
@@ -4137,7 +4140,7 @@ var addServiceCommand = defineCommand7({
|
|
|
4137
4140
|
import { defineCommand as defineCommand8 } from "citty";
|
|
4138
4141
|
|
|
4139
4142
|
// src/apply/index.ts
|
|
4140
|
-
import { existsSync as
|
|
4143
|
+
import { existsSync as existsSync7, promises as fs11 } from "fs";
|
|
4141
4144
|
import { consola as consola11 } from "consola";
|
|
4142
4145
|
|
|
4143
4146
|
// src/config/state.ts
|
|
@@ -4579,230 +4582,11 @@ function runLogs(opts) {
|
|
|
4579
4582
|
);
|
|
4580
4583
|
}
|
|
4581
4584
|
|
|
4582
|
-
// src/devcontainer/repo-reachability.ts
|
|
4583
|
-
import { spawn as spawn6 } from "child_process";
|
|
4584
|
-
var realGitLsRemote = (url) => {
|
|
4585
|
-
return new Promise((resolve, reject) => {
|
|
4586
|
-
const child = spawn6("git", ["ls-remote", "--heads", "--", url], {
|
|
4587
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
4588
|
-
env: {
|
|
4589
|
-
...process.env,
|
|
4590
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
4591
|
-
}
|
|
4592
|
-
});
|
|
4593
|
-
let stdout = "";
|
|
4594
|
-
let stderr = "";
|
|
4595
|
-
child.stdout.on("data", (chunk) => {
|
|
4596
|
-
stdout += chunk.toString();
|
|
4597
|
-
});
|
|
4598
|
-
child.stderr.on("data", (chunk) => {
|
|
4599
|
-
stderr += chunk.toString();
|
|
4600
|
-
});
|
|
4601
|
-
child.on("error", reject);
|
|
4602
|
-
child.on(
|
|
4603
|
-
"exit",
|
|
4604
|
-
(code) => resolve({ stdout, stderr, exitCode: code ?? 0 })
|
|
4605
|
-
);
|
|
4606
|
-
});
|
|
4607
|
-
};
|
|
4608
|
-
function classifyStderr(stderr) {
|
|
4609
|
-
const s = stderr.toLowerCase();
|
|
4610
|
-
if (s.includes("could not resolve host") || s.includes("name or service not known") || s.includes("temporary failure in name resolution") || s.includes("no address associated with hostname")) {
|
|
4611
|
-
return "dns";
|
|
4612
|
-
}
|
|
4613
|
-
if (s.includes("repository not found") || s.includes("may not have access") || s.includes("no longer exists") || s.includes("don't have permission") || s.includes("could not be found") || s.includes("the requested url returned error: 404")) {
|
|
4614
|
-
return "not-found-or-no-access";
|
|
4615
|
-
}
|
|
4616
|
-
if (s.includes("authentication failed") || s.includes("could not read username") || s.includes("incorrect username or password") || s.includes("the requested url returned error: 401") || s.includes("the requested url returned error: 403")) {
|
|
4617
|
-
return "auth-failed";
|
|
4618
|
-
}
|
|
4619
|
-
return "unknown";
|
|
4620
|
-
}
|
|
4621
|
-
async function checkRepoReachability(repos, options = {}) {
|
|
4622
|
-
const spawnFn = options.spawn ?? realGitLsRemote;
|
|
4623
|
-
const results = [];
|
|
4624
|
-
for (const repo of repos) {
|
|
4625
|
-
let result;
|
|
4626
|
-
try {
|
|
4627
|
-
result = await spawnFn(repo.url);
|
|
4628
|
-
} catch (err) {
|
|
4629
|
-
results.push({
|
|
4630
|
-
url: repo.url,
|
|
4631
|
-
ok: false,
|
|
4632
|
-
kind: "unknown",
|
|
4633
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
4634
|
-
});
|
|
4635
|
-
continue;
|
|
4636
|
-
}
|
|
4637
|
-
if (result.exitCode === 0) {
|
|
4638
|
-
results.push({ url: repo.url, ok: true, detail: "" });
|
|
4639
|
-
continue;
|
|
4640
|
-
}
|
|
4641
|
-
results.push({
|
|
4642
|
-
url: repo.url,
|
|
4643
|
-
ok: false,
|
|
4644
|
-
kind: classifyStderr(result.stderr),
|
|
4645
|
-
detail: result.stderr.trim()
|
|
4646
|
-
});
|
|
4647
|
-
}
|
|
4648
|
-
return results;
|
|
4649
|
-
}
|
|
4650
|
-
function formatUnreachableReposError(failures) {
|
|
4651
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
4652
|
-
for (const f of failures) {
|
|
4653
|
-
const kind = f.kind ?? "unknown";
|
|
4654
|
-
const list = byKind.get(kind) ?? [];
|
|
4655
|
-
list.push(f);
|
|
4656
|
-
byKind.set(kind, list);
|
|
4657
|
-
}
|
|
4658
|
-
const totalUrls = failures.length;
|
|
4659
|
-
const lines = [
|
|
4660
|
-
totalUrls === 1 ? `Cannot reach declared repo: ${failures[0].url}` : `Cannot reach ${totalUrls} declared repos:`,
|
|
4661
|
-
""
|
|
4662
|
-
];
|
|
4663
|
-
const sectionOrder = [
|
|
4664
|
-
"not-found-or-no-access",
|
|
4665
|
-
"auth-failed",
|
|
4666
|
-
"dns",
|
|
4667
|
-
"unknown"
|
|
4668
|
-
];
|
|
4669
|
-
for (const kind of sectionOrder) {
|
|
4670
|
-
const entries = byKind.get(kind);
|
|
4671
|
-
if (!entries || entries.length === 0) continue;
|
|
4672
|
-
lines.push(headerForKind(kind));
|
|
4673
|
-
for (const e of entries) {
|
|
4674
|
-
lines.push(` \u2022 ${e.url}`);
|
|
4675
|
-
if (e.detail) {
|
|
4676
|
-
for (const detailLine of e.detail.split("\n")) {
|
|
4677
|
-
const trimmed = detailLine.trim();
|
|
4678
|
-
if (trimmed) lines.push(` git: ${trimmed}`);
|
|
4679
|
-
}
|
|
4680
|
-
}
|
|
4681
|
-
}
|
|
4682
|
-
for (const advice of adviceForKind(kind)) {
|
|
4683
|
-
lines.push(` - ${advice}`);
|
|
4684
|
-
}
|
|
4685
|
-
lines.push("");
|
|
4686
|
-
}
|
|
4687
|
-
lines.push(`Then re-run ${cyan2("monoceros apply")}.`);
|
|
4688
|
-
return lines.join("\n");
|
|
4689
|
-
}
|
|
4690
|
-
function headerForKind(kind) {
|
|
4691
|
-
switch (kind) {
|
|
4692
|
-
case "not-found-or-no-access":
|
|
4693
|
-
return "Repository not found (or your credentials don't grant access):";
|
|
4694
|
-
case "auth-failed":
|
|
4695
|
-
return "Authentication failed (credentials are present but rejected):";
|
|
4696
|
-
case "dns":
|
|
4697
|
-
return "Host unreachable (DNS / VPN / offline \u2014 git couldn't resolve the hostname):";
|
|
4698
|
-
case "unknown":
|
|
4699
|
-
return "Unrecognised git error:";
|
|
4700
|
-
}
|
|
4701
|
-
}
|
|
4702
|
-
function adviceForKind(kind) {
|
|
4703
|
-
switch (kind) {
|
|
4704
|
-
case "not-found-or-no-access":
|
|
4705
|
-
return [
|
|
4706
|
-
"Re-check the URL for typos (case-sensitive on most hosts).",
|
|
4707
|
-
"Verify the repo still exists / is not archived in a way that hides it.",
|
|
4708
|
-
"Ensure your token covers this org / workspace and has read scope (GitHub: `repo`; GitLab: `read_repository`; Bitbucket: repo read)."
|
|
4709
|
-
];
|
|
4710
|
-
case "auth-failed":
|
|
4711
|
-
return [
|
|
4712
|
-
"Token may be expired or revoked \u2014 regenerate it from the provider UI.",
|
|
4713
|
-
"Re-run the provider CLI login (gh auth login / glab auth login) \u2014 Monoceros picks up the refreshed token on the next apply."
|
|
4714
|
-
];
|
|
4715
|
-
case "dns":
|
|
4716
|
-
return [
|
|
4717
|
-
"Check your internet / VPN \u2014 corporate Git hosts often require VPN.",
|
|
4718
|
-
"Verify the hostname spelling in the yml."
|
|
4719
|
-
];
|
|
4720
|
-
case "unknown":
|
|
4721
|
-
return [
|
|
4722
|
-
"Run `git ls-remote <url>` manually on the host to see the raw error."
|
|
4723
|
-
];
|
|
4724
|
-
}
|
|
4725
|
-
}
|
|
4726
|
-
|
|
4727
|
-
// src/devcontainer/repo-clone.ts
|
|
4728
|
-
import { spawn as spawn7 } from "child_process";
|
|
4729
|
-
import { existsSync as existsSync7, promises as fs10 } from "fs";
|
|
4730
|
-
import path13 from "path";
|
|
4731
|
-
var realGitClone = (url, dest) => {
|
|
4732
|
-
return new Promise((resolve, reject) => {
|
|
4733
|
-
const child = spawn7("git", ["clone", "--", url, dest], {
|
|
4734
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
4735
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
4736
|
-
});
|
|
4737
|
-
let stdout = "";
|
|
4738
|
-
let stderr = "";
|
|
4739
|
-
child.stdout.on("data", (c) => {
|
|
4740
|
-
stdout += c.toString();
|
|
4741
|
-
});
|
|
4742
|
-
child.stderr.on("data", (c) => {
|
|
4743
|
-
stderr += c.toString();
|
|
4744
|
-
});
|
|
4745
|
-
child.on("error", reject);
|
|
4746
|
-
child.on(
|
|
4747
|
-
"exit",
|
|
4748
|
-
(code) => resolve({ stdout, stderr, exitCode: code ?? 0 })
|
|
4749
|
-
);
|
|
4750
|
-
});
|
|
4751
|
-
};
|
|
4752
|
-
async function cloneReposHostSide(containerRoot, repos, options = {}) {
|
|
4753
|
-
const spawnFn = options.spawn ?? realGitClone;
|
|
4754
|
-
const results = [];
|
|
4755
|
-
for (const repo of repos) {
|
|
4756
|
-
const dest = path13.join(containerRoot, "projects", repo.path);
|
|
4757
|
-
if (existsSync7(dest)) {
|
|
4758
|
-
results.push({ path: repo.path, url: repo.url, status: "skipped" });
|
|
4759
|
-
continue;
|
|
4760
|
-
}
|
|
4761
|
-
await fs10.mkdir(path13.dirname(dest), { recursive: true });
|
|
4762
|
-
let r;
|
|
4763
|
-
try {
|
|
4764
|
-
r = await spawnFn(repo.url, dest);
|
|
4765
|
-
} catch (err) {
|
|
4766
|
-
results.push({
|
|
4767
|
-
path: repo.path,
|
|
4768
|
-
url: repo.url,
|
|
4769
|
-
status: "failed",
|
|
4770
|
-
detail: err instanceof Error ? err.message : String(err)
|
|
4771
|
-
});
|
|
4772
|
-
continue;
|
|
4773
|
-
}
|
|
4774
|
-
results.push(
|
|
4775
|
-
r.exitCode === 0 ? { path: repo.path, url: repo.url, status: "cloned" } : {
|
|
4776
|
-
path: repo.path,
|
|
4777
|
-
url: repo.url,
|
|
4778
|
-
status: "failed",
|
|
4779
|
-
detail: r.stderr.trim()
|
|
4780
|
-
}
|
|
4781
|
-
);
|
|
4782
|
-
}
|
|
4783
|
-
return results;
|
|
4784
|
-
}
|
|
4785
|
-
function formatCloneFailuresError(failures) {
|
|
4786
|
-
const lines = failures.length === 1 ? [`Failed to clone declared repo: ${failures[0].url}`, ""] : [`Failed to clone ${failures.length} declared repos:`, ""];
|
|
4787
|
-
for (const f of failures) {
|
|
4788
|
-
lines.push(` \u2022 ${f.url} \u2192 projects/${f.path}`);
|
|
4789
|
-
if (f.detail) lines.push(` ${f.detail}`);
|
|
4790
|
-
}
|
|
4791
|
-
lines.push("");
|
|
4792
|
-
lines.push(
|
|
4793
|
-
"Reachability was confirmed earlier, so this is usually a local issue"
|
|
4794
|
-
);
|
|
4795
|
-
lines.push(
|
|
4796
|
-
"(disk space, a leftover non-empty target dir). Fix it and re-run " + cyan2("monoceros apply") + "."
|
|
4797
|
-
);
|
|
4798
|
-
return lines.join("\n");
|
|
4799
|
-
}
|
|
4800
|
-
|
|
4801
4585
|
// src/devcontainer/docker-mode.ts
|
|
4802
|
-
import { spawn as
|
|
4586
|
+
import { spawn as spawn6 } from "child_process";
|
|
4803
4587
|
var realDockerInfo = () => {
|
|
4804
4588
|
return new Promise((resolve, reject) => {
|
|
4805
|
-
const child =
|
|
4589
|
+
const child = spawn6(
|
|
4806
4590
|
"docker",
|
|
4807
4591
|
["info", "--format", "{{json .SecurityOptions}}"],
|
|
4808
4592
|
{
|
|
@@ -4861,13 +4645,13 @@ function formatRootlessNotSupportedError() {
|
|
|
4861
4645
|
}
|
|
4862
4646
|
|
|
4863
4647
|
// src/devcontainer/identity.ts
|
|
4864
|
-
import { spawn as
|
|
4865
|
-
import { promises as
|
|
4866
|
-
import
|
|
4648
|
+
import { spawn as spawn7 } from "child_process";
|
|
4649
|
+
import { promises as fs10 } from "fs";
|
|
4650
|
+
import path13 from "path";
|
|
4867
4651
|
import { consola as consola10 } from "consola";
|
|
4868
4652
|
var realGitConfigGet = (key) => {
|
|
4869
4653
|
return new Promise((resolve, reject) => {
|
|
4870
|
-
const child =
|
|
4654
|
+
const child = spawn7("git", ["config", "--global", "--get", key], {
|
|
4871
4655
|
stdio: ["ignore", "pipe", "inherit"]
|
|
4872
4656
|
});
|
|
4873
4657
|
let stdout = "";
|
|
@@ -4977,8 +4761,8 @@ async function resolveIdentityWithPrompt(options = {}) {
|
|
|
4977
4761
|
};
|
|
4978
4762
|
}
|
|
4979
4763
|
async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
4980
|
-
const gitconfigDir =
|
|
4981
|
-
const gitconfigPath =
|
|
4764
|
+
const gitconfigDir = path13.join(devContainerRoot, ".monoceros");
|
|
4765
|
+
const gitconfigPath = path13.join(gitconfigDir, "gitconfig");
|
|
4982
4766
|
const logger = options.logger ?? { info: () => {
|
|
4983
4767
|
}, warn: () => {
|
|
4984
4768
|
} };
|
|
@@ -4991,8 +4775,8 @@ async function collectGitIdentity(devContainerRoot, options = {}) {
|
|
|
4991
4775
|
const lines = ["[user]"];
|
|
4992
4776
|
if (resolved.name !== void 0) lines.push(` name = ${resolved.name}`);
|
|
4993
4777
|
if (resolved.email !== void 0) lines.push(` email = ${resolved.email}`);
|
|
4994
|
-
await
|
|
4995
|
-
await
|
|
4778
|
+
await fs10.mkdir(gitconfigDir, { recursive: true });
|
|
4779
|
+
await fs10.writeFile(gitconfigPath, lines.join("\n") + "\n");
|
|
4996
4780
|
return {
|
|
4997
4781
|
...resolved.name !== void 0 ? { name: resolved.name } : {},
|
|
4998
4782
|
...resolved.email !== void 0 ? { email: resolved.email } : {},
|
|
@@ -5035,7 +4819,7 @@ async function readKeyFromHost(spawnFn, key, logger) {
|
|
|
5035
4819
|
}
|
|
5036
4820
|
async function readExistingGitconfig(filePath) {
|
|
5037
4821
|
try {
|
|
5038
|
-
const content = await
|
|
4822
|
+
const content = await fs10.readFile(filePath, "utf8");
|
|
5039
4823
|
const result = {};
|
|
5040
4824
|
const nameMatch = /^\s*name\s*=\s*(.+?)\s*$/m.exec(content);
|
|
5041
4825
|
const emailMatch = /^\s*email\s*=\s*(.+?)\s*$/m.exec(content);
|
|
@@ -5068,7 +4852,7 @@ ${sectionLine(label)}
|
|
|
5068
4852
|
);
|
|
5069
4853
|
}
|
|
5070
4854
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
5071
|
-
if (!
|
|
4855
|
+
if (!existsSync7(ymlPath)) {
|
|
5072
4856
|
throw new Error(
|
|
5073
4857
|
`No such config: ${ymlPath}. Run \`monoceros init <template> ${opts.name}\` first.`
|
|
5074
4858
|
);
|
|
@@ -5174,16 +4958,6 @@ Fix the value in the env file (or the yml).`
|
|
|
5174
4958
|
throw new Error(formatMissingCredentialsError(missing));
|
|
5175
4959
|
}
|
|
5176
4960
|
}
|
|
5177
|
-
const declaredRepos = createOpts.repos ?? [];
|
|
5178
|
-
if (declaredRepos.length > 0) {
|
|
5179
|
-
const reachability = await checkRepoReachability(declaredRepos, {
|
|
5180
|
-
...opts.reachabilitySpawn ? { spawn: opts.reachabilitySpawn } : {}
|
|
5181
|
-
});
|
|
5182
|
-
const unreachable = reachability.filter((r) => !r.ok);
|
|
5183
|
-
if (unreachable.length > 0) {
|
|
5184
|
-
throw new Error(formatUnreachableReposError(unreachable));
|
|
5185
|
-
}
|
|
5186
|
-
}
|
|
5187
4961
|
section("Scaffold");
|
|
5188
4962
|
const dockerMode = await detectDockerMode({
|
|
5189
4963
|
...opts.dockerInfoSpawn ? { spawn: opts.dockerInfoSpawn } : {}
|
|
@@ -5191,7 +4965,7 @@ Fix the value in the env file (or the yml).`
|
|
|
5191
4965
|
if (dockerMode === "rootless") {
|
|
5192
4966
|
throw new Error(formatRootlessNotSupportedError());
|
|
5193
4967
|
}
|
|
5194
|
-
await
|
|
4968
|
+
await fs11.mkdir(targetDir, { recursive: true });
|
|
5195
4969
|
await writeScaffold(createOpts, targetDir, { dockerMode });
|
|
5196
4970
|
await writeStateFile(
|
|
5197
4971
|
targetDir,
|
|
@@ -5202,23 +4976,6 @@ Fix the value in the env file (or the yml).`
|
|
|
5202
4976
|
})
|
|
5203
4977
|
);
|
|
5204
4978
|
logger.success(`materialized into ${prettyPath(targetDir)}`);
|
|
5205
|
-
const reposToClone = createOpts.repos ?? [];
|
|
5206
|
-
if (reposToClone.length > 0) {
|
|
5207
|
-
const cloneResults = await cloneReposHostSide(targetDir, reposToClone, {
|
|
5208
|
-
...opts.cloneSpawn ? { spawn: opts.cloneSpawn } : {}
|
|
5209
|
-
});
|
|
5210
|
-
for (const r of cloneResults) {
|
|
5211
|
-
if (r.status === "cloned") {
|
|
5212
|
-
logger.success(`cloned ${cyan2(r.path)} ${dim(`(${r.url})`)}`);
|
|
5213
|
-
} else if (r.status === "skipped") {
|
|
5214
|
-
logger.info(`projects/${r.path} already present \u2014 skipped clone`);
|
|
5215
|
-
}
|
|
5216
|
-
}
|
|
5217
|
-
const cloneFailures = cloneResults.filter((r) => r.status === "failed");
|
|
5218
|
-
if (cloneFailures.length > 0) {
|
|
5219
|
-
throw new Error(formatCloneFailuresError(cloneFailures));
|
|
5220
|
-
}
|
|
5221
|
-
}
|
|
5222
4979
|
section("Container");
|
|
5223
4980
|
const featureRefs = parsed.config.features.map((f) => f.ref);
|
|
5224
4981
|
if (featureRefs.length > 0) {
|
|
@@ -5266,8 +5023,8 @@ Fix the value in the env file (or the yml).`
|
|
|
5266
5023
|
return { targetDir, configPath: ymlPath, containerExitCode: exitCode };
|
|
5267
5024
|
}
|
|
5268
5025
|
async function assertSafeTargetDir(targetDir, expectedOrigin) {
|
|
5269
|
-
if (!
|
|
5270
|
-
const entries = await
|
|
5026
|
+
if (!existsSync7(targetDir)) return;
|
|
5027
|
+
const entries = await fs11.readdir(targetDir);
|
|
5271
5028
|
if (entries.length === 0) return;
|
|
5272
5029
|
const state = await readStateFile(targetDir);
|
|
5273
5030
|
if (state) {
|
|
@@ -5337,7 +5094,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
5337
5094
|
}
|
|
5338
5095
|
if (wantContainer) {
|
|
5339
5096
|
try {
|
|
5340
|
-
const text = await
|
|
5097
|
+
const text = await fs11.readFile(ymlPath, "utf8");
|
|
5341
5098
|
const parsed = parseConfig(text, ymlPath);
|
|
5342
5099
|
const changed = setContainerGitUserInDoc(parsed.doc, {
|
|
5343
5100
|
name: prompted.name,
|
|
@@ -5345,7 +5102,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
5345
5102
|
});
|
|
5346
5103
|
if (changed) {
|
|
5347
5104
|
const out = stringifyConfig(parsed.doc);
|
|
5348
|
-
await
|
|
5105
|
+
await fs11.writeFile(ymlPath, out, "utf8");
|
|
5349
5106
|
logger.info(
|
|
5350
5107
|
`Saved identity in this container \u2014 wrote git.user into ${prettyPath(ymlPath)}.`
|
|
5351
5108
|
);
|
|
@@ -5359,7 +5116,7 @@ async function persistPromptedIdentity(prompted, ymlPath, home, logger) {
|
|
|
5359
5116
|
}
|
|
5360
5117
|
|
|
5361
5118
|
// src/version.ts
|
|
5362
|
-
var CLI_VERSION = true ? "1.13.
|
|
5119
|
+
var CLI_VERSION = true ? "1.13.3" : "dev";
|
|
5363
5120
|
|
|
5364
5121
|
// src/commands/_dispatch.ts
|
|
5365
5122
|
import { consola as consola12 } from "consola";
|
|
@@ -5420,7 +5177,7 @@ function renderCompletionScript(shell) {
|
|
|
5420
5177
|
' COMPREPLY=( $(compgen -W "$candidates" -- "$cur") )',
|
|
5421
5178
|
" # Suppress the trailing space when bash narrowed the candidate",
|
|
5422
5179
|
" # set to a single token that ends with `=` \u2014 those are value-",
|
|
5423
|
-
" # flags (`--with=`, `--with-ports=`, \u2026) where the user types the",
|
|
5180
|
+
" # flags (`--with-features=`, `--with-ports=`, \u2026) where the user types the",
|
|
5424
5181
|
" # value immediately after.",
|
|
5425
5182
|
' if [[ ${#COMPREPLY[@]} -eq 1 && "${COMPREPLY[0]}" == *= ]]; then',
|
|
5426
5183
|
" compopt -o nospace",
|
|
@@ -5493,6 +5250,10 @@ var completionCommand = defineCommand9({
|
|
|
5493
5250
|
meta: {
|
|
5494
5251
|
name: "completion",
|
|
5495
5252
|
group: "tooling",
|
|
5253
|
+
// Hidden from `monoceros --help`: the install scripts wire up
|
|
5254
|
+
// completion automatically; manual setup is documented in
|
|
5255
|
+
// docs/commands/completion.md. Still runnable directly.
|
|
5256
|
+
hidden: true,
|
|
5496
5257
|
description: "Print a shell completion script for bash, zsh or PowerShell to stdout. Pipe the output into a file your shell loads at startup. The install scripts (install.sh / install.ps1) call this automatically."
|
|
5497
5258
|
},
|
|
5498
5259
|
args: {
|
|
@@ -5519,8 +5280,8 @@ var completionCommand = defineCommand9({
|
|
|
5519
5280
|
import { defineCommand as defineCommand10 } from "citty";
|
|
5520
5281
|
|
|
5521
5282
|
// src/completion/resolve.ts
|
|
5522
|
-
import { existsSync as
|
|
5523
|
-
import
|
|
5283
|
+
import { existsSync as existsSync8, promises as fs12 } from "fs";
|
|
5284
|
+
import path14 from "path";
|
|
5524
5285
|
async function resolveCompletions(line, point, opts = {}) {
|
|
5525
5286
|
const { prev, current } = parseCompletionLine(line, point);
|
|
5526
5287
|
const ctx = { prev, current, opts };
|
|
@@ -5668,9 +5429,9 @@ function filterPrefix(values, fragment) {
|
|
|
5668
5429
|
}
|
|
5669
5430
|
async function listContainerNames(ctx) {
|
|
5670
5431
|
const home = ctx.opts.monocerosHome ?? monocerosHome();
|
|
5671
|
-
const dir =
|
|
5672
|
-
if (!
|
|
5673
|
-
const entries = await
|
|
5432
|
+
const dir = path14.join(home, "container-configs");
|
|
5433
|
+
if (!existsSync8(dir)) return [];
|
|
5434
|
+
const entries = await fs12.readdir(dir);
|
|
5674
5435
|
return entries.filter((e) => e.endsWith(".yml")).map((e) => e.slice(0, -".yml".length)).sort();
|
|
5675
5436
|
}
|
|
5676
5437
|
async function listFeatureComponents() {
|
|
@@ -5888,6 +5649,8 @@ var __completeCommand = defineCommand10({
|
|
|
5888
5649
|
meta: {
|
|
5889
5650
|
name: "__complete",
|
|
5890
5651
|
group: "internal",
|
|
5652
|
+
// Internal plumbing for the shell wrappers — never shown in help.
|
|
5653
|
+
hidden: true,
|
|
5891
5654
|
description: "Internal \u2014 shell completion engine. Used by the wrappers emitted by `monoceros completion <shell>`. Output one candidate completion per line."
|
|
5892
5655
|
},
|
|
5893
5656
|
args: {
|
|
@@ -5925,8 +5688,8 @@ import { defineCommand as defineCommand11 } from "citty";
|
|
|
5925
5688
|
import { consola as consola14 } from "consola";
|
|
5926
5689
|
|
|
5927
5690
|
// src/init/index.ts
|
|
5928
|
-
import { existsSync as
|
|
5929
|
-
import
|
|
5691
|
+
import { existsSync as existsSync9, promises as fs13 } from "fs";
|
|
5692
|
+
import path15 from "path";
|
|
5930
5693
|
import { consola as consola13 } from "consola";
|
|
5931
5694
|
|
|
5932
5695
|
// src/init/generator.ts
|
|
@@ -6292,7 +6055,7 @@ async function runInit(opts) {
|
|
|
6292
6055
|
);
|
|
6293
6056
|
}
|
|
6294
6057
|
const dest = containerConfigPath(opts.name, home);
|
|
6295
|
-
if (
|
|
6058
|
+
if (existsSync9(dest)) {
|
|
6296
6059
|
throw new Error(
|
|
6297
6060
|
`Config already exists: ${dest}. Delete it manually before re-running \`monoceros init\` \u2014 this protects any hand-edits.`
|
|
6298
6061
|
);
|
|
@@ -6363,9 +6126,9 @@ async function runInit(opts) {
|
|
|
6363
6126
|
} else {
|
|
6364
6127
|
text = generateComposedYml(opts.name, composed, lookup, repos, ports);
|
|
6365
6128
|
}
|
|
6366
|
-
await
|
|
6129
|
+
await fs13.mkdir(containerConfigsDir(home), { recursive: true });
|
|
6367
6130
|
await ensureEnvGitignored(containerConfigsDir(home));
|
|
6368
|
-
await
|
|
6131
|
+
await fs13.writeFile(dest, text, "utf8");
|
|
6369
6132
|
const envPath = containerEnvPath(opts.name, home);
|
|
6370
6133
|
const seedVars = {};
|
|
6371
6134
|
for (const f of composed.features) {
|
|
@@ -6388,8 +6151,8 @@ async function runInit(opts) {
|
|
|
6388
6151
|
}
|
|
6389
6152
|
await ensureEnvVars(envPath, opts.name, seedVars);
|
|
6390
6153
|
const documented = !anyComposed;
|
|
6391
|
-
const ymlRel =
|
|
6392
|
-
const envRel =
|
|
6154
|
+
const ymlRel = path15.relative(home, dest);
|
|
6155
|
+
const envRel = path15.relative(home, envPath);
|
|
6393
6156
|
if (documented) {
|
|
6394
6157
|
logger.success(`Wrote documented default to ${ymlRel} and ${envRel}.`);
|
|
6395
6158
|
logger.info(
|
|
@@ -6655,7 +6418,7 @@ var listComponentsCommand = defineCommand12({
|
|
|
6655
6418
|
meta: {
|
|
6656
6419
|
name: "list-components",
|
|
6657
6420
|
group: "discovery",
|
|
6658
|
-
description: "Print the components catalog used by `monoceros init --with=\u2026`, grouped by category (Languages, Services, Features). Component names render in cyan, descriptions in default colour; when piped, the formatting drops out and lines become `name<TAB>description` for grep/awk-friendly consumption."
|
|
6421
|
+
description: "Print the components catalog used by `monoceros init --with-languages=\u2026 / --with-services=\u2026 / --with-features=\u2026`, grouped by category (Languages, Services, Features). Component names render in cyan, descriptions in default colour; when piped, the formatting drops out and lines become `name<TAB>description` for grep/awk-friendly consumption."
|
|
6659
6422
|
},
|
|
6660
6423
|
args: {},
|
|
6661
6424
|
async run() {
|
|
@@ -6924,8 +6687,8 @@ import { consola as consola20 } from "consola";
|
|
|
6924
6687
|
import { createInterface } from "readline/promises";
|
|
6925
6688
|
|
|
6926
6689
|
// src/remove/index.ts
|
|
6927
|
-
import { existsSync as
|
|
6928
|
-
import
|
|
6690
|
+
import { existsSync as existsSync10, promises as fs14 } from "fs";
|
|
6691
|
+
import path16 from "path";
|
|
6929
6692
|
import { consola as consola19 } from "consola";
|
|
6930
6693
|
async function runRemove(opts) {
|
|
6931
6694
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -6942,9 +6705,9 @@ async function runRemove(opts) {
|
|
|
6942
6705
|
const ymlPath = containerConfigPath(opts.name, home);
|
|
6943
6706
|
const envPath = containerEnvPath(opts.name, home);
|
|
6944
6707
|
const containerPath = containerDir(opts.name, home);
|
|
6945
|
-
const hasYml =
|
|
6946
|
-
const hasEnv =
|
|
6947
|
-
const hasContainer =
|
|
6708
|
+
const hasYml = existsSync10(ymlPath);
|
|
6709
|
+
const hasEnv = existsSync10(envPath);
|
|
6710
|
+
const hasContainer = existsSync10(containerPath);
|
|
6948
6711
|
if (!hasYml && !hasContainer) {
|
|
6949
6712
|
throw new Error(
|
|
6950
6713
|
`Nothing to remove for '${opts.name}': neither ${ymlPath} nor ${containerPath} exists.`
|
|
@@ -6968,30 +6731,30 @@ async function runRemove(opts) {
|
|
|
6968
6731
|
let backupPath = null;
|
|
6969
6732
|
if (!opts.noBackup && (hasYml || hasContainer)) {
|
|
6970
6733
|
const ts = (opts.now ?? /* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
6971
|
-
backupPath =
|
|
6972
|
-
await
|
|
6734
|
+
backupPath = path16.join(home, "container-backups", `${opts.name}-${ts}`);
|
|
6735
|
+
await fs14.mkdir(backupPath, { recursive: true });
|
|
6973
6736
|
if (hasYml) {
|
|
6974
|
-
await
|
|
6737
|
+
await fs14.copyFile(ymlPath, path16.join(backupPath, `${opts.name}.yml`));
|
|
6975
6738
|
}
|
|
6976
6739
|
if (hasEnv) {
|
|
6977
|
-
await
|
|
6740
|
+
await fs14.copyFile(envPath, path16.join(backupPath, `${opts.name}.env`));
|
|
6978
6741
|
}
|
|
6979
6742
|
if (hasContainer) {
|
|
6980
|
-
await
|
|
6743
|
+
await fs14.cp(containerPath, path16.join(backupPath, "container"), {
|
|
6981
6744
|
recursive: true
|
|
6982
6745
|
});
|
|
6983
6746
|
}
|
|
6984
6747
|
logger.info(`Backup written to ${prettyPath(backupPath)}.`);
|
|
6985
6748
|
}
|
|
6986
6749
|
if (hasYml) {
|
|
6987
|
-
await
|
|
6750
|
+
await fs14.rm(ymlPath, { force: true });
|
|
6988
6751
|
}
|
|
6989
6752
|
if (hasEnv) {
|
|
6990
|
-
await
|
|
6753
|
+
await fs14.rm(envPath, { force: true });
|
|
6991
6754
|
}
|
|
6992
6755
|
if (hasContainer) {
|
|
6993
6756
|
try {
|
|
6994
|
-
await
|
|
6757
|
+
await fs14.rm(containerPath, { recursive: true, force: true });
|
|
6995
6758
|
} catch (err) {
|
|
6996
6759
|
const code = err.code;
|
|
6997
6760
|
if (code !== "EACCES" && code !== "EPERM") {
|
|
@@ -7017,7 +6780,7 @@ async function runRemove(opts) {
|
|
|
7017
6780
|
`docker-based cleanup of ${containerPath} exited ${exit}. Inspect with \`sudo ls -la ${containerPath}\` and clean manually.`
|
|
7018
6781
|
);
|
|
7019
6782
|
}
|
|
7020
|
-
await
|
|
6783
|
+
await fs14.rm(containerPath, { recursive: true, force: true });
|
|
7021
6784
|
}
|
|
7022
6785
|
}
|
|
7023
6786
|
logger.success(
|
|
@@ -7120,8 +6883,8 @@ import { defineCommand as defineCommand18 } from "citty";
|
|
|
7120
6883
|
import { consola as consola22 } from "consola";
|
|
7121
6884
|
|
|
7122
6885
|
// src/restore/index.ts
|
|
7123
|
-
import { existsSync as
|
|
7124
|
-
import
|
|
6886
|
+
import { existsSync as existsSync11, promises as fs15 } from "fs";
|
|
6887
|
+
import path17 from "path";
|
|
7125
6888
|
import { consola as consola21 } from "consola";
|
|
7126
6889
|
async function runRestore(opts) {
|
|
7127
6890
|
const home = opts.monocerosHome ?? monocerosHome();
|
|
@@ -7129,15 +6892,15 @@ async function runRestore(opts) {
|
|
|
7129
6892
|
info: (msg) => consola21.info(msg),
|
|
7130
6893
|
success: (msg) => consola21.success(msg)
|
|
7131
6894
|
};
|
|
7132
|
-
const backup =
|
|
7133
|
-
if (!
|
|
6895
|
+
const backup = path17.resolve(opts.backupPath);
|
|
6896
|
+
if (!existsSync11(backup)) {
|
|
7134
6897
|
throw new Error(`Backup not found: ${backup}.`);
|
|
7135
6898
|
}
|
|
7136
|
-
const stat = await
|
|
6899
|
+
const stat = await fs15.stat(backup);
|
|
7137
6900
|
if (!stat.isDirectory()) {
|
|
7138
6901
|
throw new Error(`Backup path is not a directory: ${backup}.`);
|
|
7139
6902
|
}
|
|
7140
|
-
const entries = await
|
|
6903
|
+
const entries = await fs15.readdir(backup);
|
|
7141
6904
|
const ymlFiles = entries.filter((f) => f.endsWith(".yml"));
|
|
7142
6905
|
if (ymlFiles.length === 0) {
|
|
7143
6906
|
throw new Error(
|
|
@@ -7151,29 +6914,29 @@ async function runRestore(opts) {
|
|
|
7151
6914
|
}
|
|
7152
6915
|
const ymlFile = ymlFiles[0];
|
|
7153
6916
|
const name = ymlFile.replace(/\.yml$/, "");
|
|
7154
|
-
const containerInBackup =
|
|
7155
|
-
const hasContainer =
|
|
7156
|
-
const envInBackup =
|
|
7157
|
-
const hasEnv =
|
|
6917
|
+
const containerInBackup = path17.join(backup, "container");
|
|
6918
|
+
const hasContainer = existsSync11(containerInBackup);
|
|
6919
|
+
const envInBackup = path17.join(backup, `${name}.env`);
|
|
6920
|
+
const hasEnv = existsSync11(envInBackup);
|
|
7158
6921
|
const destYml = containerConfigPath(name, home);
|
|
7159
6922
|
const destContainer = containerDir(name, home);
|
|
7160
|
-
if (
|
|
6923
|
+
if (existsSync11(destYml)) {
|
|
7161
6924
|
throw new Error(
|
|
7162
6925
|
`Refusing to restore: ${destYml} already exists. Remove the current container first (\`monoceros remove ${name}\`) or rename the existing config.`
|
|
7163
6926
|
);
|
|
7164
6927
|
}
|
|
7165
|
-
if (hasContainer &&
|
|
6928
|
+
if (hasContainer && existsSync11(destContainer)) {
|
|
7166
6929
|
throw new Error(
|
|
7167
6930
|
`Refusing to restore: ${destContainer} already exists. Remove the current container first (\`monoceros remove ${name}\`).`
|
|
7168
6931
|
);
|
|
7169
6932
|
}
|
|
7170
|
-
await
|
|
7171
|
-
await
|
|
6933
|
+
await fs15.mkdir(containerConfigsDir(home), { recursive: true });
|
|
6934
|
+
await fs15.copyFile(path17.join(backup, ymlFile), destYml);
|
|
7172
6935
|
if (hasEnv) {
|
|
7173
|
-
await
|
|
6936
|
+
await fs15.copyFile(envInBackup, containerEnvPath(name, home));
|
|
7174
6937
|
}
|
|
7175
6938
|
if (hasContainer) {
|
|
7176
|
-
await
|
|
6939
|
+
await fs15.cp(containerInBackup, destContainer, { recursive: true });
|
|
7177
6940
|
}
|
|
7178
6941
|
logger.success(`Restored '${name}' from ${prettyPath(backup)}.`);
|
|
7179
6942
|
logger.info(
|
|
@@ -7431,8 +7194,8 @@ import { defineCommand as defineCommand24 } from "citty";
|
|
|
7431
7194
|
import { consola as consola28 } from "consola";
|
|
7432
7195
|
|
|
7433
7196
|
// src/devcontainer/shell.ts
|
|
7434
|
-
import { existsSync as
|
|
7435
|
-
import
|
|
7197
|
+
import { existsSync as existsSync12 } from "fs";
|
|
7198
|
+
import path18 from "path";
|
|
7436
7199
|
async function runShell(opts) {
|
|
7437
7200
|
assertContainerExists(opts.root);
|
|
7438
7201
|
const spawnFn = opts.spawn ?? spawnDevcontainer;
|
|
@@ -7455,7 +7218,7 @@ async function runShell(opts) {
|
|
|
7455
7218
|
);
|
|
7456
7219
|
}
|
|
7457
7220
|
function assertContainerExists(root) {
|
|
7458
|
-
if (!
|
|
7221
|
+
if (!existsSync12(path18.join(root, ".devcontainer"))) {
|
|
7459
7222
|
throw new Error(
|
|
7460
7223
|
`No .devcontainer/ at ${root}. Run \`monoceros apply <name>\` first.`
|
|
7461
7224
|
);
|
|
@@ -7667,15 +7430,15 @@ import { defineCommand as defineCommand29 } from "citty";
|
|
|
7667
7430
|
import { consola as consola33 } from "consola";
|
|
7668
7431
|
|
|
7669
7432
|
// src/tunnel/run.ts
|
|
7670
|
-
import { spawn as
|
|
7433
|
+
import { spawn as spawn8 } from "child_process";
|
|
7671
7434
|
import { consola as consola32 } from "consola";
|
|
7672
7435
|
|
|
7673
7436
|
// src/tunnel/resolve.ts
|
|
7674
|
-
import { existsSync as
|
|
7675
|
-
import
|
|
7437
|
+
import { existsSync as existsSync13 } from "fs";
|
|
7438
|
+
import path19 from "path";
|
|
7676
7439
|
async function resolveTunnelTarget(opts) {
|
|
7677
7440
|
const ymlPath = containerConfigPath(opts.name, opts.monocerosHome);
|
|
7678
|
-
if (!
|
|
7441
|
+
if (!existsSync13(ymlPath)) {
|
|
7679
7442
|
throw new Error(
|
|
7680
7443
|
`No yml profile for '${opts.name}' at ${ymlPath}. Run \`monoceros init ${opts.name}\` first.`
|
|
7681
7444
|
);
|
|
@@ -7683,13 +7446,13 @@ async function resolveTunnelTarget(opts) {
|
|
|
7683
7446
|
const parsed = await readConfig(ymlPath);
|
|
7684
7447
|
const config = parsed.config;
|
|
7685
7448
|
const containerRoot = containerDir(opts.name, opts.monocerosHome);
|
|
7686
|
-
if (!
|
|
7449
|
+
if (!existsSync13(containerRoot)) {
|
|
7687
7450
|
throw new Error(
|
|
7688
7451
|
`Container '${opts.name}' is not materialised at ${containerRoot}. Run \`monoceros apply ${opts.name}\` first.`
|
|
7689
7452
|
);
|
|
7690
7453
|
}
|
|
7691
|
-
const composePath =
|
|
7692
|
-
const isCompose =
|
|
7454
|
+
const composePath = path19.join(containerRoot, ".devcontainer", "compose.yaml");
|
|
7455
|
+
const isCompose = existsSync13(composePath);
|
|
7693
7456
|
const parsedTarget = parseTargetArg(opts.target, config);
|
|
7694
7457
|
const docker = opts.docker ?? defaultDockerExec;
|
|
7695
7458
|
if (isCompose) {
|
|
@@ -7910,7 +7673,7 @@ function formatLocalPortHeldError(port, address, result) {
|
|
|
7910
7673
|
// src/tunnel/run.ts
|
|
7911
7674
|
var SOCAT_IMAGE = "alpine/socat:1.8.0.3";
|
|
7912
7675
|
var defaultDockerSpawn = (args) => {
|
|
7913
|
-
const child =
|
|
7676
|
+
const child = spawn8("docker", args, {
|
|
7914
7677
|
stdio: "inherit"
|
|
7915
7678
|
});
|
|
7916
7679
|
const exited = new Promise((resolve, reject) => {
|