/** * stage-apt-repo.ts — build the celilo + celilo-bootstrap .debs and stage * them for the apt-repo simulator (apt.celilo.lab), the e2e mirror of the * real apt.celilo.computer. * * Run at `cele2e build-infra` time (from stageSimulatorInputs). Produces: * * packages/e2e/.apt-repo-cache/pool/ ← celilo__.deb + * celilo-bootstrap__all.deb * * The repo INDEX (dists/.../Packages.gz, Release) is NOT built here — * dpkg-scanpackages/apt-ftparchive aren't available on macOS hosts, so * Dockerfile.apt-repo-sim builds the index inside a debian image at docker * build time. This script only produces fresh, version-matched debs and * copies them into the staging pool. * * Versions are derived from apps/celilo/package.json by the build-deb * scripts, so the celilo deb, the celilo-bootstrap deb's `Depends: celilo * (= )`, and the @celilo/cli tarball in the npm-registry-sim all agree. * * Graceful no-op when nfpm or the packaging/ dir is absent (e.g. an * npm-installed @celilo/e2e with no monorepo) — mirrors how * stageSimulatorInputs skips the website/npm staging in that case. */ import { spawnSync } from 'node:child_process'; import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs'; import { join } from 'node:path'; const green = '\x1b[32m'; const red = '\x1b[31m'; const dim = '\x1b[2m'; const yellow = '\x1b[33m'; const reset = '\x1b[0m'; /** * Build both .debs (host nfpm) and copy them into the apt-repo staging * pool. Returns false (with a warning) if prerequisites are missing — the * caller treats that as "apt-repo-sim ships empty", and the bootstrap-apt * test fails loudly with a clear message rather than the build breaking * for everyone. */ export function stageAptRepo(repoRoot: string, pkgDir: string): boolean { // Always (re)create an empty pool first so Dockerfile.apt-repo-sim's COPY // succeeds even when we can't build debs — build-infra then works for // everyone; only the bootstrap-apt test fails loudly (empty repo) if a // dev without nfpm tries to run it. const pool = join(pkgDir, '.apt-repo-cache', 'pool'); rmSync(join(pkgDir, '.apt-repo-cache'), { recursive: true, force: true }); mkdirSync(pool, { recursive: true }); const packagingDir = join(repoRoot, 'packaging'); if (!existsSync(packagingDir)) return false; // nfpm is the one host tool the .deb build needs. Without it we can't // produce debs — skip rather than fail the whole build-infra. const nfpmCheck = spawnSync('nfpm', ['--version'], { stdio: 'ignore' }); if (nfpmCheck.status !== 0) { console.log( ` ${'apt-repo (debs)'.padEnd(28)} ${yellow}skip${reset} ${dim}(nfpm not installed)${reset}`, ); return false; } process.stdout.write(` ${'apt-repo (build debs)'.padEnd(28)} `); const t0 = Date.now(); // The deb must install the EXACT version the npm-registry-sim serves locally // (= apps/celilo/package.json). Pin CELILO_VERSION so the postinst runs // `bun add @celilo/cli@` rather than `@celilo/cli@alpha` (build-deb.sh's // default): the sim has no local `alpha` dist-tag, so an `@alpha` install leaks // to the real npm uplink and pulls a stale published alpha (ISS-0153). Pinning // makes the deb version, the installed npm version, and the test expectation a // single source of truth. const version = currentCeliloVersion(repoRoot); // Reuse the canonical build-deb scripts so versioning/contents match a // real release exactly. build:deb builds both arches; build:deb:bootstrap // builds the arch-all meta-package. for (const script of ['build:deb', 'build:deb:bootstrap']) { const result = spawnSync('bun', ['run', script], { cwd: repoRoot, stdio: 'pipe', env: { ...process.env, CELILO_VERSION: version }, }); if (result.status !== 0) { console.log(`${red}✗${reset}`); console.error(result.stderr?.toString()); console.error(result.stdout?.toString()); process.exit(1); } } // Stage every freshly-built .deb into the pool. dist/ may also hold older // versions; copy only the current ones so the repo doesn't advertise // stale celilo versions the npm-registry-sim no longer serves. const distDir = join(repoRoot, 'dist'); const debs = readdirSync(distDir).filter((f) => f.endsWith('.deb') && f.includes(version)); for (const deb of debs) { // `force` explicit: a rebuilt .deb at the same version must replace the // one already pooled, or build-infra serves stale bytes. cpSync(join(distDir, deb), join(pool, deb), { force: true }); } console.log( `${green}✔${reset} ${dim}${Math.round((Date.now() - t0) / 1000)}s, ${debs.length} deb(s) @ ${version}${reset}`, ); return debs.length > 0; } /** Read apps/celilo/package.json version — the version all debs are built at. */ function currentCeliloVersion(repoRoot: string): string { const pkgJsonPath = join(repoRoot, 'apps', 'celilo', 'package.json'); const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8')); return pkg.version as string; }