Skip to content

Commit 58e39cc

Browse files
committed
feat(cli): rework build / test environment handling
Adds an explicit environment (env) subcommand which allows users to list and interact with existing build / test environments. Additionally contains extensive internal refactoring to make the environment tracking and cleanup more robust.
1 parent ddb4a2f commit 58e39cc

37 files changed

Lines changed: 4435 additions & 1001 deletions

‎.github/workflows/ci.yaml‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ jobs:
141141
contents: read
142142
# Push of the pre-built CI image is additionally gated to main below.
143143
packages: write
144+
env:
145+
# Registry and Environment Host roots together, so the cached build
146+
# tree stays registered under the same Environment id.
147+
DEBMAGIC_DATA_DIR: /tmp/debmagic-data
144148
steps:
145149
- uses: actions/checkout@v5
146150
- uses: Swatinem/rust-cache@v2
@@ -149,16 +153,18 @@ jobs:
149153
- name: Cache the debmagic build tree
150154
uses: actions/cache@v4
151155
with:
152-
path: /tmp/debmagic
153-
key: debmagic-build-${{ hashFiles('Cargo.lock', 'debian/changelog', 'debian/control', 'debian/rules') }}
154-
restore-keys: debmagic-build-
155-
- name: Prune stale build trees from the restored cache
156+
path: /tmp/debmagic-data
157+
key: debmagic-data-${{ hashFiles('Cargo.lock', 'debian/changelog', 'debian/control', 'debian/rules') }}
158+
restore-keys: debmagic-data-
159+
- name: Prune Environments of other package versions from the restored cache
156160
run: |
157-
mkdir -p /tmp/debmagic
158161
current="$(dpkg-parsechangelog -SSource)-$(dpkg-parsechangelog -SVersion)"
159-
find /tmp/debmagic -mindepth 1 -maxdepth 1 -type d \
160-
! -name "$current" ! -name "${current}-test" \
161-
-exec rm -rf {} +
162+
cargo run --locked -q -p debmagic -- env list \
163+
| awk -v current="$current" 'NR > 1 && $4 != current { print $1 }' \
164+
| while read -r id; do
165+
cargo run --locked -q -p debmagic -- env clean "$id" \
166+
|| echo "::warning::could not prune Environment $id"
167+
done
162168
- name: Resolve CI image tag
163169
id: image
164170
run: |

‎Cargo.lock‎

Lines changed: 96 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ ignore = { version = ">=0.4.25" }
3535
md-5 = { version = ">=0.11" }
3636
sha1 = { version = ">=0.11" }
3737
sha2 = { version = ">=0.11" }
38+
rusqlite = { version = ">=0.32" }
3839

3940
# dev dependencies
4041
test-case = { version = ">=3.3.1" }

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Modern, robust & easy [Debian](https://debian.org)/[Ubuntu](https://ubuntu.com)
77
Debmagic unifies the the packaging experience as a single, streamlined tool. Additionally, `debmagic-pkg` allows you to define package build recipes in Python.
88

99
- **Isolated builds without the setup**: `debmagic build binary` builds a `debian/`-packaged source tree in a temporary container (using LXD, Incus, Docker)
10-
- **Fast iteration**: `--persistent`/`--incremental` reuse the environment and sync only source changes; `--shell-on-failure` and `debmagic shell` drop you right where the build broke
10+
- **Fast iteration**: `--persistent`/`--incremental` reuse the environment and sync only source changes; a failed build prints `debmagic env shell <id>`
1111
- **Test & sign integrated**: `debmagic test` runs the package's autopkgtest tests in a fresh environment, `debmagic sign` GPG-signs `.changes`/`.dsc`/`.buildinfo` on the host
1212
- **Python packaging API**: replace complicated `debian/rules` Makefiles with typed Python `debian/rules.py`, with optional `dh` compatibility
1313

@@ -30,7 +30,7 @@ uvx debmagic build binary --driver docker
3030
| `debmagic build binary` | Build a binary package (`.deb`) in an isolated environment |
3131
| `debmagic build source` | Create a source package (`.dsc`) for upload (incl signing) |
3232
| `debmagic test` | Run the package's autopkgtest tests (`debian/tests/`) against a prior build |
33-
| `debmagic shell` | Attach an interactive shell to the build environment |
33+
| `debmagic env` | List, clean, or open a shell in a build or test environment |
3434
| `debmagic sign` | GPG-sign a `.changes` file (and its `.dsc`/`.buildinfo`) on the host |
3535
| `debmagic config` | Inspect and edit the effective `debmagic.toml` configuration |
3636
| `debmagic check` | Lint the package *(in progress)* |

‎debian/control‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ Build-Depends:
3838
librust-md-5-dev (>= 0.11),
3939
librust-sha1-dev (>= 0.11),
4040
librust-sha2-dev (>= 0.11),
41+
librust-rusqlite-dev (>= 0.32),
42+
libsqlite3-dev,
43+
pkg-config,
4144
Rules-Requires-Root: no
4245
X-Style: black
4346
Standards-Version: 4.7.2

‎debian/rules.py‎

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,43 @@
1717
)
1818

