The location embedded in the log record was styled with crossterm: a
remote consumer of pkh's log records would receive ANSI codes inside
the message text. Plain text is the logger's business to style.
The chlog target-series resolution becomes changelog::series_candidates
(UNRELEASED pinning, development-series default and fallbacks modeled
by SeriesCandidates), PPA references get package_info::split_ppa
(shared by pull and deb, now also rejecting empty parts), and SSH
endpoints get context::ContextConfig::from_endpoint — so a library
consumer can resolve series, validate PPAs and build context
configurations without reimplementing the CLI's rules. All three carry
unit tests; main.rs keeps only parsing of flags and error handling.
CDNs occasionally answer 200 with a zero-byte body under load; the
checksum verification then reported the empty-string hash as a mismatch,
and the by-hash retry (subject to the same glitch) silently lost its own
failure reason. Treat empty bodies as transient in both fetch paths and
append the by-hash failure to the final VerifyError.
Includes a regression test serving an empty 200 followed by a valid body
on a local socket.
Busy mirrors and CDNs routinely break bulk fetches: pooled keep-alive
connections get closed remotely ('error sending request'), downloads are
cut short (surfacing as bogus checksum mismatches), and index generations
momentarily drift from the Release file fetched moments before.
- shared client: short idle-pool timeout and TCP keepalive, and a
bounded-retry GET helper now used for index, Release, keyring and
Launchpad fetches (previously reqwest::get, which has no timeouts)
- downloads: retry the whole download, and check the content length so
truncation is reported as such instead of a checksum mismatch
- sources index: on a checksum mismatch against the Release file, retry
pinned to the exact listed generation via Debian's by-hash mechanism;
body-read errors are retried and reported per component instead of
aborting the whole lookup
- distro_info: malformed CSV rows are skipped with a warning, dates
that fail to parse become None, and all plain HTTP requests go
through a shared reqwest client with connect/total timeouts
- package_info: the Sources stanza iterator is iterative (a crafted
index with many blank stanzas overflowed the stack), stanzas missing
a Version are skipped, and failed series/pocket probes are summarized
in the final 'not found' error instead of being silently dropped
- pull: no double unwrap on the remote-derived artifact filename, an
empty series list is an error, and streaming downloads get a
per-request timeout
- deb/cross: dpkg-architecture output parsing skips unexpected lines
and its exit status is checked, as is dpkg --add-architecture
- changelog: version increments parse as u64 with checked arithmetic
(1.0-20250123123456 used to panic on the u32 parse)
The Sources index was downloaded with no authentication: per-artifact
checksums were verified, but against hashes taken from an index a MITM
could substitute along with the artifacts. Fetch each suite's InRelease
(or Release + Release.gpg), verify the signature with gpgv against the
archive keyring (or the PPA signing key) the same way apt does, and
checksum-check every Sources index against it before parsing.
Distro archives and PPAs verify strictly: an invalid or unverifiable
signature, or a missing gpgv binary, is a hard error. Flat repositories
keep working without a Release file or without a verifiable one (warned
as unauthenticated), but tampering evidence is a hard error there too.
Also switches all archive, PPA and keyring base URLs to https, and
reads suite components from the verified Release instead of fetching
them separately over an unauthenticated channel.
Add a --repository flag taking the full suite URL of an external flat
repository (e.g. https://pkg.noctalia.dev/deb/resolute/), i.e. one with
no dists/ hierarchy, like apt's exact-path suites ('Suites: resolute/').
The suite name is read from the root Release file (Codename/Suite), the
sources index is fetched from the repository root as Sources.xz/gz/plain,
and package files are resolved against the URL root, ignoring the stanza
Directory field like apt does. As with PPAs, the stanza Vcs-Git is never
used for external repositories, so the source always comes from the
repository itself.
Also make the sources index parser detect compression by magic bytes
(gz/xz/plain) instead of assuming gzip, and fix extraction of archives
with './'-prefixed entries, which previously aborted and are now
extracted in place instead of being relocated.
The Ubuntu development series (stonking) Sources.gz no longer ships a
Checksums-Sha256 field, only Checksums-Sha512 and the legacy Files
(MD5) field. The package_info parser only read Checksums-Sha256, so
the file list ended up empty and fetch_orig_tarball panicked on
Option::unwrap() when looking for the orig tarball.
- Add a ChecksumAlgo enum (Md5/Sha256/Sha512) to FileEntry, replacing
the hardcoded sha256 field, and parse the strongest available
checksum field (Sha256 > Sha512 > MD5).
- Make download_file_checksum verify against the correct algorithm
instead of always using SHA-256.
- Replace the unwrap() on the orig tarball search with a proper error
listing the available files, so future regressions fail clearly
instead of panicking.
- Add md-5 dependency for MD5 verification.
Multiple changes:
- New contexts (schroot, unshare)
- Cross-building quirks, with ephemeral contexts and repositories management
- Contexts with parents, global context manager, better lifetime handling
- Local building of binary packages
- Pull: pulling dsc files by default
- Many small bugfixes and changes
Co-authored-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>
Co-committed-by: Valentin Haudiquet <valentin.haudiquet@canonical.com>