@getmonoceros/workbench 1.6.1 → 1.6.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 CHANGED
@@ -797,13 +797,14 @@ function isValidHomeSubpath(p) {
797
797
  return p.length > 0 && !p.startsWith("/") && !p.includes("..") && HOME_SUBPATH_RE.test(p);
798
798
  }
799
799
  var HOME_SUBPATH_RE = /^[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/;
800
- function buildDevcontainerJson(opts) {
800
+ function buildDevcontainerJson(opts, dockerMode = "rootful") {
801
801
  const resolvedFeatures = resolveFeatures(opts);
802
802
  const features = {};
803
803
  for (const f of resolvedFeatures) {
804
804
  features[f.devcontainerKey] = f.options;
805
805
  }
806
806
  const featuresField = Object.keys(features).length > 0 ? { features } : void 0;
807
+ const idmapSuffix = dockerMode === "rootless" ? ",idmap" : "";
807
808
  const homeMounts = [];
808
809
  for (const f of resolvedFeatures) {
809
810
  const allSubs = [
@@ -812,7 +813,7 @@ function buildDevcontainerJson(opts) {
812
813
  ];
813
814
  for (const sub of allSubs) {
814
815
  homeMounts.push(
815
- `source=\${localWorkspaceFolder}/home/${sub},target=/home/node/${sub},type=bind`
816
+ `source=\${localWorkspaceFolder}/home/${sub},target=/home/node/${sub},type=bind${idmapSuffix}`
816
817
  );
817
818
  }
818
819
  }
@@ -831,10 +832,14 @@ function buildDevcontainerJson(opts) {
831
832
  }
832
833
  const mounts = [...homeMounts];
833
834
  const mountsField = mounts.length > 0 ? { mounts } : {};
835
+ const workspaceMountField = dockerMode === "rootless" ? {
836
+ workspaceMount: `source=\${localWorkspaceFolder},target=/workspaces/${opts.name},type=bind${idmapSuffix}`
837
+ } : {};
834
838
  return {
835
839
  name: opts.name,
836
840
  image: BASE_IMAGE,
837
841
  remoteUser: "node",
842
+ ...workspaceMountField,
838
843
  ...mountsField,
839
844
  runArgs: ["--cap-add=NET_ADMIN"],
840
845
  forwardPorts: [3e3, 4e3],
@@ -842,15 +847,25 @@ function buildDevcontainerJson(opts) {
842
847
  ...featuresField ?? {}
843
848
  };
844
849
  }
845
- function buildComposeYaml(opts) {
850
+ function buildComposeYaml(opts, dockerMode = "rootful") {
846
851
  const lines = ["services:"];
852
+ const isRootless = dockerMode === "rootless";
847
853
  lines.push(" workspace:");
848
854
  lines.push(` image: ${BASE_IMAGE}`);
849
855
  lines.push(" command: 'sleep infinity'");
850
856
  lines.push(" cap_add:");
851
857
  lines.push(" - NET_ADMIN");
852
858
  lines.push(" volumes:");
853
- lines.push(` - ..:/workspaces/${opts.name}:cached`);
859
+ if (isRootless) {
860
+ lines.push(" - type: bind");
861
+ lines.push(" source: ..");
862
+ lines.push(` target: /workspaces/${opts.name}`);
863
+ lines.push(" bind:");
864
+ lines.push(" create_host_path: true");
865
+ lines.push(" idmap: true");
866
+ } else {
867
+ lines.push(` - ..:/workspaces/${opts.name}:cached`);
868
+ }
854
869
  const resolvedFeatures = resolveFeatures(opts);
855
870
  for (const f of resolvedFeatures) {
856
871
  const allSubs = [
@@ -858,7 +873,16 @@ function buildComposeYaml(opts) {
858
873
  ...f.persistentHomeFiles.map((entry2) => entry2.path)
859
874
  ];
860
875
  for (const sub of allSubs) {
861
- lines.push(` - ../home/${sub}:/home/node/${sub}`);
876
+ if (isRootless) {
877
+ lines.push(" - type: bind");
878
+ lines.push(` source: ../home/${sub}`);
879
+ lines.push(` target: /home/node/${sub}`);
880
+ lines.push(" bind:");
881
+ lines.push(" create_host_path: true");
882
+ lines.push(" idmap: true");
883
+ } else {
884
+ lines.push(` - ../home/${sub}:/home/node/${sub}`);
885
+ }
862
886
  }
863
887
  }
864
888
  for (const svcId of opts.services) {
@@ -990,7 +1014,8 @@ async function writePostCreateScript(devcontainerDir, opts) {
990
1014
  await fs2.writeFile(dest, buildPostCreateScript(opts));
991
1015
  await fs2.chmod(dest, 493);
992
1016
  }
993
- async function writeScaffold(opts, targetDir) {
1017
+ async function writeScaffold(opts, targetDir, scaffoldOpts = {}) {
1018
+ const dockerMode = scaffoldOpts.dockerMode ?? "rootful";
994
1019
  const devcontainerDir = path2.join(targetDir, ".devcontainer");
995
1020
  const monocerosDir = path2.join(targetDir, ".monoceros");
996
1021
  const projectsDir = path2.join(targetDir, "projects");
@@ -1019,7 +1044,7 @@ async function writeScaffold(opts, targetDir) {
1019
1044
  path2.join(monocerosDir, ".gitignore"),
1020
1045
  "git-credentials*\ngitconfig\n"
1021
1046
  );
1022
- const devcontainerJson = buildDevcontainerJson(opts);
1047
+ const devcontainerJson = buildDevcontainerJson(opts, dockerMode);
1023
1048
  await fs2.writeFile(
1024
1049
  path2.join(devcontainerDir, "devcontainer.json"),
1025
1050
  JSON.stringify(devcontainerJson, null, 2) + "\n"
@@ -1050,7 +1075,7 @@ async function writeScaffold(opts, targetDir) {
1050
1075
  await writePostCreateScript(devcontainerDir, opts);
1051
1076
  const composePath = path2.join(devcontainerDir, "compose.yaml");
1052
1077
  if (needsCompose(opts)) {
1053
- await fs2.writeFile(composePath, buildComposeYaml(opts));
1078
+ await fs2.writeFile(composePath, buildComposeYaml(opts, dockerMode));
1054
1079
  } else if (existsSync2(composePath)) {
1055
1080
  await fs2.rm(composePath);
1056
1081
  }
@@ -2613,14 +2638,44 @@ function adviceForKind(kind) {
2613
2638
  }
2614
2639
  }
2615
2640
 
2616
- // src/devcontainer/identity.ts
2641
+ // src/devcontainer/docker-mode.ts
2617
2642
  import { spawn as spawn5 } from "child_process";
2643
+ var realDockerInfo = () => {
2644
+ return new Promise((resolve, reject) => {
2645
+ const child = spawn5(
2646
+ "docker",
2647
+ ["info", "--format", "{{json .SecurityOptions}}"],
2648
+ {
2649
+ stdio: ["ignore", "pipe", "inherit"]
2650
+ }
2651
+ );
2652
+ let stdout = "";
2653
+ child.stdout.on("data", (chunk) => {
2654
+ stdout += chunk.toString();
2655
+ });
2656
+ child.on("error", reject);
2657
+ child.on("exit", (code) => resolve({ stdout, exitCode: code ?? 0 }));
2658
+ });
2659
+ };
2660
+ async function detectDockerMode(options = {}) {
2661
+ const spawnFn = options.spawn ?? realDockerInfo;
2662
+ try {
2663
+ const result = await spawnFn();
2664
+ if (result.exitCode !== 0) return "rootful";
2665
+ return /\brootless\b/i.test(result.stdout) ? "rootless" : "rootful";
2666
+ } catch {
2667
+ return "rootful";
2668
+ }
2669
+ }
2670
+
2671
+ // src/devcontainer/identity.ts
2672
+ import { spawn as spawn6 } from "child_process";
2618
2673
  import { promises as fs7 } from "fs";
2619
2674
  import path7 from "path";
2620
2675
  import { consola as consola9 } from "consola";
2621
2676
  var realGitConfigGet = (key) => {
2622
2677
  return new Promise((resolve, reject) => {
2623
- const child = spawn5("git", ["config", "--global", "--get", key], {
2678
+ const child = spawn6("git", ["config", "--global", "--get", key], {
2624
2679
  stdio: ["ignore", "pipe", "inherit"]
2625
2680
  });
2626
2681
  let stdout = "";
@@ -2804,8 +2859,11 @@ ${sectionLine(label)}
2804
2859
  }
2805
2860
  }
2806
2861
  section("Scaffold");
2862
+ const dockerMode = await detectDockerMode({
2863
+ ...opts.dockerInfoSpawn ? { spawn: opts.dockerInfoSpawn } : {}
2864
+ });
2807
2865
  await fs8.mkdir(targetDir, { recursive: true });
2808
- await writeScaffold(createOpts, targetDir);
2866
+ await writeScaffold(createOpts, targetDir, { dockerMode });
2809
2867
  await writeStateFile(
2810
2868
  targetDir,
2811
2869
  buildStateFile({
@@ -2850,8 +2908,11 @@ async function assertSafeTargetDir(targetDir, expectedOrigin) {
2850
2908
  }
2851
2909
  return;
2852
2910
  }
2911
+ if (entries.length === 1 && entries[0] === ".monoceros") {
2912
+ return;
2913
+ }
2853
2914
  throw new Error(
2854
- `Refusing to materialize into non-empty directory ${targetDir} (no Monoceros state.json found). Delete the directory before re-running.`
2915
+ `Refusing to materialize into non-empty directory ${targetDir} (no Monoceros state.json found, and the directory has files we don't recognise). Delete the directory before re-running.`
2855
2916
  );
2856
2917
  }
2857
2918
  function warnOnDeprecatedFeatureRefs(containerFeatures, globalConfig, logger) {