catacomb/docs/PACKAGING.md
Luna d4bed019b2
perf: runtime performance pass, phase 1 (DB pragmas, batched scan writes)
Executes docs/superpowers/specs/2026-07-07-runtime-performance-pass-design.md:

- database.rs: every pooled connection now runs WAL + synchronous=NORMAL
  + busy_timeout=5000 + foreign_keys=ON via with_init (in-memory pools
  get the latter two). Fixes the SQLITE_BUSY failure mode under the
  parallel scanner and makes FK cascades work on every connection, not
  just whichever one last ran the old one-shot pragma in delete_folder.
- database.rs: prepare_cached for the per-video info_cache queries; new
  info_cache_put_many bulk upsert (single transaction) replaces the
  per-row autocommitted info_cache_put, with a round-trip unit test.
- library.rs: enrich_with_cache collects cache misses and flushes them
  once per channel; title sort uses sort_by_cached_key.
- app.rs: Title/ChannelAsc sorts use sort_by_cached_key instead of
  allocating two lowercased strings per comparison.
- Cargo.toml: panic=abort in release (crash-hook still fires; Cargo
  forces unwind for test harnesses). PACKAGING.md documents the local
  RUSTFLAGS target-cpu=native opt-in; repo stays portable.
- .gitignore: catacomb.db-wal / catacomb.db-shm sidecars.

Verified: 146 tests pass (integration suite exercises the pragmas
end-to-end), x86_64-pc-windows-gnu check green, live smoke shows
journal_mode=wal + batched cache writes landing correct rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 01:22:40 -07:00

5.4 KiB

Packaging Catacomb

This document covers building distributable packages. The one-liner:

scripts/package.sh all     # .deb + .rpm + .AppImage into dist/
scripts/package.sh deb     # just the .deb
scripts/package.sh rpm     # just the .rpm
scripts/package.sh appimage

The script builds the release binary once and reuses it for every format. Output lands in dist/ (gitignored).

Building for your own machine? Distributed packages must stay portable, so the repo never sets target-cpu. If you compile locally and only run the binary on the build host, you can opt into host-specific SIMD (helps the ffmpeg-adjacent hot paths like the perceptual-dedup hashing):

RUSTFLAGS="-C target-cpu=native" cargo build --release

Don't ship that binary — it will SIGILL on any older CPU.

Linux formats

.deb (Debian / Ubuntu / Mint)

Built by cargo-deb, driven by [package.metadata.deb] in Cargo.toml. The script installs cargo-deb on demand. Runtime deps declared: yt-dlp, ffmpeg, mpv, xdg-utils, libxcb1, libc6 (and libnotify4 recommended).

scripts/package.sh deb
sudo apt install ./dist/catacomb_*_amd64.deb

.rpm (Fedora / RHEL / openSUSE)

Built by cargo-generate-rpm, driven by [package.metadata.generate-rpm]. It packages the already-built (and release-profile-stripped) binary.

scripts/package.sh rpm
sudo dnf install ./dist/catacomb-*.x86_64.rpm

Note: ffmpeg on Fedora lives in RPM Fusion; the dependency is declared but the user may need that repo enabled.

AppImage (any Linux)

A hand-rolled AppDir + appimagetool (downloaded to dist/tools/ on first run). The image bundles the GUI binary and its shared-library closure only — yt-dlp/ffmpeg/mpv are still expected on the host PATH, same as the package deps. Bundling them would balloon the image and tangle the licensing.

scripts/package.sh appimage
chmod +x dist/catacomb-*-x86_64.AppImage
./dist/catacomb-*-x86_64.AppImage

Arch Linux

Use the PKGBUILD in the repo root (not this script). It builds from a fresh git clone, so run it from a clean directory:

mkdir build && cd build
cp /path/to/repo/PKGBUILD .
makepkg -si

For repeated builds after pushing new commits, always pass -C (cleanbuild) so makepkg re-checks out the latest source instead of reusing a stale cached clone.

Windows (.zip)

Windows cross-compiles cleanly from Linux — the formerly-blocking Linux-only deps are target-gated in Cargo.toml (ksni and rfd's xdg-portal backend are cfg(target_os = "linux"); tray::start is a no-op off Linux; rfd falls back to its native Win32 backend).

rustup target add x86_64-pc-windows-gnu
sudo apt install mingw-w64 zip          # or your distro's equivalent
scripts/package.sh win

This produces dist/catacomb-<ver>-x86_64-windows.zip containing catacomb.exe, LICENSE.txt, and a README.txt listing the runtime PATH deps (yt-dlp / ffmpeg / mpv). Release builds link as a GUI-subsystem app (no console window); the binary reattaches to the launching terminal at runtime (attach_windows_console in main.rs) so catacomb.exe --web 8080 from PowerShell prints its logs, while a double-click stays windowless.

scripts/package.sh all includes the Windows zip automatically when the target + mingw + zip are all present, and silently skips it otherwise.

macOS (.app .zip)

macOS cross-compiles via osxcross, which needs Apple's macOS SDK (extracted from Xcode — its license does not permit redistribution, which is why this build is local-only and not in CI). The crypto stack is ring, which builds against the osxcross SDK without trouble.

One-time setup: build osxcross with a macOS SDK and put its target/bin on your PATH (this provides the oa64-clang / o64-clang wrapper compilers). Then:

rustup target add aarch64-apple-darwin   # or x86_64-apple-darwin
MAC_ARCH=arm64 scripts/package.sh mac     # arm64 (default) | x86_64

This cross-compiles, assembles an unsigned catacomb.app (Info.plist + the Mach-O binary; an .icns icon too if png2icns is available), and zips it to dist/catacomb-<ver>-<arch>-macos.zip. The .app is not codesigned or notarized, so on the target Mac it must be opened the first time via right-click → Open, or cleared with xattr -dr com.apple.quarantine catacomb.app. Runtime deps (yt-dlp / ffmpeg / mpv) are expected on PATH as on the other platforms.

A .dmg and codesigning/notarization (and a tray-icon-based macOS tray) are follow-ups; the tray is currently a no-op off Linux. A MacPorts port (a Portfile building from source, like the Arch PKGBUILD) is a possible future native-distribution path — unscheduled.

CI

.forgejo/workflows/release.yml runs scripts/package.sh all on every pushed tag (v*) and attaches the resulting artifacts to the Codeberg release. The Linux container also installs mingw-w64 + zip + the x86_64-pc-windows-gnu target, so the Windows zip is built in CI alongside the .deb/.rpm/.AppImage. The macOS zip is not in CI (the SDK can't be hosted in a public image) — build it locally per the section above, or add a GitHub Actions macos-latest job which has Xcode and can also codesign/notarize.