@gorlitzer-labs/seldon 0.1.2 → 0.1.4

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/bin/seldon.js CHANGED
@@ -55,12 +55,21 @@ function run(cmd, args, opts = {}) {
55
55
  // Is a command on PATH? (used to pick the fastest package manager)
56
56
  const has = (bin) => { try { execSync(`command -v ${bin}`, { stdio: "ignore" }); return true; } catch { return false; } };
57
57
 
58
- // Pick the node package manager: --pm=<x> override, else the fastest present.
59
- // pnpm/bun install globally far faster than npm; npm is the universal fallback.
58
+ // pnpm can only `add -g` when a global bin dir is configured (else ERR_PNPM_NO_GLOBAL_BIN_DIR).
59
+ const pnpmGlobalReady = () => {
60
+ if (process.env.PNPM_HOME) return true;
61
+ try {
62
+ const d = execSync("pnpm config get global-bin-dir", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
63
+ return !!d && d !== "undefined" && d !== "null";
64
+ } catch { return false; }
65
+ };
66
+
67
+ // Pick the node package manager: --pm=<x> override, else the fastest that can
68
+ // actually global-install; npm is the universal fallback.
60
69
  function nodePM() {
61
70
  const ov = (process.argv.find((a) => a.startsWith("--pm=")) || "").split("=")[1];
62
71
  if (ov) return ov;
63
- if (has("pnpm")) return "pnpm";
72
+ if (has("pnpm") && pnpmGlobalReady()) return "pnpm";
64
73
  if (has("bun")) return "bun";
65
74
  return "npm";
66
75
  }
@@ -72,11 +81,15 @@ function installNpm(m, dev) {
72
81
  const pj = JSON.parse(fs.readFileSync(path.join(dir, "package.json")));
73
82
  if (pj.scripts?.build) run(pm, ["run", "build"], { cwd: dir });
74
83
  const link = pm === "pnpm" ? ["link", "--global"] : ["link"]; // bun/npm: `link`
75
- return run(pm, link, { cwd: dir });
84
+ if (run(pm, link, { cwd: dir })) return true;
85
+ if (pm !== "npm") { console.log(C.dim(" falling back to npm link…")); return run("npm", ["link"], { cwd: dir }); }
86
+ return false;
76
87
  }
77
88
  // pnpm/bun: `add -g` · npm: `install -g` — a global CLI, no project touched
78
89
  const add = pm === "npm" ? ["install", "-g", m.pkg] : ["add", "-g", m.pkg];
79
- return run(pm, add);
90
+ if (run(pm, add)) return true;
91
+ if (pm !== "npm") { console.log(C.dim(` ${pm} failed — falling back to npm…`)); return run("npm", ["install", "-g", m.pkg]); }
92
+ return false;
80
93
  }
81
94
 
82
95
  function installShell(m, dev) {
@@ -97,10 +110,15 @@ function installPython(m, dev) {
97
110
  if (!(dev && IN_CHECKOUT)) {
98
111
  console.log(C.dim(` fetching ${m.id} into ${dest}`));
99
112
  fs.mkdirSync(path.dirname(dest), { recursive: true });
100
- if (!fs.existsSync(path.join(dest, ".git"))) {
101
- if (!run("git", ["clone", "--depth", "1", `https://github.com/${MONOREPO}.git`, dest + ".tmp"])) return false;
102
- fs.renameSync(path.join(dest + ".tmp", m.dir), dest);
103
- fs.rmSync(dest + ".tmp", { recursive: true, force: true });
113
+ if (fs.existsSync(path.join(dest, ".git"))) {
114
+ run("git", ["-C", dest, "pull", "--ff-only"]); // already cloned → update in place
115
+ } else {
116
+ const tmp = dest + ".tmp";
117
+ fs.rmSync(tmp, { recursive: true, force: true }); // clear any stale tmp
118
+ if (!run("git", ["clone", "--depth", "1", `https://github.com/${MONOREPO}.git`, tmp])) return false;
119
+ fs.rmSync(dest, { recursive: true, force: true }); // dest must not exist before rename (fixes ENOTEMPTY)
120
+ fs.renameSync(path.join(tmp, m.dir), dest);
121
+ fs.rmSync(tmp, { recursive: true, force: true });
104
122
  }
105
123
  }
106
124
  // Always a DEDICATED venv under the module dir — we never install into the
package/modules.mjs CHANGED
@@ -83,13 +83,15 @@ export const MODULES = [
83
83
  export const byId = Object.fromEntries(MODULES.map((m) => [m.id, m]));
84
84
 
85
85
  /** External tools we can probe, and how a user installs each (macOS-first hint). */
86
+ // Hints are cross-platform on purpose — name the tool + where to get it, never
87
+ // assume a package manager (this ships to anyone, not one machine).
86
88
  export const DEPS = {
87
- node: { probe: "node --version", hint: "install Node 20+ (nvm, or brew install node)" },
88
- tmux: { probe: "tmux -V", hint: "brew install tmux" },
89
- sops: { probe: "sops --version", hint: "brew install sops" },
90
- age: { probe: "age --version", hint: "brew install age" },
91
- tailscale:{ probe: "tailscale version", hint: "brew install --cask tailscale (or tailscale.com/download)" },
92
- python3: { probe: "python3 --version", hint: "brew install python@3.13" },
89
+ node: { probe: "node --version", hint: "Node 20+ — nodejs.org / nvm / your package manager" },
90
+ tmux: { probe: "tmux -V", hint: "tmux — your package manager (brew · apt · dnf · pacman · pkg)" },
91
+ sops: { probe: "sops --version", hint: "sops — github.com/getsops/sops/releases (or brew · apt)" },
92
+ age: { probe: "age --version", hint: "age — github.com/FiloSottile/age/releases (or brew · apt)" },
93
+ tailscale:{ probe: "tailscale version", hint: "tailscale — tailscale.com/download" },
94
+ python3: { probe: "python3 --version", hint: "Python 3.11+ — python.org / your package manager" },
93
95
  };
94
96
 
95
97
  /** Expand a selection to include hard requires (factory -> apiary, foundation). */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gorlitzer-labs/seldon",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Install the Seldon stack — pick your tools from a checklist; each installs via its native method.",
5
5
  "type": "module",
6
6
  "bin": {