@fluid-app/fluid-cli-mist 0.1.48 → 0.1.49

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/index.mjs CHANGED
@@ -1005,6 +1005,64 @@ function readMistFileFrom(dir = process.cwd()) {
1005
1005
  return readMistFile(dir);
1006
1006
  }
1007
1007
  //#endregion
1008
+ //#region src/lib/staging-revision.ts
1009
+ /**
1010
+ * Pick the commit `fluid mist staging request` stages when no `--revision`
1011
+ * is given: the local checkout's HEAD, but only when nothing local would be
1012
+ * left out or lost.
1013
+ *
1014
+ * Staging can only serve a commit the mist's repository already has, and
1015
+ * this never pushes or switches branches to get one there; publishing to
1016
+ * main would ship the work to production. So the checkout must be clean
1017
+ * and its HEAD must already be on the remote's main branch.
1018
+ */
1019
+ /** The local checkout can't be staged as it is. Nothing was changed. */
1020
+ var StagingPreflightError = class extends Error {
1021
+ constructor(message) {
1022
+ super(`${message} Nothing was requested.`);
1023
+ this.name = "StagingPreflightError";
1024
+ }
1025
+ };
1026
+ /**
1027
+ * The full SHA of the checkout's HEAD once it's clean and on the remote's
1028
+ * main branch. `remoteUrl` may carry a short-lived token; it only reaches
1029
+ * git through `gitFetch`, never argv or config.
1030
+ *
1031
+ * Raises StagingPreflightError when the directory is not a git checkout,
1032
+ * git can't report its status, it has uncommitted or untracked files, or
1033
+ * HEAD is not on the remote's main.
1034
+ */
1035
+ async function resolveLocalStagingRevision(cwd, remoteUrl) {
1036
+ if (await git(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") throw new StagingPreflightError("This directory isn't a git checkout of the mist. Run it from the mist's folder or pass --revision.");
1037
+ const status = await git(cwd, [
1038
+ "status",
1039
+ "--porcelain",
1040
+ "--untracked-files=all"
1041
+ ]);
1042
+ if (status === null) throw new StagingPreflightError("Git couldn't report whether this checkout has uncommitted changes. Pass --revision to stage a specific commit.");
1043
+ if (status.length > 0) throw new StagingPreflightError("This checkout has uncommitted or untracked changes, and staging only serves commits already on the mist's repository. Commit and publish them, or pass --revision.");
1044
+ const head = await git(cwd, ["rev-parse", "HEAD"]);
1045
+ if (!head || !/^[0-9a-f]{40}$/.test(head)) throw new StagingPreflightError("This checkout has no commits yet.");
1046
+ await gitFetch(cwd, remoteUrl, "main");
1047
+ if ((await execa("git", [
1048
+ "merge-base",
1049
+ "--is-ancestor",
1050
+ head,
1051
+ "FETCH_HEAD"
1052
+ ], {
1053
+ cwd,
1054
+ reject: false
1055
+ })).exitCode !== 0) throw new StagingPreflightError(`Commit ${head.slice(0, 7)} isn't on the mist's main branch yet, and staging only serves commits the repository already has.`);
1056
+ return head;
1057
+ }
1058
+ async function git(cwd, args) {
1059
+ const run = await execa("git", args, {
1060
+ cwd,
1061
+ reject: false
1062
+ });
1063
+ return run.exitCode === 0 ? run.stdout.trim() : null;
1064
+ }
1065
+ //#endregion
1008
1066
  //#region src/lib/staging.ts
1009
1067
  /** A staging step the user can act on, as opposed to a server fault. */
1010
1068
  var StagingUnavailableError = class extends Error {
@@ -1158,7 +1216,7 @@ function handle(err) {
1158
1216
  console.error(chalk.red(err.message));
1159
1217
  process.exit(1);
1160
1218
  }
1161
- if (err instanceof StagingUnavailableError || err instanceof StagingWaitTimeoutError) {
1219
+ if (err instanceof StagingPreflightError || err instanceof StagingUnavailableError || err instanceof StagingWaitTimeoutError) {
1162
1220
  console.error(chalk.yellow(err.message));
1163
1221
  process.exit(1);
1164
1222
  }
@@ -4193,13 +4251,15 @@ const stagingWaitCommand = new Command("wait").description("Follow a staging ope
4193
4251
  //#endregion
4194
4252
  //#region src/commands/staging/request.ts
4195
4253
  /** `fluid mist staging request` — request staging at a revision. */
4196
- const stagingRequestCommand = new Command("request").description("Request the staging environment at a pushed commit").argument("[slug]", "The mist slug (defaults to .mist file in CWD)").requiredOption("--revision <sha>", "Full 40-character SHA of a commit already on the mist's repository").option("--operation-key <key>", "Resume an interrupted request with the key it printed").option("--wait", "Follow the operation until staging is ready").option("--timeout <minutes>", "How long --wait follows it (default 30)").option("--json", "Emit the operation as JSON").action((slug, opts) => runAction(async () => {
4254
+ const stagingRequestCommand = new Command("request").description("Request the staging environment at a pushed commit").argument("[slug]", "The mist slug (defaults to .mist file in CWD)").option("--revision <sha>", "Full commit SHA to stage (defaults to this checkout's HEAD when it is clean and on main)").option("--operation-key <key>", "Resume an interrupted request with the key and --revision it printed").option("--wait", "Follow the operation until staging is ready").option("--timeout <minutes>", "How long --wait follows it (default 30)").option("--json", "Emit the operation as JSON").action((slug, opts) => runAction(async () => {
4255
+ if (opts.operationKey && !opts.revision) throw new Error("--operation-key needs the --revision it was first sent with. The interrupted request printed both.");
4197
4256
  const resolved = resolveSlug(slug);
4198
4257
  const client = createMistClient();
4258
+ const sourceRevision = opts.revision ?? await localRevision(client, resolved);
4199
4259
  let operation = await admitStaging(client, resolved, {
4200
- sourceRevision: opts.revision,
4260
+ sourceRevision,
4201
4261
  operationKey: opts.operationKey,
4202
- onOperationKey: (key) => process.stderr.write(chalk.dim(`Operation key ${key}. If this is interrupted, rerun with --operation-key ${key}.\n`))
4262
+ onOperationKey: (key) => process.stderr.write(chalk.dim(`Operation key ${key}. If this is interrupted, rerun with --operation-key ${key} --revision ${sourceRevision}.\n`))
4203
4263
  });
4204
4264
  process.stderr.write(`Staging operation ${operation.id}: ${describeProgress(operation)}\n`);
4205
4265
  if (opts.wait) operation = await waitForOperation(client, resolved, operation.id, waitOptions(opts.timeout));
@@ -4207,6 +4267,13 @@ const stagingRequestCommand = new Command("request").description("Request the st
4207
4267
  if (operation.state === "succeeded") console.log(`✔ Staging is ready. Run \`fluid mist staging status\` for its URL.`);
4208
4268
  else console.log(`Requested. Follow it with \`fluid mist staging wait --operation ${operation.id}\`.`);
4209
4269
  }));
4270
+ /** This checkout's HEAD, once it is clean and already on the remote's main. */
4271
+ async function localRevision(client, slug) {
4272
+ const { git_credential: credential } = await createGitCredential(client, slug);
4273
+ const revision = await resolveLocalStagingRevision(process.cwd(), credential.remote_url);
4274
+ process.stderr.write(chalk.dim(`Staging this checkout's HEAD, ${revision.slice(0, 7)}.\n`));
4275
+ return revision;
4276
+ }
4210
4277
  //#endregion
4211
4278
  //#region src/commands/staging/index.ts
4212
4279
  /** `fluid mist staging` — sub-command aggregator. */