camstack 0.5.3 → 0.6.0

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.
Files changed (2) hide show
  1. package/dist/cli.js +63 -3
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -178,6 +178,33 @@ function resolveAddonPath(arg) {
178
178
  }
179
179
  return null;
180
180
  }
181
+ function readBuildScript(addonDir, scriptName) {
182
+ const pkgJsonPath = path2.join(addonDir, "package.json");
183
+ if (!fs2.existsSync(pkgJsonPath)) return null;
184
+ try {
185
+ const parsed = JSON.parse(fs2.readFileSync(pkgJsonPath, "utf8"));
186
+ if (!parsed || typeof parsed !== "object") return null;
187
+ const scripts = parsed.scripts;
188
+ if (!scripts || typeof scripts !== "object") return null;
189
+ const val = scripts[scriptName];
190
+ return typeof val === "string" && val.length > 0 ? scriptName : null;
191
+ } catch {
192
+ return null;
193
+ }
194
+ }
195
+ function buildAddon(addonDir, scriptName) {
196
+ console.log(`[camstack] Building (npm run ${scriptName}) in ${addonDir}...`);
197
+ try {
198
+ execSync(`npm run ${scriptName}`, {
199
+ cwd: addonDir,
200
+ stdio: "inherit",
201
+ timeout: 5 * 6e4
202
+ });
203
+ } catch (err) {
204
+ const msg = err instanceof Error ? err.message : String(err);
205
+ throw new Error(`Build script "${scriptName}" failed in ${addonDir}: ${msg}`);
206
+ }
207
+ }
181
208
  function packAddon(addonDir) {
182
209
  const pkgJsonPath = path2.join(addonDir, "package.json");
183
210
  if (!fs2.existsSync(pkgJsonPath)) {
@@ -258,6 +285,18 @@ async function deployAddon(addonPath, opts) {
258
285
  if (!fs2.existsSync(resolvedPath)) throw new Error(`File not found: ${resolvedPath}`);
259
286
  tgzPath = resolvedPath;
260
287
  } else {
288
+ const buildMode = opts.buildMode ?? "auto";
289
+ const scriptName = opts.buildScript ?? "build";
290
+ if (buildMode !== "skip") {
291
+ const declared = readBuildScript(resolvedPath, scriptName);
292
+ if (declared) {
293
+ buildAddon(resolvedPath, declared);
294
+ } else if (buildMode === "force") {
295
+ throw new Error(`Build mode "force" but no "${scriptName}" script declared in ${path2.join(resolvedPath, "package.json")}`);
296
+ } else {
297
+ console.log(`[camstack] No "${scriptName}" script \u2014 skipping build (use --build force to require one).`);
298
+ }
299
+ }
261
300
  const packed = packAddon(resolvedPath);
262
301
  tgzPath = packed.tgzPath;
263
302
  cleanup = packed.cleanup;
@@ -935,7 +974,13 @@ function buildCommands() {
935
974
  ' path Path to addon dir, .tgz, or workspace name (e.g. "tailscale")',
936
975
  ' Default: "." (current dir)',
937
976
  "",
938
- "Options:",
977
+ "Build (only when path is a directory):",
978
+ " -b, --build <mode> `auto` (default \u2014 run build if declared),",
979
+ " `force` (require + run), or `skip`",
980
+ " --no-build Equivalent to --build skip",
981
+ " --build-script <n> npm script name (default: build)",
982
+ "",
983
+ "Target options:",
939
984
  " -s, --server <url> Server URL (default: cached session from `camstack login`)",
940
985
  " -t, --token <token> Auth token override ($CAMSTACK_TOKEN, then cached scoped token)",
941
986
  " -n, --node <id> Push to a specific node. Mutually exclusive with --cluster",
@@ -1035,7 +1080,10 @@ async function runDeploy(args) {
1035
1080
  server: { type: "string", short: "s" },
1036
1081
  token: { type: "string", short: "t" },
1037
1082
  node: { type: "string", short: "n" },
1038
- cluster: { type: "boolean", short: "c" }
1083
+ cluster: { type: "boolean", short: "c" },
1084
+ build: { type: "string", short: "b" },
1085
+ "build-script": { type: "string" },
1086
+ "no-build": { type: "boolean" }
1039
1087
  }, true);
1040
1088
  if (!parsed) {
1041
1089
  console.log(commandHelp("deploy"));
@@ -1055,11 +1103,23 @@ async function runDeploy(args) {
1055
1103
  console.error("[camstack] Error: --node and --cluster are mutually exclusive.");
1056
1104
  process.exit(1);
1057
1105
  }
1106
+ const buildFlag = stringOpt(parsed.values, "build");
1107
+ const noBuild = boolOpt(parsed.values, "no-build");
1108
+ let buildMode = "auto";
1109
+ if (noBuild) buildMode = "skip";
1110
+ else if (buildFlag === "force" || buildFlag === "skip" || buildFlag === "auto") buildMode = buildFlag;
1111
+ else if (buildFlag) {
1112
+ console.error(`[camstack] Error: --build must be one of: auto, force, skip (got: ${buildFlag})`);
1113
+ process.exit(1);
1114
+ }
1115
+ const buildScript = stringOpt(parsed.values, "build-script");
1058
1116
  await deployAddon(addonPath, {
1059
1117
  serverUrl: server,
1060
1118
  token,
1061
1119
  ...nodeId ? { nodeId } : {},
1062
- ...cluster ? { cluster: true } : {}
1120
+ ...cluster ? { cluster: true } : {},
1121
+ buildMode,
1122
+ ...buildScript ? { buildScript } : {}
1063
1123
  });
1064
1124
  }
1065
1125
  function commandHelp(name) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "camstack",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "CLI tool for managing and running CamStack server",
5
5
  "keywords": [
6
6
  "camstack",