import { afterAll, beforeAll, expect, test } from "bun:test"; import { existsSync, readdirSync } from "node:fs"; import { mkdtemp, readFile, stat, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { openBucket } from "../src/bucket/reader.ts"; import { syncAll } from "../src/bucket/sync.ts"; import { createApp } from "../src/server/app.ts"; import { openKeprDb } from "../src/server/db.ts"; import { makeRepos, writeRelease } from "./fixture.ts"; // install.sh runs for real: Kepr serves on localhost, and its downloads redirect to a stand-in S3 // server that serves the bucket folder (a folder source's file:// URLs are not followed by curl). // The script's https:// addresses are switched to http:// for the run; nothing else changes. const PLATFORM = `${process.platform === "darwin" ? "macos" : "linux"}-${process.arch === "arm64" ? "aarch64" : "x86_64"}`; const PLATFORMS = ["linux-x86_64", "linux-aarch64", "macos-aarch64", "macos-x86_64"]; const binary = (version: string, platform: string) => new TextEncoder().encode(`#!/bin/sh\necho tool ${version} ${platform}\n`); let kepr: ReturnType; let s3: ReturnType; let bucketDir: string; let script: string; beforeAll(async () => { const repos = await makeRepos(["tool"]); bucketDir = repos.bucketDir; for (const version of ["1.0.0", "1.1.0"]) { const files = PLATFORMS.map((platform) => ({ platform, name: "tool", bytes: binary(version, platform), })); await writeRelease(bucketDir, "tool", { version, save: repos.saves.tool as string, files }); } s3 = Bun.serve({ port: 0, fetch: (req) => new Response(Bun.file(join(bucketDir, new URL(req.url).pathname.slice("/b/".length)))), }); const dataDir = await mkdtemp(join(tmpdir(), "kepr-install-")); const db = openKeprDb(dataDir); await syncAll({ bucket: openBucket({ kind: "folder", path: bucketDir }), kepr: db, mirrorDir: join(dataDir, "mirror"), }); db.run("UPDATE repos SET public = 1"); kepr = Bun.serve({ port: 0, fetch: () => new Response("starting") }); const app = createApp( { domain: `127.0.0.1:${kepr.port}`, dataDir, adminPasswordHash: "$argon2id$x", bucket: { kind: "s3", endpoint: `http://127.0.0.1:${s3.port}`, name: "b", region: "fr-par", accessKeyId: "read", secretAccessKey: "only", }, }, db, ); kepr.reload({ fetch: app.fetch }); const res = await fetch(`http://127.0.0.1:${kepr.port}/tool/install.sh`); expect([res.status, res.headers.get("Content-Type")]).toEqual([200, "text/plain; charset=UTF-8"]); script = await res.text(); }); afterAll(() => { kepr.stop(); s3.stop(); }); async function install(): Promise<{ code: number; stderr: string; dir: string }> { const home = await mkdtemp(join(tmpdir(), "kepr-install-home-")); const path = join(home, "install.sh"); await writeFile(path, script.replaceAll("https://", "http://")); const dir = join(home, "bin"); const run = Bun.spawn(["sh", path], { cwd: home, env: { PATH: process.env.PATH, HOME: home, INSTALL_DIR: dir }, stderr: "pipe", stdout: "pipe", }); return { code: await run.exited, stderr: await new Response(run.stderr).text(), dir }; } test("install.sh is POSIX sh naming this repo and domain", async () => { const dir = await mkdtemp(join(tmpdir(), "kepr-install-check-")); await writeFile(join(dir, "install.sh"), script); expect(Bun.spawnSync(["sh", "-n", join(dir, "install.sh")]).exitCode).toBe(0); expect(script).toContain("repo=tool\n"); expect(script).toContain(`base=https://127.0.0.1:${kepr.port}/tool/releases\n`); }); test("installs the newest build for this machine into INSTALL_DIR, executable", async () => { const { code, stderr, dir } = await install(); expect([code, stderr]).toEqual([0, ""]); const installed = join(dir, "tool"); expect(await readFile(installed)).toEqual(Buffer.from(binary("1.1.0", PLATFORM))); expect((await stat(installed)).mode & 0o111).toBe(0o111); expect(readdirSync(dir)).toEqual(["tool"]); }); test("refuses a file that doesn't match checksums.txt, installing nothing", async () => { await writeFile(join(bucketDir, "tool/releases/1.1.0", PLATFORM, "tool"), "tampered"); const { code, stderr, dir } = await install(); expect(code).not.toBe(0); expect(stderr).toContain("checksum mismatch; nothing installed"); expect(existsSync(join(dir, "tool"))).toBe(false); expect(readdirSync(dir)).toEqual([]); }); test("a repo without releases has no installer", async () => { const res = await fetch(`http://127.0.0.1:${kepr.port}/nowhere/install.sh`); expect(res.status).toBe(404); });