Packastack is a small CLI tool to assist with building OpenStack packages for Ubuntu.
Packastack targets an Ubuntu host and drives the standard Debian packaging toolchain. Install the system packages before first use:
sudo apt install git git-buildpackage devscripts dpkg-dev sbuild schroot gnupg
sudo sbuild-adduser $USER # then log out/in (or `newgrp sbuild`) for group membership to applygit, gbp, dch, and dpkg-source are required for source builds; sbuild/schroot are needed for binary builds (the default) and gnupg for signature verification. Missing tools are reported with install hints when you run a build.
Python 3.12+ and uv are used for the Python side:
sudo snap install astral-uv --classic # or see uv's install docsgit clone https://github.com/MylesJP/packastack.git
cd packastack
uv syncRun the CLI through uv from the repo checkout:
uv run packastack --helpOr install it as a tool so packastack is on your PATH:
uv tool install .
packastack --help# 1. One-time setup: writes ~/.config/packastack/config.yaml, creates caches
# under ~/.cache/packastack, and clones the OpenStack releases metadata.
packastack init # add --prime to also fetch Ubuntu archive indexes now
# 2. Refresh Ubuntu archive package indexes (also refreshed on demand by builds).
packastack refresh
# 3. Build a package. Schroots are created automatically on first build
# (requires sudo). The host architecture is used.
packastack build nova --ubuntu-series nobleBuild artifacts and logs land under ~/.cache/packastack/build/<package>/<build-id>/, and built packages are published to the local APT repository at ~/.cache/packastack/apt-repo.
Resume an interrupted build by reusing a previous workspace:
packastack build cinder --resume # latest build
packastack build cinder --resume-build 20260210-143022 # specific buildpackastack init— initialize configuration and cache directories, clone OpenStack releases, and optionally prime Ubuntu archive metadata (--prime).packastack refresh— fetch and cache Packages.gz indexes from an Ubuntu archive mirror, respecting TTL and offline mode.packastack build <package>— build a package (and optionally its dependencies with--build-deps, or everything with--all).packastack plan <package>— show the validated build plan without building.packastack clean— inspect and manage local cache state.
Three special package names build an entire class of managed packages in one command. Each discovers all packages, classifies them from the OpenStack releases metadata, and builds the matching set in dependency order using the same infrastructure as build --all (including --parallel, --keep-going, and --dry-run):
packastack build libraries— all library packages: Oslo and other shared libraries plus the Python client libraries.packastack build clients— only the Python client libraries (python-novaclient,python-glanceclient, and friends).packastack build services— only the core services (nova, glance, neutron, cinder, keystone, ...).
A typical release workflow builds libraries first so the local APT repo can satisfy service build-dependencies, then services:
packastack build libraries --ubuntu-series noble
packastack build services --ubuntu-series nobleUse --dry-run to list what a subset would build without building it. Related: packastack build rc (or rc1, rc2, ...) builds all packages with a matching release-candidate release.
See packastack <command> --help for full flags, pyproject.toml for development dependencies and test configuration, and docs/ for the full documentation.
Packastack ships an AI-assisted triage system called build-doctor. When an sbuild fails and an API key is configured, build-doctor inspects the log, picks the best specialist skill for the failure shape, and either produces a validated patch or explains what the maintainer needs to do. AI features are opt-in and non-blocking: if the API call fails, you get the normal failure output. See docs/reference/config.rst for configuration and docs/explanation/ai-skills.rst for the architecture.
build-doctor is a deterministic Python dispatcher plus pluggable skills on disk. Skills are folders containing a SKILL.md file with YAML frontmatter (name, description, output contract, trigger regexes) and a prompt body. The dispatcher:
- Extracts the failure section from the sbuild log.
- Tries a cheap regex pass over every skill's
triggers.log_patterns. A match routes directly — no router call. - Otherwise asks the
build-doctorrouter skill to pick a specialist. The router returns a structured dispatch payload with a confidence score, evidence lines, and optional fallbacks. - Validates the pick against a confidence threshold (default 0.5, configurable) and the installed skill registry; walks the fallback chain if needed; drops to the generic
build-patchskill as a last resort. - Runs the chosen specialist, parses its response against its declared output contract, validates any patch with
git apply --check(with one correction retry via thepatch-correctionskill), and commits the result.
Adding a new failure-class handler is a folder drop — no Python changes. Drop src/packastack/skills/<name>/SKILL.md in place (or point PACKASTACK_SKILLS_DIR at a dev copy), and the router picks it up at next run.
| Skill | Output | Role |
|---|---|---|
build-doctor |
dispatch | Router — picks the specialist |
build-patch |
patch | Generic patch author; safe fallback |
patch-diagnosis |
diagnosis | Decides whether an existing quilt patch can be dropped (already upstream?) |
patch-refresh |
patch | Refreshes fuzzy patches after upstream drift, including setup.cfg → pyproject.toml migrations |
patch-correction |
patch | Fixes a patch that failed git apply --check |
python-compat |
patch | Handles interpreter bumps (distutils/imp/cgi removed, ast.Str → Constant, asyncio.coroutine removed, etc.) |
library-sync-advisor |
guidance | Detects stale oslo / python-*client / stevedore / keystoneauth1 dependencies and advises a library sync instead of a patch |
- Python interpreter bumps — routed to
python-compat, which knows the common removed-stdlib replacements. - Fuzzy quilt patches — routed to
patch-refresh; produces an updated patch against current upstream. - Upstreamed patches —
patch-diagnosis+ auto-drop: removes the file, updatesdebian/patches/series, and commits. - Stale dependencies —
library-sync-advisorexplicitly refuses to patch and tells you which src:package to sync. - Retry with memory — failed attempts are saved to
ai-memory.jsonin the run directory so subsequent builds can tell skills what was already tried.
| Gap | Detail |
|---|---|
No debian/ file edits |
Skills are restricted to upstream-source patches; changing debian/control, debian/rules, or debian/changelog requires a human. |
| No build-dep auto-resolution | A skill can diagnose "missing libfoo-dev" via guidance, but cannot install or add it to Build-Depends. |
| Single patch-and-rebuild cycle | One retry per build; a second failure saves memory and exits. |
| No multi-arch awareness | Arch-specific fixes need human review. |
| No interactive approval | Valid patches are applied automatically; no preview step. |
| No API retry or cost tracking | Single HTTP call per skill invocation; transient failures are not retried. |
extra_files_needed parsed but unused |
The router can ask for additional context, but the runner doesn't yet re-run with it. |
- Eval fixtures — a corpus of real failed sbuild logs with expected router picks to guard routing quality as skills multiply.
- Consume
extra_files_needed— when the router asks fordebian/patches/seriesetc., re-run with them instead of guessing. ai captureCLI — snapshot a triage run into a fixture directory.- More specialists —
dep-gate(guidance for dependencies not landed yet),packaging-advisor(structural shifts like eventlet removal). - Approval mode — pause before applying AI changes so operators can review.
debian/file skills — let specialists emitdebian/control/debian/rulesedits behind an explicit opt-in.