@groundfloorcloud/cli 0.2.0 → 0.2.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +72 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -180,7 +180,7 @@ wrong identity for testing Coderunner Vault/Files as the workload.
180
180
  | `--app-id <uuid>` | Bind a **new** coderunner to an App as a helper. Ignored on reuse. |
181
181
  | `--secret <NAME>` | Inject a workspace secret (repeatable). Only works where the platform has injection enabled; prefer runtime reads via M2M `secrets:read`. |
182
182
  | `--api-url <url>` | Control Plane base URL override. |
183
- | `-y, --yes` | Skip the reuse confirmation (required without a terminal). |
183
+ | `-y, --yes` | Skip the reuse confirmation (required without a terminal) and the prompt shown with `-c` when the deploy would drop previously deployed env variables (key names only; values are not stored). |
184
184
  | `-p, --path <dir>` | Directory to package (default: cwd). |
185
185
  | `--git <url>` / `--ref <ref>` | Clone and deploy a remote repo. |
186
186
  | `--subdir <dir>` | Package a subdirectory of the source. |
package/dist/index.js CHANGED
@@ -752,6 +752,26 @@ async function getDeployment(apiUrl, workspaceId, coderunnerId) {
752
752
  }
753
753
  return await res.json();
754
754
  }
755
+ async function getDeploymentById(apiUrl, workspaceId, coderunnerId, deploymentId) {
756
+ const token = await getValidAccessToken();
757
+ const res = await fetch(
758
+ `${apiUrl}/v1/workspaces/${encodeURIComponent(
759
+ workspaceId
760
+ )}/coderunners/${encodeURIComponent(
761
+ coderunnerId
762
+ )}/deployments/${encodeURIComponent(deploymentId)}`,
763
+ {
764
+ headers: {
765
+ Authorization: `Bearer ${token}`,
766
+ Accept: "application/json"
767
+ }
768
+ }
769
+ );
770
+ if (!res.ok) {
771
+ throw new CpError(res.status, await parseError(res));
772
+ }
773
+ return await res.json();
774
+ }
755
775
  function deleteCoderunnerDeployment(apiUrl, workspaceId, coderunnerId, deploymentId) {
756
776
  return cpDelete(
757
777
  apiUrl,
@@ -2041,9 +2061,12 @@ function priorEnvKeys(coderunner2) {
2041
2061
  const keys = manifest?.coderunner?.deploy?.env_keys;
2042
2062
  return Array.isArray(keys) ? keys.map(String) : [];
2043
2063
  }
2044
- function envRedeployWarning(priorKeys, sentKeys) {
2064
+ function envKeysNotResent(priorKeys, sentKeys) {
2045
2065
  const sent = new Set(sentKeys);
2046
- const missing = priorKeys.filter((k) => !sent.has(k)).sort();
2066
+ return priorKeys.filter((k) => !sent.has(k)).sort();
2067
+ }
2068
+ function envRedeployWarning(priorKeys, sentKeys) {
2069
+ const missing = envKeysNotResent(priorKeys, sentKeys);
2047
2070
  const base = 'Environment variable VALUES are not stored by Groundfloor: every deploy must resend them with -e KEY=VALUE or groundfloor.json "env".';
2048
2071
  if (priorKeys.length > 0) {
2049
2072
  if (missing.length === 0) return [];
@@ -2076,6 +2099,7 @@ var VERSION_POLL_INTERVAL_MS = 2e3;
2076
2099
  var VERSION_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2077
2100
  var DEPLOY_POLL_INTERVAL_MS = 2e3;
2078
2101
  var DEPLOY_POLL_TIMEOUT_MS = 5 * 60 * 1e3;
2102
+ var DEPLOY_TERMINAL_FAILURE = /* @__PURE__ */ new Set([4, 5, 6]);
2079
2103
  function slugify(input) {
2080
2104
  const slug = input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2081
2105
  return slug || "app";
@@ -2235,19 +2259,30 @@ async function deployCommand(opts) {
2235
2259
  const injectSecretKeys = (opts.secret ?? []).map((key) => key.trim()).filter(Boolean);
2236
2260
  let coderunnerId = opts.coderunner;
2237
2261
  if (coderunnerId && !opts.skipDeploy) {
2262
+ let known;
2238
2263
  try {
2239
- const known = (await listCoderunners(cfg.apiUrl, ws)).find(
2264
+ known = (await listCoderunners(cfg.apiUrl, ws)).find(
2240
2265
  (c) => c.id === coderunnerId
2241
2266
  );
2242
- if (known) {
2243
- for (const line of envRedeployWarning(
2244
- priorEnvKeys(known),
2245
- Object.keys(envVariable)
2246
- )) {
2247
- log(line);
2267
+ } catch {
2268
+ }
2269
+ if (known) {
2270
+ const prior = priorEnvKeys(known);
2271
+ const sentKeys = Object.keys(envVariable);
2272
+ const warning = envRedeployWarning(prior, sentKeys);
2273
+ if (envKeysNotResent(prior, sentKeys).length > 0) {
2274
+ if (io.isTTY && !opts.yes) {
2275
+ await confirmAction({
2276
+ summary: warning,
2277
+ question: "Deploy anyway and clear these variables?",
2278
+ io
2279
+ });
2280
+ } else {
2281
+ for (const line of warning) log(line);
2248
2282
  }
2283
+ } else {
2284
+ for (const line of warning) log(line);
2249
2285
  }
2250
- } catch {
2251
2286
  }
2252
2287
  }
2253
2288
  if (opts.appId && coderunnerId) {
@@ -2400,24 +2435,43 @@ async function deployCommand(opts) {
2400
2435
  throw err;
2401
2436
  }
2402
2437
  log("Waiting for deployment...");
2438
+ const newDeploymentId = result.deployment_id || void 0;
2439
+ const readNewDeployment = async () => {
2440
+ if (newDeploymentId) {
2441
+ try {
2442
+ return await getDeploymentById(
2443
+ cfg.apiUrl,
2444
+ ws,
2445
+ coderunnerId,
2446
+ newDeploymentId
2447
+ );
2448
+ } catch (err) {
2449
+ if (err instanceof CpError && err.status === 404) return null;
2450
+ throw err;
2451
+ }
2452
+ }
2453
+ const current = await getDeployment(cfg.apiUrl, ws, coderunnerId);
2454
+ return current?.version_id === uploaded.version_id ? current : null;
2455
+ };
2403
2456
  const deployDeadline = Date.now() + DEPLOY_POLL_TIMEOUT_MS;
2404
2457
  let deployOk = false;
2405
2458
  while (Date.now() < deployDeadline) {
2406
- const deployment = await getDeployment(cfg.apiUrl, ws, coderunnerId);
2459
+ const deployment = await readNewDeployment();
2407
2460
  const dStatus = deployment?.status ?? 0;
2408
2461
  if (dStatus === 3) {
2409
2462
  deployOk = true;
2410
2463
  break;
2411
2464
  }
2412
- if (dStatus === 4) {
2465
+ if (DEPLOY_TERMINAL_FAILURE.has(dStatus)) {
2466
+ process.stderr.write("\n");
2413
2467
  return await failDeploy({
2414
2468
  apiUrl: cfg.apiUrl,
2415
2469
  workspaceId: ws,
2416
- headline: `Deployment failed for coderunner ${coderunnerId} (${statusLabel(4)}).`,
2470
+ headline: `Deployment failed for coderunner ${coderunnerId} (${statusLabel(dStatus)}).`,
2417
2471
  coderunnerId,
2418
2472
  versionId: uploaded.version_id,
2419
- deploymentId: result.deployment_id ?? deployment?.id,
2420
- status: 4
2473
+ deploymentId: newDeploymentId ?? deployment?.id,
2474
+ status: dStatus
2421
2475
  });
2422
2476
  }
2423
2477
  process.stderr.write(".");
@@ -2426,9 +2480,9 @@ async function deployCommand(opts) {
2426
2480
  process.stderr.write("\n");
2427
2481
  if (!deployOk) {
2428
2482
  let lastStatus = null;
2429
- let deploymentId = result.deployment_id;
2483
+ let deploymentId = newDeploymentId;
2430
2484
  try {
2431
- const latest = await getDeployment(cfg.apiUrl, ws, coderunnerId);
2485
+ const latest = await readNewDeployment();
2432
2486
  lastStatus = latest?.status ?? null;
2433
2487
  deploymentId = latest?.id ?? deploymentId;
2434
2488
  } catch {
@@ -4164,7 +4218,7 @@ program.command("deploy").description(
4164
4218
  []
4165
4219
  ).option(
4166
4220
  "-y, --yes",
4167
- "Confirm updating an existing same-named coderunner (required with no interactive terminal). Does not bypass the saved-default-workspace check: pass -w"
4221
+ "Confirm updating an existing same-named coderunner (required with no interactive terminal), and skip the prompt about previously deployed env variables that this deploy does not resend (-c). Does not bypass the saved-default-workspace check: pass -w"
4168
4222
  ).option("-m, --message <text>", "Version description").option("--skip-deploy", "Upload a new version but do not deploy it").action((opts) => run(() => deployCommand(opts)));
4169
4223
  program.command("usage").description(
4170
4224
  "Show account quota/usage and the billing estimate (read-only). The account is the billing parent of the workspace, not the workspace itself"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@groundfloorcloud/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Groundfloor gf CLI — sign in, deploy coderunners, publish Shell apps, and manage secrets, files, and Dataplane.",
5
5
  "type": "module",
6
6
  "bin": {