􀀂􀀟􀀍􀀆 􀀂􀀖􀀌􀀅 Custom APK Repositories

I’ve had reason to build APK packages for installing to Alpine Linux machines that I want to maintain outside of the official Alpine Linux aports repository. I have a few recommendations:

  1. Exactly one source directory per APK repository
  2. One Alpine Linux major version per APK repository
  3. Build packages in Docker containers

One source directory per repo

(Of course, you probably want to keep track of your source directory inside git, as the aports repository does.)

It’s best to follow the example of the Alpine project itself, even if this idea not explicitly written anywhere that I could find: the APK repository you’re building should have exactly one directory which contains the APKBUILD files for building packages. It’s OK to have multiple APK repos use the same git repo, as Alpine does with main, community, and testing, all in the aports repo.

On disk, that looks like:

/path/to/
    $apk_repo_name/
        $package_one/
            APKBUILD
            ... other files
        $package_two/
            APKBUILD
            ... other files

You can see my psyopsOS APK packages in the psyopsOS/abuild/ directory of my repo (master, 88c1457).

By contrast, avoid keeping an APKBUILD file in each project’s own directory or separate git repository. One reason for this is that abuild(1) assumes the APK repository name is the name of the package directory’s parent – $apk_repo_name above. There is no way to override this. (See the abuild source.)

You can hack around this by writing an APK build process that first copies the package directory to a temporary directory that has the APK repository name you want to use, but this is messy and can be confusing.

I’m still doing this hack in a script (master, a8b2aa5) for my progfiguration site package. The design of progfiguration’s progfigbuild module (master, 76db47b), in particular the way the ProgfigsitePythonPackagePreparer context manager works, forces me to keep it this way, I think. It expects to be able to have the site package generate a version number based on a date, and the APKBUILD file expects to be able to know the version number before calling any code that might use ProgfigsitePythonPackagePreparer, so I’m stuck. If I can figure out how to square this dependency circle, it would make the process for building an APK package from my progfigsite program much simpler.

One Alpine version per repo

Again following the Alpine Linux project’s example, generate a separate APK repository for each version of Alpine Linux you support, and use the major version name in the repository URL. For instance:

https://dl-cdn.alpinelinux.org/alpine/v3.18/main
https://you.example.com/whatever/v3.18/your-repo-name

If you’re generating packages designed to work on multiple Alpine versions, you might want to skip this, as it is annoying and adds complexity to your build setup. However, doing that prevents you from working around issues with just one Alpine version in the future, and if your packages are going to be relevant for multiple Alpine releases, the cost is worth it.

Build packages in Docker containers

The Alpine build tools must be run on Alpine Linux. This means you must either have a real Alpine Linux machine (physical or virtual), or some way of running Alpine userland like a chroot or Docker container.

I recommend Docker, because it can run on any system (including macOS and Windows), and its volumes make repeatable, fast builds easier than any other way I know about.

I use Docker volumes to:

  • Mount my own code into the container, without having a separate checkout
  • Mount the desired output directory into the container, without having to copy artifacts after the build is complete
  • Mount large git repositories maintained on the hosts into the container, avoiding a separate checkout
  • Keep a cache of all downloaded APK packages between runs so I don’t redownload the same thing from Alpine’s mirrors on every build
  • Mount single files into the container where the build scripts expect them to go, including the APK signing key (Docker volumes do not have to be directories)

Over time, I’ve grown a big Python context manager that I use in my build package, called AlpineDockerBuilder (master, 56b49d7). It makes starts the build container for me the same way every time.

Good luck

If you have any tips for running private Alpine package repos, I’d be interested to hear them.

Responses

Comments are hosted on this site and powered by Remark42 (thanks!).

Webmentions are hosted on remote sites and syndicated via Webmention.io (thanks!).