Commit Graph
62 Commits
Author SHA1 Message Date
vhaudiquet 7790b9452d test: add --mode vm, the autopkgtest escape hatch
VM virtualization is the one backend pkh does not reimplement: the qemu
runner carries the serial-console protocol, image overlay management
and reboot support, and it is the only one satisfying
isolation-machine/needs-reboot tests. pkh execs the installed
autopkgtest with its qemu runner (--image required, --cpus/--ram-size
passthrough) in its own process group so Ctrl+C reaches the testbed
cleanup, parses the --summary file back into the pkh report model and
maps autopkgtest's exit classes onto the 0/1/2 contract (12/14/16/20
are runtime errors; the raw code rides the JSON report). Pockets, PPAs
and injection are refused in vm mode for now: they would need the
keyring setup inside the image.
2026-09-27 21:33:11 +02:00
vhaudiquet 2a9287d942 test,cli: wire pkh test into the CLI
Argument wiring only, per the report ports: the live view carries
phases and the outcome, the per-test report renders after the run
(text or --json) and decides the exit code; runtime errors exit 2.
--list-tests prints the parsed debian/tests/control and exits without
booting a testbed.
2026-09-27 21:04:03 +02:00
vhaudiquet 1b71a6814c cli: add deb --resume/--keep and the deb list subcommand
Wiring for the session feature: --resume optionally takes a session
id (bare --resume adopts the newest session of the tree), --keep
keeps the session after a successful build, and 'pkh deb list'
renders the read-only session table for the current tree. Listing
is a subcommand, not a mode-switching flag: pkh deb with flags
always starts a build.
2026-09-26 11:51:55 +02:00
vhaudiquet 5a1c1672cd cli: intercept Ctrl+C for deb, build and put
The SIGINT handler only records the interruption and wakes a watchdog
through a self-pipe (async-signal-safe); the watchdog runs the whole
shutdown in thread context — the live view's reporter first, then the
notice and the log hint, then the cleanup hooks, then exit 130. Flows
park in wait_for_shutdown instead of racing it with their own exit,
and an end-to-end test drives the sequence by re-spawning the test
binary and raising SIGINT at itself.
2026-09-22 10:44:05 +02:00
vhaudiquet 02e1f739c3 chlog,cli,docs: offer suite-aliased series in the chlog selector
The chlog series selector only appeared when the changelog's current
distribution resolved to a known series; a Debian package targeting
'unstable' (or 'stable', 'testing', ...) fell through to keeping the
current series, silently, with no menu.

Resolve the changelog distribution through the suite aliases first
(unstable identifies the same series as sid, which resolves to the
Debian series list). The selector offers an aliased series as
'<suite> (<series>)' — 'unstable (sid)' — preselected, but selects
the suite name: what a changelog distribution field expects, instead
of the codename. Every other label and free-typed input selects
itself, unchanged.

Reflect the selector in the README roadmap checklist.
2026-09-21 11:52:39 +02:00
vhaudiquet fa121f08ec cli: expose --version on the root command
The flag was disabled in the initial commit, leaving the binary with
no way to report its number — wrong for a release. clap scopes the
automatic version flag to the root command (-V/--version), so the
per-subcommand -v target-version options of pull and chlog are
unaffected.
2026-09-21 01:36:39 +02:00
vhaudiquet b99f945b98 cli: remove the pkh context subcommand
The context management interface never worked reliably, and keeping
it exposed presents a feature that is not ready. Contexts remain in
the library as the execution backend for pkh deb (unshare chroots
and friends); only the CLI surface goes.
2026-09-21 00:10:45 +02:00
vhaudiquet a0e74073bf lint: add pkh lint, wrapping lintian for parity plus pkh-native checks
CI / build (push) Successful in 2m55s
CI / test (push) Skipped
CI / snap (push) Successful in 4m32s
pkh covered the package lifecycle but never validated the packaging
itself: broken control stanzas, unparsable changelog versions or
uncommitted debian/ edits only surfaced at build or upload time. pkh
lint lints a source tree with day-one lintian parity plus a native Rust
engine for the checks lintian cannot have.