1919
# TODO: add this section to our rust debmagic module to make setting this up less annoying
20-
os.environ.update({
21-
"PATH": f"/usr/share/cargo/bin:{os.environ['PATH']}",
22-
"CARGO": "/usr/share/cargo/bin/cargo",
23-
"CARGO_HOME": f"{pkg.base_dir}/debian/cargo_home",
24-
"CARGO_REGISTRY": f"{pkg.base_dir}/debian/cargo_registry",
25-
"DEB_CARGO_CRATE": f"{pkg.build_env.DEB_SOURCE}_{pkg.build_env.DEB_VERSION_UPSTREAM}"
26-
})
20+
os.environ.update(
21+
{
22+
"PATH": f"/usr/share/cargo/bin:{os.environ['PATH']}",
23+
"CARGO": "/usr/share/cargo/bin/cargo",
24+
"CARGO_HOME": f"{pkg.base_dir}/debian/cargo_home",
25+
"CARGO_REGISTRY": f"{pkg.base_dir}/debian/cargo_registry",
26+
"DEB_CARGO_CRATE": f"{pkg.build_env.DEB_SOURCE}_{pkg.build_env.DEB_VERSION_UPSTREAM}",
27+
# debmagic-pkg is setuptools and ignores this. The rewrite below is what
28+
# makes resolution succeed; this only keeps maturin from enabling the
29+
# (now empty) default feature.
30+
"MATURIN_PEP517_ARGS": "--no-default-features",
31+
}
32+
)
33+
34+
# Cargo resolves every feature name in the table, including ones that are not
35+
# enabled, so `--no-default-features` still requires rusqlite to advertise
36+
# `bundled`. Debian's librust-rusqlite-dev deletes that feature and links the
37+
# system libsqlite3. Drop the reference for this build, then put the manifest
38+
# back so a host or incremental tree is not left edited.
39+
cargo_toml = Path("packages/debmagic/Cargo.toml")
40+
bundled_sqlite = 'bundled-sqlite = ["rusqlite/bundled"]'
41+
system_sqlite = "bundled-sqlite = []"
42+
43+
44+
def use_system_sqlite() -> str:
45+
original = cargo_toml.read_text()
46+
if bundled_sqlite in original:
47+
cargo_toml.write_text(original.replace(bundled_sqlite, system_sqlite, 1))
48+
return original
49+
if system_sqlite in original:
50+
restored = original.replace(system_sqlite, bundled_sqlite, 1)
51+
return restored
52+
raise SystemExit(
53+
f"{cargo_toml}: expected {bundled_sqlite!r} so the Debian build can drop "
54+
"the rusqlite bundled feature that librust-rusqlite-dev removes"
55+
)
56+
2757

2858
packages = {
2959
"debmagic": ("packages/debmagic", "debmagic"),
@@ -33,10 +63,14 @@
3363
cargo_lock = Path("Cargo.lock")
3464
cargo_lock_saved = Path("Cargo.lock.saved")
3565

66+
3667
def dh_auto(build: Build, stage: str, use_destdir: bool = False):
3768
for pkg_name, (path, python_pkg_name) in packages.items():
3869
destdir = f" --destdir debian/{pkg_name} " if use_destdir else ""
39-
build.cmd(f"{stage} -p {pkg_name} --sourcedirectory {path} --buildsystem=pybuild {destdir} -- --name {python_pkg_name}")
70+
build.cmd(
71+
f"{stage} -p {pkg_name} --sourcedirectory {path} --buildsystem=pybuild {destdir} -- --name {python_pkg_name}"
72+
)
73+
4074

4175
@dhp.override
4276
def dh_auto_configure(build: Build):
@@ -68,4 +102,8 @@ def dh_auto_clean(build: Build):
68102
dh_auto(build, "dh_auto_clean")
69103

70104

71-
pkg.pack()
105+
cargo_toml_original = use_system_sqlite()
106+
try:
107+
pkg.pack()
108+
finally:
109+
cargo_toml.write_text(cargo_toml_original)

‎docs/index.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Working with Debian packages without `debmagic` means juggling with several inde
99
Debmagic unifies this into one CLI with useful defaults:
1010

1111
- **Isolated builds without the setup**: `debmagic build binary` builds a `debian/`-packaged source tree in a temporary container (using LXD, Incus, Docker)
12-
- **Fast iteration**: `--persistent`/`--incremental` reuse the environment and sync only source changes; `--shell-on-failure` and `debmagic shell` drop you right where the build broke
12+
- **Fast iteration**: `--persistent`/`--incremental` reuse the environment and sync only source changes; a failed build prints `debmagic env shell <id>`
1313
- **Test & sign integrated**: `debmagic test` runs the package's autopkgtest tests in a fresh environment, `debmagic sign` GPG-signs `.changes`/`.dsc`/`.buildinfo` on the host
1414
- **Package uploading**: `debmagic upload` (to `debian`, `ubuntu`, `ppa:user/repo`) pushes signed builds to archives and PPAs via scp/sftp, with configurable pre-upload checks
1515
- **Upstream tracking**: `debmagic upstream list`/`switch` replace uscan/uupdate, reading the package's existing `debian/watch` and `debian/copyright`
@@ -37,6 +37,7 @@ Then dive into the pages below — start with [Getting started](usage/getting-st
3737
usage/getting-started.md
3838
usage/build.md
3939
usage/test.md
40+
usage/env.md
4041
usage/source.md
4142
usage/upload.md
4243
usage/upstream.md

0 commit comments

Comments
 (0)