syntaxai/tdd.md · main · src / b32_sama_cli_install.ts

b32_sama_cli_install.ts 155 lines · 5081 bytes raw
// b32 — logic: builds the self-contained sama-cli install script.
// Pure: takes the in-memory file list, emits a single bash script
// with every tools/sama-cli/* file embedded as a quoted heredoc.
// The script depends on no network calls beyond the initial
// `curl https://tdd.md/install | bash` — every byte the user
// installs is in the response body.
//
// The Layer 3 /install handler reads the files from disk at startup
// and calls this function; that's the boundary. Here we just
// stitch strings together — no I/O, no clock.

const HEREDOC_MARKER = "__SAMACLI_INSTALL_EOF__";

export interface SamaCliInstallFile {
  // Path relative to the install dir (e.g. "src/a31_constants.sh").
  path: string;
  content: string;
  // If true, install script chmods +x after writing.
  executable?: boolean;
}

export interface BuildSamaCliInstallOptions {
  repoUrl?: string;
  specUrl?: string;
}

const DEFAULT_REPO_URL = "https://tdd.md/GIT/tdd.md/tree/main/tools/sama-cli";
const DEFAULT_SPEC_URL = "https://tdd.md/sama/v2";

export const buildSamaCliInstallScript = (
  files: ReadonlyArray<SamaCliInstallFile>,
  options: BuildSamaCliInstallOptions = {},
): string => {
  // Defensive: refuse to emit if any file contains the heredoc
  // marker. We control the marker and our files, but a future
  // contributor could accidentally introduce one.
  for (const f of files) {
    if (f.content.includes(HEREDOC_MARKER)) {
      throw new Error(
        `file ${f.path} contains heredoc marker \`${HEREDOC_MARKER}\` — install script would break`,
      );
    }
    if (f.path.includes("..") || f.path.startsWith("/")) {
      throw new Error(`refusing path escape: ${f.path}`);
    }
  }

  const repoUrl = options.repoUrl ?? DEFAULT_REPO_URL;
  const specUrl = options.specUrl ?? DEFAULT_SPEC_URL;

  const blocks = files.map((f) => emitFileBlock(f)).join("\n\n");

  return [
    HEADER,
    "",
    "mkdir -p \"$INSTALL_DIR\"",
    "",
    blocks,
    "",
    SYMLINK_BLOCK,
    "",
    footer(repoUrl, specUrl),
    "",
  ].join("\n");
};

const emitFileBlock = (f: SamaCliInstallFile): string => {
  const lines: string[] = [];
  lines.push(`# ── ${f.path} ──`);
  // mkdir -p only emitted for nested paths, but it's idempotent so
  // running it unconditionally is fine — keeps the emitter simple.
  if (f.path.includes("/")) {
    lines.push(`mkdir -p "$INSTALL_DIR/$(dirname "${f.path}")"`);
  }
  // Single-quoted heredoc: NO parameter expansion, NO command
  // substitution — body is preserved byte-for-byte (modulo a
  // possible trailing newline added by the heredoc terminator).
  lines.push(`cat > "$INSTALL_DIR/${f.path}" <<'${HEREDOC_MARKER}'`);
  // Ensure the closing marker lands on its own line — guarantee a
  // trailing newline on the embedded content.
  const body = f.content.endsWith("\n") ? f.content : f.content + "\n";
  lines.push(body.slice(0, -1)); // drop one trailing newline; the `lines.push` below adds one
  lines.push(HEREDOC_MARKER);
  if (f.executable) {
    lines.push(`chmod +x "$INSTALL_DIR/${f.path}"`);
  }
  return lines.join("\n");
};

const HEADER = `#!/usr/bin/env bash
# sama-cli installer — self-contained. The SAMA v2 shell verifier,
# served by https://tdd.md/install. Every file is embedded inline
# below; no second curl call, no GitHub raw URL, no package
# manager. Drops files into ~/.sama-cli/ and symlinks
# ~/.local/bin/sama. Idempotent — re-running upgrades in place.
#
# Usage:
#   curl -fsSL https://tdd.md/install | bash
#
# Environment overrides:
#   SAMA_CLI_INSTALL_DIR  default: $HOME/.sama-cli
#   SAMA_CLI_BIN_LINK     default: $HOME/.local/bin/sama
#
# Uninstall:
#   rm -rf ~/.sama-cli ~/.local/bin/sama

set -eu

INSTALL_DIR="\${SAMA_CLI_INSTALL_DIR:-$HOME/.sama-cli}"
BIN_LINK="\${SAMA_CLI_BIN_LINK:-$HOME/.local/bin/sama}"

if [ -t 1 ]; then
  C_GREEN=$'\\033[32m'
  C_CYAN=$'\\033[36m'
  C_DIM=$'\\033[2m'
  C_BOLD=$'\\033[1m'
  C_RESET=$'\\033[0m'
else
  C_GREEN="" C_CYAN="" C_DIM="" C_BOLD="" C_RESET=""
fi
say()  { printf "%s%s%s\\n" "$C_CYAN" "$1" "$C_RESET"; }
ok()   { printf "%s✓%s %s\\n" "$C_GREEN" "$C_RESET" "$1"; }
dim()  { printf "%s%s%s\\n" "$C_DIM" "$1" "$C_RESET"; }
bold() { printf "%s%s%s\\n" "$C_BOLD" "$1" "$C_RESET"; }

bold "Installing sama-cli — SAMA v2 shell verifier"
dim  "  dest: $INSTALL_DIR"
echo`;

const SYMLINK_BLOCK = `chmod +x "$INSTALL_DIR/sama"
mkdir -p "$(dirname "$BIN_LINK")"
ln -sf "$INSTALL_DIR/sama" "$BIN_LINK"
ok "installed to $INSTALL_DIR"
ok "symlinked $BIN_LINK"`;

const footer = (repoUrl: string, specUrl: string): string => `echo
bold "Done. Try:"
echo
case ":$PATH:" in
  *":$(dirname "$BIN_LINK"):"*)
    echo "  sama --help"
    echo "  sama check          # in any SAMA-conforming repo"
    ;;
  *)
    dim "  $(dirname "$BIN_LINK") is NOT on your PATH — either run:"
    echo "    $BIN_LINK --help"
    dim "  or add this to your shell rc:"
    echo "    export PATH=\\"$(dirname "$BIN_LINK"):\\$PATH\\""
    ;;
esac
echo
dim "Source:    ${repoUrl}"
dim "Spec:      ${specUrl}"
dim "Uninstall: rm -rf $INSTALL_DIR && rm -f $BIN_LINK"`;