Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Publishing to an archive

The pool a run leaves behind is a complete archive for one suite and one architecture, and serving it directly is the shortest path from a build to apt install. A published archive is usually a different shape: one Release covering every architecture of a suite, managed by an archive tool with snapshots and a signing key behind it.

src2deb export bridges the two. It copies what a work directory holds into a directory laid out for an archive tool to ingest, so a publisher never reads anything under the work directory.

An export carries .deb files and metadata, never an index, so the pool’s own Release — its date and the identity the recipe gave it — does not travel with them. The archive tool writes its own, and the two should agree: give the recipe the same origin and label the publisher declares, so a package reaches a client under one identity whether it came from the pool directly or through the archive.

src2deb export recipes/cosmic-epoch --work /mnt/build/work --to /srv/drop/rk1

What it writes

/srv/drop/rk1/trixie/
├── export.toml
├── manifests/
│   └── cosmic-epoch/
│       ├── amd64.toml
│       └── arm64.toml
├── cosmic-comp_1.0.0+deb13.20260802.abc1234_amd64.buildinfo
├── cosmic-comp_1.0.0+deb13.20260802.abc1234_amd64.changes
├── cosmic-comp_1.0.0+deb13.20260802.abc1234_amd64.deb
├── cosmic-comp_1.0.0+deb13.20260802.abc1234_arm64.deb
└── cosmic-icons_1.0.0+deb13.20260802.abc1234_all.deb

The suite is a directory of its own, so one destination holds several suites. Inside it the packages are flat, so the whole suite is one argument:

aptly repo add cosmic-trixie /srv/drop/rk1/trixie

An archive tool that scans a directory takes the packages and passes over the rest. Keep the directory to what src2deb wrote, though: aptly, for one, fails a whole repo add over a single file named *.deb that it cannot parse, whoever put it there.

Beside each package travel the .changes and .buildinfo its build wrote, and a copy of each architecture’s provenance manifest. Together they are the record of how the packages were built, in a place a publisher can archive next to a release without reaching into a build host’s work directory.

src2deb writes the export and stops there. It does not run an archive tool, sign anything, or upload.

What an export carries

Every component the work directory records as built, for every architecture it holds a manifest for — not only what the last run produced. A --skip-published run may build two components of twenty-six while the archive still wants all twenty-six, and the manifest carries a built record forward for exactly this reason.

To narrow to particular architectures, name them; the flag is repeatable:

src2deb export recipes/cosmic-epoch --to /srv/drop/rk1 --architecture arm64

A component the manifest calls built whose packages are no longer in the output tree fails the export, naming the component. An archive quietly missing a package it was told about is worse than an export that stops.

Architecture: all packages

An arch-indep package’s file name carries no architecture, and its stamped version does not vary with one, so a recipe built for two architectures produces one file name over two sets of bytes. A merged archive holds one of them, so an export carries one of them:

src2deb: cosmic-icons: Architecture: all package taken from arm64, not from amd64
src2deb: set arch-indep-owner in the recipe to build those once rather than once
per architecture

Which copy is carried follows the recipe. With an arch-indep owner declared, the owner’s copy is carried — and the other architecture never builds it in the first place, so there is nothing to choose between. With none, the later version is carried, and architecture name order breaks a tie, so an export is a function of what the work directory holds rather than of the order it was read in.

The .changes and .buildinfo of a build whose arch-indep output was dropped still travel, and still name it: they record what that build produced, which is what they are kept for. Declaring an owner removes the divergence at its source.

Exporting again

An export replaces the one before it. export.toml names every file the export wrote, so the next export into the same directory removes exactly those and writes its own — a scheduled run stays idempotent, and a superseded version never reaches the archive by being left behind.

Three rules make that safe to point at a shared drop directory:

  • An export removes only files an export of its own wrote. Anything else in the directory is left alone.
  • The index is keyed by recipe. Several recipes may export into one directory, and each replaces only its own files — which is the ordinary case for an archive publishing more than one recipe into a suite.
  • The index names every file the directory holds for the recipe, at every moment. It is written before the files it names, and it goes on naming the files it is superseding until they have actually been removed. An export killed partway through therefore leaves nothing unaccounted for, and the next one finishes what it started.

An export into a directory whose export.toml names a different suite is refused, since that means the destination is not the one intended.

Keeping an export beyond the next one is a matter of choosing where it goes: --to /srv/drop/rk1-$(date +%F) writes a fresh directory each time. It is worth knowing what that buys before reaching for it — the pool upstream keeps as many versions as --keep is told to, and an archive tool downstream keeps snapshots, so the export itself is usually the one copy not worth archiving.

A scheduled build and publish

The whole cycle, as a build host runs it overnight:

#!/bin/sh
set -eu
src2deb build recipes/cosmic-epoch --work /mnt/build/work \
    --suite trixie --skip-published --keep 2
src2deb check recipes/cosmic-epoch --work /mnt/build/work --suite trixie
src2deb export recipes/cosmic-epoch --work /mnt/build/work \
    --suite trixie --to /srv/drop/rk1

--skip-published builds only what moved, --keep 2 bounds the pool, the check stops a publish whose packages would not install, and the export replaces the drop directory’s contents with the archive’s current state. What the publisher then does with /srv/drop/rk1/trixie — ingest, snapshot, sign, publish — is outside src2deb.

The check earns its place under set -eu: a build validates that every component builds, and a suite that drops a package overnight makes what built yesterday uninstallable today without failing a single build. See Checking installability.