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 tarballDir: join(dataDir, "tarballs"),49 });50 db.run("UPDATE repos SET public = 1");51 kepr = Bun.serve({ port: 0, fetch: () => new Response("starting") });52 const app = createApp(53 {54 domain: `127.0.0.1:${kepr.port}`,55 dataDir,56 adminPasswordHash: "$argon2id$x",57 bucket: {58 kind: "s3",59 endpoint: `http://127.0.0.1:${s3.port}`,60 name: "b",61 region: "fr-par",62 accessKeyId: "read",63 secretAccessKey: "only",64 },65 },66 db,67 );68 kepr.reload({ fetch: app.fetch });69 const res = await fetch(`http://127.0.0.1:${kepr.port}/tool/install.sh`);70 expect([res.status, res.headers.get("Content-Type")]).toEqual([200, "text/plain; charset=UTF-8"]);71 script = await res.text();72});7374afterAll(() => {75 kepr.stop();76 s3.stop();77});7879async function install(): Promise<{ code: number; stderr: string; dir: string }> {80 const home = await mkdtemp(join(tmpdir(), "kepr-install-home-"));81 const path = join(home, "install.sh");82 await writeFile(path, script.replaceAll("https://", "http://"));83 const dir = join(home, "bin");84 const run = Bun.spawn(["sh", path], {85 cwd: home,86 env: { PATH: process.env.PATH, HOME: home, INSTALL_DIR: dir },87 stderr: "pipe",88 stdout: "pipe",89 });90 return { code: await run.exited, stderr: await new Response(run.stderr).text(), dir };91}9293test("install.sh is POSIX sh naming this repo and domain", async () => {94 const dir = await mkdtemp(join(tmpdir(), "kepr-install-check-"));95 await writeFile(join(dir, "install.sh"), script);96 expect(Bun.spawnSync(["sh", "-n", join(dir, "install.sh")]).exitCode).toBe(0);97 expect(script).toContain("repo=tool\n");98 expect(script).toContain(`base=https://127.0.0.1:${kepr.port}/tool/releases\n`);99});100101test("installs the newest build for this machine into INSTALL_DIR, executable", async () => {102 const { code, stderr, dir } = await install();103 expect([code, stderr]).toEqual([0, ""]);104 const installed = join(dir, "tool");105 expect(await readFile(installed)).toEqual(Buffer.from(binary("1.1.0", PLATFORM)));106 expect((await stat(installed)).mode & 0o111).toBe(0o111);107 expect(readdirSync(dir)).toEqual(["tool"]);108});109110test("refuses a file that doesn't match checksums.txt, installing nothing", async () => {111 await writeFile(join(bucketDir, "tool/releases/1.1.0", PLATFORM, "tool"), "tampered");112 const { code, stderr, dir } = await install();113 expect(code).not.toBe(0);114 expect(stderr).toContain("checksum mismatch; nothing installed");115 expect(existsSync(join(dir, "tool"))).toBe(false);116 expect(readdirSync(dir)).toEqual([]);117});118119test("a repo without releases has no installer", async () => {120 const res = await fetch(`http://127.0.0.1:${kepr.port}/nowhere/install.sh`);121 expect(res.status).toBe(404);122});