1import { afterAll, beforeAll, expect, test } from "bun:test";2import { existsSync, readdirSync } from "node:fs";3import { mkdtemp, readFile, stat, writeFile } from "node:fs/promises";4import { tmpdir } from "node:os";5import { join } from "node:path";6import { openBucket } from "../src/bucket/reader.ts";7import { syncAll } from "../src/bucket/sync.ts";8import { createApp } from "../src/server/app.ts";9import { openKeprDb } from "../src/server/db.ts";10import { makeRepos, writeRelease } from "./fixture.ts";111213141516const PLATFORM = `${process.platform === "darwin" ? "macos" : "linux"}-${process.arch === "arm64" ? "aarch64" : "x86_64"}`;17const PLATFORMS = ["linux-x86_64", "linux-aarch64", "macos-aarch64", "macos-x86_64"];18const binary = (version: string, platform: string) =>19 new TextEncoder().encode(`#!/bin/sh\necho tool ${version} ${platform}\n`);2021let kepr: ReturnType<typeof Bun.serve>;22let s3: ReturnType<typeof Bun.serve>;23let bucketDir: string;24let script: string;2526beforeAll(async () => {27 const repos = await makeRepos(["tool"]);28 bucketDir = repos.bucketDir;29 for (const version of ["1.0.0", "1.1.0"]) {30 const files = PLATFORMS.map((platform) => ({31 platform,32 name: "tool",33 bytes: binary(version, platform),34 }));35 await writeRelease(bucketDir, "tool", { version, save: repos.saves.tool as string, files });36 }37 s3 = Bun.serve({38 port: 0,39 fetch: (req) =>40 new Response(Bun.file(join(bucketDir, new URL(req.url).pathname.slice("/b/".length)))),41 });42 const dataDir = await mkdtemp(join(tmpdir(), "kepr-install-"));43 const db = openKeprDb(dataDir);44 await syncAll({45 bucket: openBucket({ kind: "folder", path: bucketDir }),46 kepr: db,47 mirrorDir: join(dataDir, "mirror"),48 });49 db.run("UPDATE repos SET public = 1");50 kepr = Bun.serve({ port: 0, fetch: () => new Response("starting") });51 const app = createApp(52 {53 domain: `127.0.0.1:${kepr.port}`,54 dataDir,55 adminPasswordHash: "$argon2id$x",56 bucket: {57 kind: "s3",58 endpoint: `http://127.0.0.1:${s3.port}`,59 name: "b",60 region: "fr-par",61 accessKeyId: "read",62 secretAccessKey: "only",63 },64 },65 db,66 );67 kepr.reload({ fetch: app.fetch });68 const res = await fetch(`http://127.0.0.1:${kepr.port}/tool/install.sh`);69 expect([res.status, res.headers.get("Content-Type")]).toEqual([200, "text/plain; charset=UTF-8"]);70 script = await res.text();71});7273afterAll(() => {74 kepr.stop();75 s3.stop();76});7778async function install(): Promise<{ code: number; stderr: string; dir: string }> {79 const home = await mkdtemp(join(tmpdir(), "kepr-install-home-"));80 const path = join(home, "install.sh");81 await writeFile(path, script.replaceAll("https://", "http://"));82 const dir = join(home, "bin");83 const run = Bun.spawn(["sh", path], {84 cwd: home,85 env: { PATH: process.env.PATH, HOME: home, INSTALL_DIR: dir },86 stderr: "pipe",87 stdout: "pipe",88 });89 return { code: await run.exited, stderr: await new Response(run.stderr).text(), dir };90}9192test("install.sh is POSIX sh naming this repo and domain", async () => {93 const dir = await mkdtemp(join(tmpdir(), "kepr-install-check-"));94 await writeFile(join(dir, "install.sh"), script);95 expect(Bun.spawnSync(["sh", "-n", join(dir, "install.sh")]).exitCode).toBe(0);96 expect(script).toContain("repo=tool\n");97 expect(script).toContain(`base=https://127.0.0.1:${kepr.port}/tool/releases\n`);98});99100test("installs the newest build for this machine into INSTALL_DIR, executable", async () => {101 const { code, stderr, dir } = await install();102 expect([code, stderr]).toEqual([0, ""]);103 const installed = join(dir, "tool");104 expect(await readFile(installed)).toEqual(Buffer.from(binary("1.1.0", PLATFORM)));105 expect((await stat(installed)).mode & 0o111).toBe(0o111);106 expect(readdirSync(dir)).toEqual(["tool"]);107});108109test("refuses a file that doesn't match checksums.txt, installing nothing", async () => {110 await writeFile(join(bucketDir, "tool/releases/1.1.0", PLATFORM, "tool"), "tampered");111 const { code, stderr, dir } = await install();112 expect(code).not.toBe(0);113 expect(stderr).toContain("checksum mismatch; nothing installed");114 expect(existsSync(join(dir, "tool"))).toBe(false);115 expect(readdirSync(dir)).toEqual([]);116});117118test("a repo without releases has no installer", async () => {119 const res = await fetch(`http://127.0.0.1:${kepr.port}/nowhere/install.sh`);120 expect(res.status).toBe(404);121});