runRun

tcmts.cli.run.runRun · #tole3xyw · term · runs on the server

uses node:path

Runs tcm run.

import { routeRun } from "#codebase/tcmts/cli/commands";
import { execute, words } from "#codebase/tcmts/cli/main";
import { packagesIn } from "#codebase/tcmts/cli/manifests";
import type { Context } from "#codebase/tcmts/cli/run";
import { dim } from "#codebase/tcmts/cli/style";
import { commandsIn } from "#codebase/tcmts/cli/suites";
import { runOn } from "#codebase/tcmts/cli/sync";
import { chooseName, UsageError } from "#codebase/tcmts/cli/ui";
import { run } from "#codebase/tcmts/loader";
import { parse } from "@bomb.sh/router";
import path_1 from "node:path";

export async function runRun({ cb, cmd, rest }: Context): Promise<number> {
  const r = execute(cmd, parse(routeRun(), { argv: rest }));
  // a command of the codebase's packages, by its name: what its bin runs, with every argument
  const bin = r.model.bin;
  const target = bin
    ? packagesIn(cb, cb.store.names()).flatMap((p) =>
        p.manifest.bin[bin]
          ? [`${p.namespace ? `${p.namespace}.` : ""}${p.manifest.bin[bin]}`]
          : [],
      )[0]
    : undefined;
  if (bin && !target) throw new UsageError(`no package here has a command called ${bin}`);
  const name =
    target ??
    r.model.name ??
    (await chooseName(cb, "run <name> [args...] [--on <remote>]", "Run what?", "value"));
  // (with --bin, a word before `--` is the command's first argument, not a definition's name)
  const args = [
    ...(target && r.model.name !== undefined ? [r.model.name] : []),
    ...r.model.args,
    ...words(r.literals),
  ];
  const h = cb.lookupOne(name, "value");
  if (cb.store.getDef(h)!.kind === "type") throw new UsageError(`${name} is a type`);
  if (r.model.on !== undefined) return runOn(cb, r.model.on, h, args);
  // what it starts can run the codebase's commands, as <CMD>_BIN (a script running tcm, say). One the caller set is
  // kept, and said (an end-to-end test run against another tcm); one a tcm run further out set (TCM_COMMANDS
  // records them) is the codebase's build as it was then, so it's replaced, as is one of its runners (a `tcm run`
  // further out made it: a tcm from before TCM_COMMANDS doesn't record it)
  const before = JSON.parse(process.env.TCM_COMMANDS || "{}") as Record<string, string>;
  const set: Record<string, string> = {};
  const runnerHere = (file: string) =>
    path_1.dirname(path_1.resolve(file)) === path_1.resolve(cb.lay.cacheDir) &&
    /^run_[a-z2-7]+\.ts$/.test(path_1.basename(file));
  for (const [variable, runner] of Object.entries(commandsIn(cb))) {
    const given = process.env[variable];
    if (given && given !== before[variable] && !runnerHere(given))
      console.error(dim(`${variable} is set: ${given}, not this codebase's build`));
    else process.env[variable] = set[variable] = runner;
  }
  process.env.TCM_COMMANDS = JSON.stringify({ ...before, ...set });
  return run(h, args, cb.source(), cb.lay);
}

uses

used by