The wrapper reuses the pkh build output next to the tree when it matches
the current changelog entry and no tree content is newer (mtime walk,
skipping .git/.pc), else packs fresh with dpkg-source -b using weak gzip
compression (the artifact is ephemeral; xz dominated the run at 9.8 s
versus 2.7 s on a 111 MB tree) and symlinks quilt orig tarballs from the
tree's parent, which dpkg-source searches in cwd. Findings are parsed
from the installed lintian into a unified report, deduplicated by tag
name against the native engine, and rendered lintian-shaped
(<L>: <pkg> <type>: <tag> <details>) as text or JSON, colorized at
render time (--color auto/always/never). Exit codes follow lintian's
contract (0 clean, 1 findings at/above --fail-on, 2 runtime error);
lintian's own exit code is ignored because it uses 2 both for findings
and for runtime errors. -d/--dist maps to lintian --profile so the
target distro's rules apply even on a foreign host.

The native engine hosts the first workflow check lintian cannot know:
pkh-debian-changes-not-committed flags debian/ content that is not
committed to git, since the pkh flow builds and uploads the tree as-is.
Checks register in a static registry validated by a unit test, and the
wrapper's parser is pinned by golden tests captured from lintian 2.129
output. Strategies for lintian's Ubuntu blind spots (its vendor data
there is one file plus 14 disabled tags) are specced in
plans/pkh-lint.md, deliberately not implemented yet.
2026-09-19 23:45:31 +02:00
vhaudiquet 4f5246ccd3 chlog: number Ubuntu backports with the per-release SRU scheme
CI / build (push) Successful in 2m56s
CI / test (push) Skipped
CI / snap (push) Successful in 4m35s
--backport was Debian-only: ~bpo is backports.debian.org's scheme and
its number the Debian release, so Ubuntu targets were rejected outright.
Ubuntu backports have their own documented scheme (Ubuntu version-
strings): the development release's version with a per-release ~YY.MM.1
appended, sorting before it (3.1-1ubuntu2 backported to 22.04 becomes
3.1-1ubuntu2~22.04.1; native 3.1 becomes 3.1~22.04.1) and independent of
the version the target release carries. The .N increments for
subsequent per-release SRU uploads.

backport_series_number becomes backport_suffix_for_series: the release
number comes from the new generic get_series_release_number (version
column of the target series' own distro-info data, leading token kept —
"12" for bookworm, "26.04" out of resolute's "26.04 LTS"; empty column
as on sid/experimental means None), and the suffix is picked per vendor:
~bpoNN+ for Debian (plain integer releases only), ~YY.MM. for Ubuntu.
Unnumbered series still error before anything is written.

Also serialize the changelog tests that mutate the process-global
DEBFULLNAME/DEBEMAIL variables behind a tokio Mutex: run in parallel
they raced each other's identity reads, which started failing
intermittently as generate_entry tests accumulated.
2026-09-19 21:32:02 +02:00
vhaudiquet 012df20961 chlog: number entries as backport, NMU or no-change rebuild
The version flags were never reachable: --backport was declared but not
read, and compute_new_version's NMU/rebuild numbering sat behind a TODO
asking for CLI wiring (the old positional-bool signature always received
false). generate_entry now takes an EntryKind selected by three mutually
exclusive flags, and is async because the backport numbering derives the
Debian release number of the target series from distro-info:

- --backport: 1.0-1 becomes 1.0-1~bpo12+1 (12 = release number of the
  target series, backport suite names accepted too). Re-running on an
  already-numbered version bumps the counter; series without a numeric
  Debian release (sid, Ubuntu series, UNRELEASED) are rejected before
  anything is written.
- --nmu: 1.0-1 becomes 1.0-1.1 (native 1.0 becomes 1.0+nmu1).
- --rebuild: 1.0-1 becomes 1.0-1build1.

An explicit --version overrides all three. compute_new_version went
from four positional bools to a private Bump enum; backport numbering
reuses increment_suffix with a '~bpoNN+' suffix. The CLI prints the
computed new version before opening the editor.
2026-09-19 20:56:03 +02:00
vhaudiquet 6caedce61a report: reproduce the pre-refactor CLI output through the ports
Instead of carrying raw UI in core, the ports now represent everything
the CLI used to do inline:

- Prompter::present shows context outside of a question (the wizard
  summary screen, the vendoring notice spacing); TerminalPrompter
  prints it on stdout exactly like the println!s it replaces, server
  embeds forward it as a display event.
- generate_entry returns the generated entry (package, versions,
  series, path) instead of printing; the CLI renders the same lines.
