/** * Where an apt repository built by reprepro files a package. * * `apt.celilo.computer` is a reprepro repository, and reprepro does not keep * packages flat in `pool/`. It files each one under * * pool////__.deb * * where `` is the first letter of the source name, or its first four * when the name starts with `lib`. Every caller in this repo once assumed * `pool/`; that URL returns 404 on the real repository, and the e2e apt * simulator served it anyway, so the suite agreed with the simulator and with * nothing real (celilo#1505). * * The rule lives here, once, for the code that stages packages into the * simulator and for the gate that holds the signal module to it. */ import { cpSync, mkdirSync } from 'node:fs'; import { basename, dirname, join } from 'node:path'; /** The control fields that decide where a package is filed. */ export interface DebControl { package: string; version: string; architecture: string; /** The `Source:` field, when it differs from `Package:`. */ source?: string; } /** * Control fields read from a `.deb` file name, `__.deb`. * * A file name cannot carry a `Source:` that differs from the package name. The * packages this repo stages (celilo, celilo-bootstrap, libsignal-jni) declare * none, so the name is enough for them. */ export function controlFromDebName(fileName: string): DebControl { const match = basename(fileName).match(/^([^_]+)_([^_]+)_([^_]+)\.deb$/); if (!match) { throw new Error(`${fileName} is not named __.deb`); } return { package: match[1], version: match[2], architecture: match[3] }; } /** The path reprepro serves a package at, relative to the repository root. */ export function repreproPoolPath(control: DebControl, component = 'main'): string { const source = control.source ?? control.package; const prefix = source.startsWith('lib') ? source.slice(0, 4) : source.slice(0, 1); // An epoch (`1:2.0`) orders versions and is not written into file names. const version = control.version.replace(/^\d+:/, ''); const file = `${control.package}_${version}_${control.architecture}.deb`; return `pool/${component}/${prefix}/${source}/${file}`; } /** * Copy a `.deb` into the simulator's staging directory where reprepro would * file it. `repoCacheDir` is the directory that holds `pool/`. * * Returns the staged path. */ export function stageDebInPool(repoCacheDir: string, debPath: string): string { const staged = join(repoCacheDir, repreproPoolPath(controlFromDebName(debPath))); mkdirSync(dirname(staged), { recursive: true }); // `force` explicit: a rebuilt .deb at the same version must replace the one // already staged, or build-infra serves stale bytes. cpSync(debPath, staged, { force: true }); return staged; }