makeBuild

tcmts.cli.builds.makeBuild · #u7ewdhpc · term · runs on the server

uses node:fs

tcm builds make <entry>: a build of the codebase's tcm (its package whose command is tcm) as it was at a history entry, kept with the others in root (builds/<id>) to run as they are. It's packed from the codebase as it was then, by this tcm, and installed beside a clone of the known-good install's dependencies when they're the same (seconds), or by npm. One kept already is that one.

import { buildBin, KEEP_BUILDS, keptBuilds, pruneKeptBuilds } from "#codebase/tcmts/cli/builds";
import type { TcmBuild } from "#codebase/tcmts/cli/builds";
import type { Codebase } from "#codebase/tcmts/cli/codebase";
import { causalArg } from "#codebase/tcmts/cli/main";
import { packagesIn } from "#codebase/tcmts/cli/manifests";
import { packPackage, rowsAt } from "#codebase/tcmts/cli/releases";
import { UsageError } from "#codebase/tcmts/cli/ui";
import { shortHash } from "#codebase/tcmts/core/hash";
import { spawnSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";

export function makeBuild(
  cb: Codebase,
  root: string,
  entry: string,
): { build: TcmBuild; made: boolean } {
  const head = causalArg(cb, entry);
  const kept = keptBuilds(root).find((b) => b.head === head);
  if (kept) return { build: kept, made: false };
  const pkg = packagesIn(cb, rowsAt(cb, head)).find((p) => p.manifest.bin.tcm);
  if (!pkg)
    throw new UsageError(
      `no package of the codebase had a command called tcm at ${shortHash(head)}: there's no tcm to build`,
    );
  const work = fs.mkdtempSync(path.join(root, ".making-"));
  try {
    const out = path.join(work, "package");
    packPackage(cb, pkg.name, undefined, out, head);
    const dependencies = (dir: string) =>
      JSON.stringify(
        (
          JSON.parse(fs.readFileSync(path.join(dir, "package.json"), "utf8")) as {
            dependencies?: unknown;
          }
        ).dependencies ?? {},
      );
    const build = path.join(work, "build");
    const good = path.join(root, "known-good");
    const goodBin = buildBin(good);
    const goodPackage = goodBin && path.dirname(path.dirname(goodBin));
    if (goodPackage && dependencies(goodPackage) === dependencies(out)) {
      // the known-good's dependencies, at the same versions: a clone of its install, with this package in it
      fs.cpSync(good, build, { recursive: true, mode: fs.constants.COPYFILE_FICLONE });
      for (const f of fs.readdirSync(build))
        if (/^(VERIFIED|verified\.json|build\.json)$|\.tgz$/.test(f))
          fs.rmSync(path.join(build, f));
      fs.rmSync(path.join(build, path.relative(good, goodPackage)), {
        recursive: true,
        force: true,
      });
    } else {
      fs.mkdirSync(build, { recursive: true });
      if (dependencies(out) !== "{}") {
        fs.writeFileSync(
          path.join(build, "package.json"),
          JSON.stringify({ private: true, dependencies: JSON.parse(dependencies(out)) }, null, 2),
        );
        const npm = spawnSync("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], {
          cwd: build,
          encoding: "utf8",
        });
        if (npm.status !== 0)
          throw new Error(
            `installing ${pkg.name}'s dependencies failed:\n${npm.stdout}${npm.stderr}`,
          );
      }
    }
    const installed = path.join(build, "node_modules", pkg.name);
    fs.mkdirSync(path.dirname(installed), { recursive: true });
    fs.cpSync(out, installed, { recursive: true });
    fs.writeFileSync(
      path.join(build, "build.json"),
      `${JSON.stringify(
        {
          head,
          when: new Date().toISOString().slice(0, 16).replace("T", " "),
          package: pkg.name,
        },
        null,
        2,
      )}\n`,
    );
    const dir = path.join(root, "builds", head.slice(0, 8));
    fs.rmSync(dir, { recursive: true, force: true });
    fs.mkdirSync(path.dirname(dir), { recursive: true });
    fs.renameSync(build, dir);
    pruneKeptBuilds(root, KEEP_BUILDS);
    const made = keptBuilds(root).find((b) => b.dir === dir);
    if (!made) throw new Error(`the build made in ${dir} has no bin/tcm.js`);
    return { build: made, made: true };
  } finally {
    fs.rmSync(work, { recursive: true, force: true });
  }
}

uses

used by