58 lines
3.2 KiB
TypeScript
58 lines
3.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFile } from "node:fs/promises";
|
|
import { execFile } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
import path from "node:path";
|
|
import type { SelfTestCase } from "../harness.js";
|
|
|
|
const requiredRunnerPackages = Object.freeze(["git", "openssh-client", "ripgrep"]);
|
|
const execFileAsync = promisify(execFile);
|
|
|
|
const selfTest: SelfTestCase = async (context) => {
|
|
const containerfile = await readFile(path.join(context.root, "deploy/container/Containerfile"), "utf8");
|
|
const apkPackages = installedApkPackages(containerfile);
|
|
const tran = await readFile(path.join(context.root, "tools/tran"), "utf8");
|
|
const trans = await readFile(path.join(context.root, "tools/trans"), "utf8");
|
|
const applyPatch = await readFile(path.join(context.root, "tools/apply_patch"), "utf8");
|
|
|
|
for (const packageName of requiredRunnerPackages) {
|
|
assert.equal(apkPackages.has(packageName), true, `runner image must install ${packageName}`);
|
|
}
|
|
|
|
assert.equal(tran.startsWith("#!/usr/bin/env bun\n"), true, "tools/tran must be a shebang executable discovered by gitbundle tools");
|
|
assert.equal(trans.startsWith("#!/bin/sh\n"), true, "tools/trans must be a shebang executable discovered by gitbundle tools");
|
|
assert.equal(applyPatch.startsWith("#!/bin/sh\n"), true, "tools/apply_patch must be a shebang helper copied with runner tools");
|
|
assert.equal(tran.includes("UNIDESK_SSH_CLIENT_TOKEN"), true, "tools/tran must require the scoped UniDesk SSH client token");
|
|
assert.equal(tran.includes("/ws/ssh"), true, "tools/tran must use the frontend SSH WebSocket path");
|
|
assert.equal(tran.includes("apply-patch < patch.diff"), true, "tools/tran help must advertise runner-side apply-patch");
|
|
|
|
const help = await execFileAsync(path.join(context.root, "tools/tran"), ["--help"], { cwd: context.root, timeout: 10_000 });
|
|
const parsed = JSON.parse(help.stdout) as { ok?: boolean; supported?: string[]; unsupported?: string[]; valuesPrinted?: boolean };
|
|
assert.equal(parsed.ok, true);
|
|
assert.equal(parsed.valuesPrinted, false);
|
|
assert.equal(parsed.supported?.some((line) => line.includes("script")), true);
|
|
assert.equal(parsed.supported?.some((line) => line.includes("apply-patch")), true);
|
|
assert.equal(parsed.unsupported?.includes("apply-patch"), false);
|
|
const patchHelp = await execFileAsync(path.join(context.root, "tools/apply_patch"), ["--help"], { cwd: context.root, timeout: 10_000 });
|
|
assert.equal(patchHelp.stdout.includes("reads *** Begin Patch format"), true);
|
|
|
|
return { name: "90-runner-image-tools", tests: ["runner image installs required CLI tools", "gitbundle tran tools are executable and documented", "runner apply-patch helper is bundled"] };
|
|
};
|
|
|
|
function installedApkPackages(containerfile: string): Set<string> {
|
|
const packages = new Set<string>();
|
|
const normalized = containerfile.replace(/\\\s*\r?\n\s*/gu, " ");
|
|
|
|
for (const match of normalized.matchAll(/\bapk\s+add\s+--no-cache\s+([^\n]+)/gu)) {
|
|
const rawPackages = match[1] ?? "";
|
|
for (const item of rawPackages.split(/\s+/u)) {
|
|
const packageName = item.trim();
|
|
if (packageName) packages.add(packageName);
|
|
}
|
|
}
|
|
|
|
return packages;
|
|
}
|
|
|
|
export default selfTest;
|