runRegistry

tcmts.cli.run.runRegistry · #5zs6hebn · term · runs on the server

uses node:path

Runs tcm registry, without opening a codebase: the registry tcm ships (registry), copied into --dir (again when tcm's changes) and run there by tcm start, its jobs with it, until it's stopped.

import { routeRegistry } from "#codebase/tcmts/cli/commands";
import { execute } from "#codebase/tcmts/cli/main";
import { SHIPPED_APP } from "#codebase/tcmts/cli/pack";
import { registryEnv } from "#codebase/tcmts/cli/run";
import type { Started } from "#codebase/tcmts/cli/run";
import { shippedApp } from "#codebase/tcmts/cli/sync";
import { UsageError } from "#codebase/tcmts/cli/ui";
import { parse } from "@bomb.sh/router";
import { spawn } from "node:child_process";
import { createHash } from "node:crypto";
import fs from "node:fs";
import path_1 from "node:path";

export async function runRegistry({ cmd, rest, cwd }: Started): Promise<number> {
  const m = execute(cmd, parse(routeRegistry(), { argv: rest })).model;
  const shipped = shippedApp("registry");
  if (!shipped)
    throw new UsageError(
      "this tcm doesn't ship the registry: a tcm installed from its package does",
    );
  const base = m.base?.replace(/\/+$/, "");
  if (base !== undefined && !/^\/[^\s?#]+$/.test(base))
    throw new UsageError(`--base ${m.base}: the path it's served under, as /registry`);
  const dir = path_1.resolve(cwd, m.dir);
  const from = path_1.join(shipped, "codebase.sqlite");
  const to = path_1.join(dir, ".tcm", "codebase.sqlite");
  const digest = createHash("sha256").update(fs.readFileSync(from)).digest("hex");
  const mark = path_1.join(dir, ".tcm", "shipped");
  if (fs.existsSync(path_1.join(dir, ".tcm")) && !fs.existsSync(mark))
    throw new UsageError(`${dir} holds a codebase that isn't a registry: give another --dir`);
  if (!fs.existsSync(to) || fs.readFileSync(mark, "utf8") !== digest) {
    fs.mkdirSync(path_1.dirname(to), { recursive: true });
    for (const f of [to, `${to}-wal`, `${to}-shm`]) fs.rmSync(f, { force: true });
    fs.copyFileSync(from, to);
    fs.writeFileSync(mark, digest);
  }
  // (it stops when this does, however this ends)
  const env = registryEnv(process.env);
  const child = spawn(
    process.execPath,
    // (and its HTTP routes, under /api: its OAuth client metadata, for visitors signing in on a domain, and search;
    // all of it under the base, if it has one)
    [
      process.argv[1]!,
      "start",
      SHIPPED_APP,
      "--port",
      String(m.port),
      "--host",
      m.host,
      "--http",
      "http=/api",
      ...(base ? ["--base", base] : []),
    ],
    { cwd: dir, env, stdio: "inherit" },
  );
  const stop = () => child.kill();
  process.once("SIGINT", stop);
  process.once("SIGTERM", stop);
  return new Promise((resolve) => child.on("exit", (code) => resolve(code ?? 0)));
}

uses

used by