Packaging/Override configuration files

Oftentimes, postmarketOS packages need to replace files provided by Alpine packages. One example is /etc/issue, which says "Welcome to postmarketOS" in pmOS.
=== History ===
For a few years we did this by modifying config files from post-install scripts: after a certain postmarketOS package was installed, it would run a shell script that adjusts the config file as we need it. But this has the downside that the apk package manager now assumes, the user has modified the file. Because it is different from the version supplied by the Alpine package. With this assumption, apk will not update the file anymore. So the config file always remains whatever random version from the Alpine package was installed at that time plus all modifications done in (various versions of) post-install scripts from postmarketOS. It is not deterministic anymore what is in the config file. This is not so bad for /etc/issue, but if you consider important config files, like the sudo configuration, it becomes apparent that this isn't great at all.
In order to get this right, some simple shell logic was put into postmarketos-mvcfg, which will move the existing files (where we don't know the exact content anymore) to a backup directory. Afterwards, the postmarketOS version of the config file will be installed with the package. (We realized that apk allows policy packages that overwrite config files of other packages, more on that below.) From user's perspective, the migration looks like the following. The hint about moved config files are only shown to the user if they did the upgrade via terminal (not if they did it via GNOME Software or KDE Discover). But in postmarketOS it should be safe to assume that most people did not modify these specific config files, and if they did, they are very likely going to do the upgrades via terminal too. In addition, we wrote about this change in the edge blog.
$ sudo apk upgrade
(1/3) Installing postmarketos-mvcfg (1-r0)
(2/3) Upgrading postmarketos-base (4-r0 -> 5-r2)
Executing postmarketos-base-5-r2.pre-upgrade
 *
 * These configs were created with legacy packaging methods:
 * /etc/chrony/chrony.conf
 * /etc/conf.d/syslog
 * /etc/conf.d/wpa_supplicant
 * /etc/fstab
 * /etc/issue
 * /etc/motd
 * /etc/sudoers
 * /etc/wpa_supplicant/wpa_supplicant.conf
 *
 * In order to replace them with properly packaged configs,
 * the old versions (which you might have adjusted manually)
 * were moved to:
 * /etc/postmarketos-mvcfg/backup/postmarketos-base
 *
 * If you did not manually adjust these configs, you can ignore
 * this message.
 *
 * More information: https://postmarketos.org/mvcfg
 *
...
=== Overriding configs properly ===
Let's say you want to override /etc/conf.d/wpa_supplicant to change the order of the Wi-Fi drivers being used. Use pkgs.alpinelinux.org to find out that this file is provided by wpa_supplicant-openrc. Add replaces (reference) to the APKBUILD, install your own config file into the same path in package(). That's it.
apk will override the config with your custom config, when both packages are installed. Even when the original package gets upgraded and has a newer version of the config file, apk will still use your version of the file.
pkgname="override-test"
replaces="wpa_supplicant-openrc"
source="wpa_supplicant.confd"
package() {
	install -Dm644 wpa_supplicant.confd "$pkgdir"/etc/conf.d/wpa_supplicant
}
=== Modernizing packages ===
Follow these steps to change a package from using the legacy method to the proper method.
Consider omitting the postmarketos-mvcfg migration if this package isn't used widely (i.e. any random downstream port that was added last week).
==== Add the config as file ====
* Find the original config file contents from the Alpine package.
: This can be done by installing just this package and nothing else in a new pmbootstrap chroot, then running cat to read the config file.
* Add this config to your overriding package.
* Apply modifications to that config.
==== Adjust post-install script ====
* Remove logic to apply the modifications to the config file from the post-install script.
* Consider removing the post-install script altogether, if it is empty now.
==== Adjust APKBUILD ====
===== Main package =====
...
install="$pkgname.pre-upgrade"
replaces="pkg-with-config"
...
package() {
	install -Dm644 myconfig1 "$pkgdir"/etc/myconfig1
	install -Dm644 myconfig2 "$pkgdir"/etc/myconfig2
	postmarketos-mvcfg-package "$pkgdir" "$pkgname"
}
===== Subpackage =====
mysubpkg() {
	install="$subpkgname.pre-upgrade"
	replaces="pkg-with-config"
	install -Dm644 myconfig1 "$subpkgdir"/etc/myconfig1
	install -Dm644 myconfig2 "$subpkgdir"/etc/myconfig2
	postmarketos-mvcfg-package "$subpkgdir" "$subpkgname"
}
==== Create pre-upgrade script ====
Add a pre-upgrade script with the following content:
#!/bin/sh
postmarketos-mvcfg-pre-upgrade \
	my-pkgname \
	/etc/myconfig1 \
	/etc/myconfig2
my-pkgname is the backupdir. This should be the package name of the package where you are adding the pre-upgrade script. (Unless you need to do a second migration for some reason, then add another postmarketos-mvcfg-pre-upgrade statement below and add a suffix like -v2 to the backupdir.)
/etc/myconfig1 this is the first config file. Put each argument in its own line, and order the config filenames alphabetically.