Let us install packages without announcing. Useful if there is some more
contextual message display already.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
As we don't call apk from outside of the chroot, the paths need to be
adjusted to be /mnt/pmbootstrap instead of the outside paths.
This should be reverted together with the other TEMP commit after we
upgraded apk to fix this properly (see issue 2388).
Fixes: f5122420 ("TEMP: chroot: apk: run apk through QEMU again (MR 2351)")
This revers b82c4eb167 ("chroot: always run apk static (MR 2252)")
since the current iteration of the apk-tools /usr merge patches
don't handle --root properly and will behave differently based on your
host systems directory structure (specifially for /usr merge).
This commit can be reverted once we fix apk-tools.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
We recently changed how we manage chroots, requiring the user (of the
chroot) to initialize it before doing stuff like installing packages.
There are however still certain edgecases in pmbootstrap where this
doesn't get done (for example qemu/run.py would attempt to install the
kernel package for the device, but we don't initialize the rootfs chroot
in QEMU since that doesn't make sense to do). Check for and catch this
situation explicitly so we don't ruin someones day, but still be loud
about it so we can hopefully catch the remaining instances of this.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This turns out to have been wrong, since we need to give apk the package
name AND the apk file, it installs the apk and then uninstalls it but
leaves the package installed by name.
Turns out this is necessary, *sigh*. Since we could have one locally
built package depend on another locally built package.
So yeah, re-implement this, but in a slightly lazier / hopefully faster
way.
Bah
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
This got broken during rework. Fix it up, make sure we filter out
packages from to_add that are specified in to_add_local.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
It turns out that apk with the --root argument will still treat local
repositories relative to the host. This makes sense considering the
intended usecase, however as a result the recent change to use
apk.static for all apk commands inside the target root will break the
use of local packages.
To fix this, adjust how we populate /etc/apk/repositories, changing the
default to no longer include the local user repo. Instead we pass
--repository to apk.static with the host path to the repo.
The only time we want to add the user repo to /etc/apk/repositories
inside the chroot is when the user enters the chroot, so a special case
is added to helpers/frontend.py for the chroot command.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
We now have support for having mirrors per-aports repo, drop the
mirrors_postmarketos arg from chroot.init and instead have
repo_bootstrap call apk.update_repository_list() explicitly to exclude
the mirrors for the repository we're going to update.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
We used to recursively descend into the dependencies of the packages we
mark for install, this was needed so that we build all the packages we
want to.
However, the new package builder now does this for us. So simplify and
speed this up a bit by not doing it in apk.install().
Additionally, soft-remove support for passing in packages to delete by
having the package name start with a "!". This was useful at some point,
but we now handle this much better in pmaports.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Generalise pmb.helpers.other.cache with a more python decorator.
The Cache decorator takes a list of function arguments to use as cache
keys, keyword args can be used to restrict caching so that it is skipped
entirely unless the attribute has a specific value.
For example, pmb.helpers.pmaports.get() has the decorator:
@Cache("pkgname", subpackages=True)
This means the return value will be cached only when subpackages is
True, otherwise it will always miss.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Use the new config.mirrors section to handle repository URLs instead of
the old mirror_alpine / mirrors_postmarketos options. This let's us
add the systemd staging repo automatically when on the systemd staging
branch / systemd is enabled.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Move pmb/parse/arch.py over to core and refactor it as an Arch type,
similar to how Chroot was done. Fix all the uses (that I can find) of
arch in the codebase that need adjusting.
The new Arch type is an Enum, making it clear what architectures can be
represented and making it much easier to reason about. Since we support
~5 (kinda) different representations of an Architecture (Alpine, Kernel,
target triple, platform, and QEMU), we now formalise that the Alpine
format is what we represent internally, with methods to convert to any
of the others as-needed.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Cease merging pmbootstrap.cfg into args, implement a Context type to let
us pull globals out of thin air (as an intermediate workaround) and rip
args out of a lot of the codebase.
This is just a first pass, after this we can split all the state that
leaked over into Context into types with narrower scopes (like a
BuildContext(), etc).
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Testing by building postmarketos-initramfs (which installs >100 packages
but is very fast to build, so a worst-case scenario) this results in a
~15-20% speedup (which everything cached and doing multiple back to back
runs). From 32 seconds down to 25.
Doing a full install with --no-image, this takes us from 70 seconds on
my laptop down to 40s!
This also lets us drastically simplify pmb/helpers/apk.py!
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
We currently lazily initialize the chroot's on first use, plus a few
bonus calls to init. However, there are some instances where we actually
don't want the chroot to be initialised (mostly to break recursion
loops).
Simplify the codebase by removing all of this, and just calling
pmb.chroot.init() where it's needed.
In addition, print a warning if init() is called multiple times for one
chroot. This should help us catch these instances if they crop up again.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
Defaulting to the native chroot isn't necessarily intuitive. Let's
require this be specified in full.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
With the new chroot type, we can now write fancy paths in the pythonic
way. Convert most of the codebase over, as well as adding various other
type hints.
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
We use a custom verbose log level in pmbootstrap, unfortunately it isn't
possible to correctly type this due to some limitations in the logging
library [1], [2].
Given that our usecase is fairly simple, we can just wrap the module
with our own so we only have to tell mypy to ignore the error once
instead of at every callsite.
[1]: https://github.com/cryptax/droidlysis/issues/15
[2]: https://github.com/python/typing/discussions/980
Signed-off-by: Caleb Connolly <caleb@postmarketos.org>
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).
Additionally, migrate the entire codebase over to using pathlib.Path.
This is a relatively new part of the Python standard library that uses a
more object oriented model for path handling. It also uses strong type
hinting and has other features that make it much cleaner and easier to
work with than pure f-strings. The Chroot class overloads the "/"
operator the same way the Path object does, allowing one to write paths
relative to a given chroot as:
builddir = chroot / "home/pmos/build"
The Chroot class also has a string representation ("native", or
"rootfs_valve-jupiter"), and a .path property for directly accessing the
absolute path (as a Path object).
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.
As the chroot suffixes are now part of the Chroot class, we also
implement validation for them, this encodes the rules on suffix naming
and will cause a runtime exception if a suffix doesn't follow the rules.
This is needed to bring up the v24.06 repositories at
build.postmarketos.org. With the latest apk version, apk refuses to
operate if an URL from /etc/apk/repositories cannot be fetched.
Before the repositories are created for the first time, they do not
exist, so we will just set PMB_APK_FORCE_MISSING_REPOSITORIES=1 in bpo
to be not blocked here.
I've also spent significant time on alternative implementations, but
they have problems:
- Let bpo create an empty APKINDEX before building the first package,
but this was a larger code change, leading to lots of adjustments in
the tests, and ultimately it seems it didn't work properly (it seems
apk/abuild doesn't create a valid signed APKINDEX for one that has no
packages).
- Do not set the --mirror-pmOS argument for the "final" repository, only
the "wip" repository, until the "final" repository is available for
the first time. This works fine for x86_64, but not for foreign arch
repositories because then the cross compilers from the x86_64
repository are not available. I've also tried to make a different env
var that ensures we don't write the non-existing repository to
/etc/apk/repositories from within pmbootstrap if initializing a
foreign arch chroot, but then we would find a sane way to do this only
for the "final" repository and not for the "wip" repository which
leads to a lot more complexity than this patch.
So this is not the nicest solution (apk still tries to fetch the indexes
and gets a 404), but it is the simplest one and unblocks us from working
on v24.06. Also it doesn't add more complexity which is important in the
middle of the feature freeze we are currently in.
Related: bpo issue 137
Related: d76213e643
Related: https://postmarketos.org/blog/2024/05/19/pmOS-update-2024-05/#pmbootstrap-230-and-feature-freeze
Add this argument, so we can disable the pmOS repository during
"pmbootstrap repo_bootstrap" for the chroot we build in.
If building natively, it will be disabled in the "native" chroot.
If building for a foreign arch, it will be disabled in the
"buildroot_$arch" chroot, but still be enabled in the native chroot so
we have the cross compilers available.
The name of the argument is a bit long, but it is consistent with the
argument of the same name in pmb.helpers.repo.urls() (to which it gets
passed).
This fixes an issue when pmb is run on an Alpine/pmOS host and apk.static sees
that /etc/apk/interactive is set on the host. It's really annoying to have the
build pause so apk can prompt when building chroots, so this uses a flag to apk
to disable interactive mode. I can't think of any situations where we would
actually want to prompt users when building chroots, by then all user
configuration should have been completed via pmb init/config.
Reviewed-by: Oliver Smith <ollieparanoid@postmarketos.org>
Co-developed-by: Oliver Smith <ollieparanoid@postmarketos.org>
Link: https://lists.sr.ht/~postmarketos/pmbootstrap-devel/%3C20231110185320.8358-1-clayton@craftyguy.net%3E
Check if the pkgnames are sane in install_run_apk, right before running
apk. This makes sure that we really run it on all arguments that are
supposed to be packages / files and not options to apk.
Previously pmbootstrap would only show the packages that are about to be
installed. In case all packages were already installed, this would lead
to weird empty install messages:
(rootfs_asus-me176c) install
Show all packages that we want to install, even if they are already
installed in the given chroot.
(rootfs_asus-me176c) install device-asus-me176c
Previously to this patch, pmbootstrap would pass a full dependency tree
to "apk add". It would use a virtual package to ensure only the right
packages get added to /etc/apk/world. For example:
apk add -u --virtual .pmbootstrap postmarketos-base device-asus-me176c \
postmarketos-ui-sxmo-de-sway device-asus-me176c-nonfree-firmware w3m \
sfeed clickclack firefox-esr font-noto font-noto-emoji gnome-icon-theme \
imv megapixels mobile-config-firefox ttyescape postmarketos-base-nofde \
eudev openssh postmarketos-mkinitfs postmarketos-mvcfg postmarketos-keys \
...
Instead of doing that, only pass the packages we want to install and let
apk figure out the dependencies. Most of the time we can even avoid
using the virtual package now.
== Remaining edge case: locally built packages
apk will only upgrade a package with the same pkgver + pkgrel but a
different build date if the full path to an apk file gets passed as
argument. So if the user built a package locally that will be installed,
or one of its dependencies then we still need to use a virtual package
and possibly pass a dependency to apk. Replace
replace_aports_packages_with_path() with packages_get_locally_built_apks()
to get a list of such packages and adjust install() and
install_run_apk() to use it.
Make the code easier to read by moving split_to_add_del() to a separate
function and do some related refactoring. A future patch will use it
twice in install().
Move "arch = ..." to the top of the function while at it, since it's
needed later in the function in 2 places and is not needed for figuring
out packages_with_depends, to_add and to_del.
Remove "# Add depends to packages" because it's obvious from the
packages_with_depends variable name, and getting to_add/to_del is a
different action that stood under the same comment.
Split out the part that builds the apk commands and runs them out from
install() to a new function install_run_apk(). This makes install()
easier to read.
Since the previous commit that adds install_build(), all packages either
end up in to_add or to_del. Move the check for empty packages to the top
of the function, and directly check the packages variable.
I think it's worth keeping this check because it's shorter to add this
check once here than having it a few times in other place where we may
have or may not have something to install. And so we can avoid printing
an empty "install" message with no packages.
install_is_necessary used to do the following things:
1. Error out if there's no binary package but pmb was invoked as
"pmbootstrap install" and build_pkgs_on_install is disabled.
2. Build the package if necessary.
3. Return if a package "needs to be installed" (Boolean or Float).
The only caller of the function is pmb.chroot.apk.install. It would not
add the package to the long "apk add" command if according to 3. it does
not need to be installed.
When I implemented this a few years ago, I probably thought it would be
useful to not unnecessarily pass packages to apk. But this actually
makes it more complicated and doesn't have a benefit, apk is perfectly
capable of recognizing which packages it had already installed.
Replace the function with a much simpler pmb.chroot.apk.install_build,
which only does 1. and 2. Change the order of the package, arch
arguments to match called functions pmb.parse.apkindex.package and
pmb.build.package.
Put underscores between the words for consistency with other variable
names. Since the whole function is about dealing with packages, remove
the "packages_" prefix to make them shorter.
packages_toadd -> to_add
packages_todel -> to_del
When installing dependencies for a package, conflicting (!) dependencies
are now deleted (with `apk del pkg`) whereas before a constraint for
their _absence_ was added (with `apk add !pkg`). Doing it the new way
around prevents creating deadlocks because a `!pkg` constraint will
prevent pkg from ever being installed without an explicit `apk del`
call.
Fixes: #2092