Skip to content

Type hinting and Chroot's (Caleb's amazing pmbootstrap v3 patch series!)

Caleb Connolly requested to merge caleb/typed-suffix into master

Introduce a new module: pmb.core to contain explicitly typed pmbootstrap API. The first component being Suffix and SuffixType. This explicitly defines what suffixes are possible, future changes should aim to further constrain this API (e.g. by validating against available device codenames or architectures for buildroot suffixes).

This patch also converts the codebase to use the new Suffix type, and introduces the pathlib Path type for all path handling, to replace the common string building patterns. This is less error prone and can be integrated nicely with the Chroot type to make the code more readable and easier to refactor in the future.

The general idea here is to encapsulate common patterns into type hinted code, and gradually reduce the amount of assumptions made around the codebase so that future changes are easier to implement.


Ok, this MR has grown quite a lot. Here's a rough overview of what's in it now:

Args removed from most of the codebase, except where it actually makes sense.

The next step here will be to expand on pmb/commands/ types and use them to abstract out args from the other areas like pmb/install where it's still used a lot.

Introduced pmb/core/context

This replaces args for some things, and allows accessing the pmbootstrap config. As implemented right now it can be accessed from anywhere by calling get_context(). This is not ideal since it still keeps context as global state, ironically it would be better to have this state be passed around to functions, however I think some larger restructuring is needed to make this easier (like right now some really "low level" functions need access to this, but they're called from so many places that we'd end up with just the args problem all over again).

Chroot management

See the first section of this MR. Additionally, we no longer call pmb.chroot.init() everywhere, instead pmb.chroot module expects the first user to initialise the chroot before use. This avoids all the weird places it was being called from and solves a whole class of recursion issues that were really easy to run into when hacking on pmb before.

One approach to consider in the future is if we can make the Chroot type manage state, by refactoring the codebase to pass around Chroot objects (even) more, and/or by having the Chroot constructors always return the same object (so if you call Chroot.native() from two places you'll always get the same Chroot object).

The second approach seems like something fairly idiomatic imo, since the Chroot class is meant to be representative of the actual chroot on your filesystem, so doing chroot = Chroot.native() already feels like you're getting a handle to the chroot.

pathlib Paths

See the first section of this MR. Makes path building really simple and helps cut down on common path building patterns (since you now do chroot / "etc/apk/repositories to get an absolute path for example).

Arch type

pmb/parse/arch.py has been moved over to pmb/core/arch.py and refactored into a proper type. Since this is used in so many places across the codebase, having a proper type simplifies all the usages and disambiguates what kind of Arch we're working with.

Deviceinfo type

Similarly to Arch, deviceinfo is now a class, next steps will be to move this to core (requires reworking some of the parsing). As well as maybe doing type conversions (currently only done for deviceinfo.arch).

Config rework

pmb/core/config.py now contains a Config class with some magic handling so we can parse pmbootstrap.cfg, validate the types, and use it.

Previously, the config values were merged into args. Now, the runtime config is updated with any relevant values from args.

config.mirrors

The mirror_alpine and mirrors_postmarketos config options are removed in favor of a new [mirrors] section in pmbootstrap.cfg. This let's us specify mirrors per-aports for multiple aports repositories.

package builder logic rework

The package building logic in pmb/build/_package.py is rewritten. The old builder used recursion to descend into package dependencies and build them first. This limits the UX since we don't know ahead of time what packages we're actually going to build, and messages can get lost in the middle.

The new system recurses dependencies first and figures out what to build (in what order), then we actually build the packages.

Cache decorator

The pmb.helpers.other.cache dictionary is (mostly) removed in favor of a new @Cache function decorator. The decorator replaces almost all uses allowing caching to be done based on the function arguments, and even letting you disable caching when certain function arguments have specific values.

One or two uses of caching are not covered (and maybe never will be?).

pkgrepo API + support for multiple source repositories

One of the main blockers for systemd. We now have a new API in pmbootstrap to formalise the patterns for accessing the pmaports directory, and allowing multiple repos to be used.

In addition, special handling is added for the extra-repos dir we use for the systemd packages. For all intents and purposes, pmbootstrap now treats the extra-repos/systemd as it's own source repository, so packages like gnome-shell can be in both pmaports and systemd repos.

There are some issues that will need to be figured out regarding stuff like pmbootstrap lint (which right now will lint both).

As part of the package building rework, built packages will now be kept in a different local binary repo depending on which package repo it's from.

With this, the plasma mobile nightly repo could now be moved it it's own git repository, or into extra-repos - though the latter will require changes to pmbootstrap since there is a lot of systemd hardcoding right now.

The extra repos can also have their own mirrors, currently this is a TypedDict, but that shouldn't prevent arbitrary additional repos being added.

Use apk.static in native chroot and for all apk invocations

We fetch apk static from Alpine, however for systemd we actually use a fork of apk-tools. We now install apk-tools-static in the native chroot and run it from there. This is necessary for the second half since we now use apk.static whenever we install packages in a chroot, since it's much faster than running apk through QEMU.

Don't enforce checksums unless building with strict

The abuild verify() function is overridden by appending a drop-in to the end of the APKBUILD file when copying it into the chroot. This version is modified to not die() when the checksums don't match, instead it sets a flag that pmb uses after building to display a warning (and instructions to generate the checksum).

The should immensely speed up the dev loop case where you're building a package over and over again for testing.

Allow for incremental builds with --src

Make rsync exclude files that are in the .gitignore of the project (like build artifacts). And by default have abuild keep the build artifacts. This way if you're running pmbootstrap build --src=... multiple times then incremental builds will be used.

Edited by Caleb Connolly

Merge request reports