- BuildTarget carries a flow-composed display line and a tee_log flag:
  the terminal adapter renders it verbatim ("Building source package
  ...", "Building ... for series/arch", "Uploading ... to ...") and
  uploads open no build log.
- The unmet build-dependency diagnostics are rendered by the CLI from
  the typed error, in the original order (details, then summary).
- --verbose constructs no live view at all (an idle widget used to
  linger), and the re-vendor offer only logs when it is actually
  asked, so headless runs print the error exactly once.
2026-09-19 00:52:31 +02:00
vhaudiquet c2dae4f3f9 lift main.rs business logic into the library
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.
2026-09-18 20:51:36 +02:00
vhaudiquet 54cb04ba27 put: report through the view and ask the host-key question through the Prompter
put() loses its MultiProgress parameter: the summary, pre-flight and
connection spinners become view messages, the per-file SFTP transfer
reports determinate progress through view.progress (upload_file takes
a byte-count callback instead of an indicatif bar), and the display is
released through view.suspend on every exit path. The hardcoded
trust-on-first-use prompt in the SSH host-key verification becomes the
Prompter::accept_host_key port (fail-closed by default; the terminal
prompter prints the authenticity banner and confirms), so a remote
frontend can surface its own host-key dialog.
2026-09-18 20:42:17 +02:00
vhaudiquet bb76e41908 new: drive the wizard and verification offers through the Prompter port
The interactive half of pkh new no longer touches the terminal prompt
module directly: run() takes a Prompter, picks the wizard or the plain
resolve path through interactive(), and every select/text/confirm
question (including the verification offers) goes through the port.
Cancellations propagate as Err, preserving Ctrl+C-aborts; the summary
and vendoring-notice prints become log lines. A builder-server embed
can now drive the whole scaffold wizard over its own wire format by
implementing Prompter.
2026-09-18 20:38:05 +02:00
vhaudiquet d64e472845 report: grow the Prompter port and move display_path out of the ui module
Prompter gains interactive(), select() and text() (with the Validator
type), and confirm() now propagates cancellation as Err so flows abort
instead of silently taking a default when the user hits Ctrl+C. The
terminal prompter implements the full port; the port also re-exports
the path display helper, which is pure presentation formatting used by
events and messages rather than terminal code.
2026-09-18 20:34:33 +02:00
vhaudiquet 052c02cdc3 deb: drive build_binary_package through the BuildView port
The binary build joins the source build on the reporting ports:
build_binary_package takes a DebBuildOptions struct (replacing eleven
positional arguments), reports target, phases, progress and the
outcome through the environment-agnostic BuildView, and the Phase enum
with its default classifiers moves from the terminal widget into the
deb module (announced through the enter_phase helper). DebUi loses its
inherent event methods and only implements the port; tee logging and
the SIGINT behavior are unchanged.

No behavior change for the CLI; headless consumers pass report::Quiet.
2026-09-18 20:22:45 +02:00
vhaudiquet 27b1083b15 report: add BuildView/Prompter ports and drive pkh build through them
Core flows no longer reach into the terminal UI: build_source_package
takes a BuildSourceOptions struct (source tree, domain options, view,
prompter) and reports phases, messages and outcomes through the
environment-agnostic ports in the new report module. The classifiers
move from ui/logfmt to the core logfmt module, DebUi becomes a
BuildView adapter, the re-vendor retry asks the prompter instead of
checking for a TTY, and artifact/success printing moves to the CLI.

Headless consumers pass report::Quiet; an embedding (e.g. a builder
server forwarding events to a web frontend) implements BuildView and
maps the plain-data events onto its own wire format.
2026-09-18 20:02:56 +02:00
vhaudiquet 4ab41e691a build: only redistribute the orig tarball on new upstream (-si)
dpkg-genchanges includes the upstream tarballs in the .changes only when
the upload brings a new upstream: no previous changelog entry, a changed
upstream version or a renamed source. On a plain revision bump the
tarball already sits in the archive, and dpkg strips it (and its .asc)
from the distribution set.

pkh's native source pipeline listed every .dsc-referenced tarball
unconditionally, making every upload re-ship the orig. Implement the
dpkg source styles as --orig auto|always|never (auto being the -si
default; always/never are -sa/-sd), stripping the tarballs out of the
changes, buildinfo-free checksum set and artifact list like dpkg, with
the explicit 'never' ignored for native packages. Comparison uses the
epoch-less upstream version, exactly like dpkg's version().

Differential tests against real dpkg cover revision bumps, new upstream
versions and both forced styles.
2026-09-17 23:45:15 +02:00
vhaudiquet 77420e723a new: add upstream-aware orig tarball origins and the orig-vendor component 2026-09-17 01:31:35 +02:00
vhaudiquet 84824f61c6 new: surface cargo vendor failures and pin the vendoring toolchain 2026-09-16 23:24:43 +02:00
vhaudiquet f27d27ea99 new: add pkh put, a native dput replacement for PPA uploads
Upload built source packages over SFTP with host-key verification
(Launchpad fingerprints pinned in host_keys.yml, ask-to-accept
otherwise), Launchpad account discovery (git config lp.user), and
pre-flight checks the upload queue itself never does: changes file
discovery/validation, PPA existence via the Launchpad API, target
series validity, and debian/control Section validity (sections
bundled in distro_info.yml). Upload log prevents duplicate uploads
unless --force.
2026-09-16 21:38:53 +02:00
vhaudiquet 9b98f5c7c3 new: add interactive wizard and remaining ecosystem templates 2026-09-16 13:49:29 +02:00
vhaudiquet d044f757e9 new: scaffold new Debian source packages (non-interactive core) 2026-09-16 12:14:09 +02:00
vhaudiquet 9c3394750d distro_info: accept case-insensitive dist in effective_series, fix UNRELEASED fallbacks 2026-09-16 12:12:15 +02:00
vhaudiquet ae420989f9 distro_info: add UNRELEASED series handling, use it in deb and chlog 2026-09-16 11:15:09 +02:00
vhaudiquet f6fed7328b pull: reject malformed --ppa values instead of silently using the archive
A --ppa value that was not exactly 'user/name' (full URL, extra
segment, empty halves) made base_url None and pulled the package from
the main archive without any warning. Error out naming the expected
format instead, and document the format in --help.
2026-09-16 01:24:02 +02:00
vhaudiquet 5500f98586 pull: add --repository to pull from external flat repositories
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.
2026-09-15 10:57:06 +02:00
vhaudiquet 182a06ffbe deb: add -j/--jobs to control parallel build jobs
By default the number of parallel jobs is detected with nproc inside
the build context. Add a -j/--jobs option so an explicit count can be
honored instead, threading it through build_binary_package and
local::build into DEB_BUILD_OPTIONS=parallel=N.
2026-09-10 15:27:50 +02:00
vhaudiquet c9b48d4573 build: live view for source builds, pin LC_ALL for the pipeline
Route 'pkh build' through the DebUi capture machinery 'pkh deb'
already uses instead of letting dpkg-source inherit the terminal:

- pin LANG=C and LC_ALL=C so dpkg-source emits deterministic English
  diagnostics regardless of the session locale;
- new DpkgSourceClassifier rewrites info:/warning:/error: lines into
  colored pane entries, telling benign tar warnings from failures;
- DebUi generalizes for reuse (arbitrary phase labels, build-specific
  log naming); run_source_build() drives phases and pipes subprocess
  output through the sink when a UI is present;
- glyph-free house-style summaries: 'Built in Ns:' plus artifact
  paths relative to cwd; failures print captured errors + log path;
- drop/capitalize pipeline chatter, add 'pkh build --verbose' to
  bypass the view like 'pkh deb --verbose'.
2026-08-24 11:58:19 +02:00
vhaudiquet dfaab0606a debian/deps: native dependency grammar and build-dep checking
Replace dpkg-checkbuilddeps with a native implementation:

- full dependency grammar: comma clauses, | alternatives, << <= = >= >>
  relations, :arch qualifiers (any/native/specific), [arch lists] and
  <profile restriction> formulas per alternative;
- restriction reduction against active build profiles and the host arch
  at parse time (reduce_restrictions semantics);
- evaluation against a parsed dpkg status database with Multi-Arch
  semantics (foreign/allowed) and versioned Provides rules (unversioned
  provides never satisfy versioned deps; versioned ones must satisfy the
  relation);
- clause simplification with implication-based deduplication, rendering
  dpkg-compatible 'unmet build dependencies/conflicts' diagnostics.

check_build_depends() consumes debian/control + CheckOpts (-A/-B/-I
equivalents). run_source_build performs the check when forced (-D
parity); source-only builds skip it entirely like dpkg-buildpackage,
and unsatisfied deps propagate as UnmetBuildDependencies -> exit 3.

Unit tests port the Dpkg_Deps.t reduction matrices; differential gate
runs 24 scenarios (alternatives, versions, arch/profile restrictions,
Multi-Arch, Provides, conflicts, -A/-B flags) against real
dpkg-checkbuilddeps comparing exit status and diagnostics.
2026-08-24 11:58:06 +02:00
vhaudiquet e5adf600c3 deb: change ui/ux of pkh deb
CI / build (push) Successful in 2m54s
CI / test (push) Skipped
CI / snap (push) Failing after 12s
2026-08-22 22:26:20 +02:00
vhaudiquet f54be8ad8c pull,deb: add top-level --pocket option
CI / build (push) Failing after 2m31s
CI / snap (push) Skipped
- pull: select the pocket to download the source package from
- deb: build against dependencies from a specific pocket:
  - local mode: enable '{series}-{pocket}' suite on archive sources
  - cross mode: include the pocket suite in required repositories
2026-08-22 12:02:05 +02:00
vhaudiquet ec77bc6d4d prune: add 'pkh prune' command
CI / build (push) Successful in 15m13s
CI / snap (push) Successful in 2m13s
2026-08-14 10:28:56 +02:00
vhaudiquet d3a07bc80d chlog: add series selector
CI / build (push) Successful in 15m3s
CI / snap (push) Successful in 2m6s
2026-08-11 14:42:23 +02:00
vhaudiquet 62ce4e3696 deb: remove sbuild build mode
CI / build (push) Successful in 8m15s
CI / snap (push) Successful in 3m28s
2026-07-24 14:32:47 +02:00
vhaudiquet 768e1c4f78 fmt
CI / build (push) Failing after 1m56s
CI / snap (push) Has been skipped
2026-07-22 14:36:28 +02:00
vhaudiquet c4b59a4376 context: copy symlinks as symlinks and improve error messages 2026-07-17 10:12:46 +02:00
vhaudiquet 2b6207981a deb: make tests parallel
CI / build (push) Failing after 12m10s
CI / snap (push) Has been skipped
2026-03-19 00:24:48 +01:00
vhaudiquet dce39c9a84 deb: allow adding multiple ppas for deps
CI / build (push) Failing after 13m29s
CI / snap (push) Has been skipped
2026-02-19 15:36:08 +01:00
vhaudiquet 87b48bf9c8 deb: add --inject to inject a package
CI / build (push) Failing after 20m25s
CI / snap (push) Has been skipped
2026-02-18 23:19:57 +01:00
vhaudiquet 8345f51d2f Multiple changes:
CI / build (push) Successful in 16m23s
CI / snap (push) Successful in 4m1s
- deb: can use ppa as dependency
- deb: cross and regular are using parallel nocheck builds
- deb: ephemeral will not pull keyring if no root powers
2026-02-06 12:04:25 +01:00
vhaudiquet 225157be63 pull: allow pulling from ppa
CI / build (push) Successful in 15m54s
CI / snap (push) Successful in 3m19s
2026-01-29 17:11:01 +01:00
vhaudiquet 2f5fb3b1e7 deb: fix runtime usage for chroot
CI / build (push) Failing after 11m13s
2026-01-26 10:14:04 +01:00
vhaudiquet 29297d6f34 pkh: added pull --archive flag
CI / build (push) Successful in 9m20s
2026-01-12 22:42:09 +01:00
vhaudiquet 91c812a530 pull: allow to force pull from archive
CI / build (push) Failing after 1m2s
2026-01-11 20:43:01 +01:00
vhaudiquet 70e6d8c051 pull: refactor to remove series argument
CI / build (push) Successful in 9m31s
2026-01-11 12:36:19 +01:00
vhaudiquet a2d4f885b5 doc: update README, documentation 2026-01-11 12:16:27 +01:00
vhaudiquet 650adc28a3 pull: split into package_info::lookup and pull
CI / build (push) Failing after 1m47s
2026-01-11 12:12:19 +01:00
vhaudiquet b3365afe5b docs: added documentation, enforced documentation
CI / build (push) Successful in 7m21s
2026-01-01 18:37:40 +01:00
vhaudiquetandvhaudiquet 1538e9ee19 deb: cross-compilation, ephemeral contexts, local builds
CI / build (push) Successful in 7m18s
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>
2025-12-25 17:10:44 +00:00