Establishing a packaging surroundings for Alpine Linux (introducing alpkg)

Not too long ago I’ve been eager about Alpine Linux and thought it will be good to keep up some Rust packages of their repositories. On this put up, I’ll share my notes/adventures on establishing a packaging surroundings and a instrument referred to as “alpkg” for automating this course of.
My first curiosity in Alpine Linux started once I first began to containerize my open supply initiatives utilizing light-weight Alpine containers. I particularly wish to comply with this method for Rust functions as a result of the sizes of conventional (glibc
) distro containers like Debian/Ubuntu can go as much as 200-300MB on account of bloat whereas Alpine (musl
) containers can keep so minimal similar to solely 3 MB!
Right here is an instance Dockerfile from rustypaste that ends in a 3MB image when constructed/compressed:
FROM rust:1.67.0-alpine3.17 as builder
WORKDIR /app
RUN apk replace
RUN apk add --no-cache musl-dev
COPY . .
RUN cargo construct --locked --release
RUN mkdir -p build-out/
RUN cp goal/launch/rustypaste build-out/
FROM scratch
WORKDIR /app
COPY --from=builder /app/build-out/rustypaste .
ENV SERVER__ADDRESS=0.0.0.0:8000
EXPOSE 8000
USER 1000:1000
CMD ["./rustypaste"]
One factor to notice right here, I particularly select scratch
picture because the runner since it’s tremendous minimal. You will get related outcomes with pictures like distroless
as nicely.
Though you have to take care of compiling with musl
typically, it’s well worth the problem when the result’s that satisfying. Different issues which might be completely different in Alpine are the next:
glibc ➔ musl
systemd ➔ OpenRC
GNU Core Utilities ➔ BusyBox
For extra details about Alpine, take a look at this post which works into element about why it is neat.
Now, let’s speak about how one can arrange a packaging surroundings for Alpine Linux.
Overview
In my particular case, I wish to preserve utilizing my Arch Linux system and likewise bundle for Alpine Linux. There are a few choices for doing that:
- Run Alpine Linux on a virtual machine
- Run Alpine Linux in a chroot
- Use a Docker/Podman container with persistent storage
I instantly eradicated the primary possibility since I did not need to take care of VM software program and thought it will add additional complexity to my setup.
Then I spun up a Docker container that runs Alpine and tried to make the storage persistent. After coming throughout this StackOverflow post, it turned out to be one thing tougher than I anticipated and I began to really feel like I used to be re-inventing VMs on account of all these mount-binds and permissions. Unsurprisingly sufficient, I ditched that concept as nicely.
Afterward, I got here throughout this great article on Alpine Wiki about making a chroot:
Contained in the chroot surroundings, you possibly can construct, debug, and run Alpine packages or develop issues. It is probably the most recognized approach to take action with out changing your system or utilizing a Digital Machine.
That is precisely what I wanted!
After deciding on what to make use of, I had a plan in thoughts and got here up with this diagram:

Let’s break it down:
- Every part is working inside Arch Linux.
- There’s a chroot container that holds the bundle sources and Alpine SDK instruments wanted.
- There’s a Git repository arrange outdoors the chroot for including/updating/deleting packages.
- For interacting with the repositories, we will merely ship a patch (i.e. merge request) to
aports
repositories which reside in gitlab.alpinelinux.org/alpine/aports. - We solely must create a GitLab account and fork the repository, no particular position is required!
- For interacting with the repositories, we will merely ship a patch (i.e. merge request) to
Establishing the chroot
alpine-chroot-install is a instrument that automates the guide steps of making a chroot. We will use it as follows:
$ alpine-chroot-install
-a x86_64 # structure
-d alpine # listing
-p build-base # set up build-base
-p alpine-sdk # set up alpine-sdk
In a few seconds, it should create a chroot and we will simply swap to it with the next script:
$ alpine/enter-chroot -u "$USER" <CMD>
And right here we’ve Alpine Linux working inside Arch Linux!
Story Time
After I created the Alpine chroot, I performed round a bit and tried out completely different options of apk
bundle supervisor. I put in a few of my favourite Rust instruments and every part was working easily.
Then I bought an thought: I ought to strive putting in these instruments in the course of the chroot set up. Fortunately, alpine-chroot-install
has an possibility for it and you may merely use -p <pkg>
for putting in packages.
In fact, I wished to take away the chroot listing I simply created earlier than creating one other chroot. For a second I assumed chroot was only a easy listing and tried to delete it with rm -rf
.
Ouch.
Nonetheless, chroot was not an everyday listing. It has a bunch of issues mounted to it:
$ alpine-chroot-install
# ...
> Binding filesystems into chroot
mount: none mounted on /alpine/proc.
mount: /sys sure on /alpine/sys.
mount: /dev sure on /alpine/dev.
So once I deleted chroot, I additionally deleted /dev
x_x
$ rm -rf /chroot
rm: WARNING: Round listing construction.
This nearly actually means that you've got a corrupted file system.
NOTIFY YOUR SYSTEM MANAGER.
Then every part began to fail:
$ ls
Did not open file to remap file descriptor (No such file or listing)
/dev/null
was additionally gone and I bought errors like “permission denied: /dev/null” as nicely. Enjoyable.
Fortunately a easy reboot fixes this situation. The scariest half was when every part began to throw errors left and proper, I panicked and realized what I did. I used to be afraid I rm -rf
‘d my complete system however fortunately it was simply the mount factors.
Lesson realized, I used the elimination script for deleting the chroot subsequent time: /chroot/destroy --remove
.
Establishing the packaging surroundings
Let’s set up the mandatory packages for packaging/improvement on Alpine:
$ apk add alpine-sdk atools
After that, we have to configure the construct defaults in /and so forth/abuild.conf
, particularly the packager data:
# PACKAGER and MAINTAINER are utilized by newapkbuild when creating new aports for
# the APKBUILD's "Contributor:" and "Maintainer:" feedback, respectively.
PACKAGER="Your Title <your@electronic mail.deal with>"
MAINTAINER="$PACKAGER"
Subsequent, we will configure the safety keys:
$ abuild-keygen --append --install
After this step, we at the moment are prepared for making ready APKBUILDs
in keeping with this guide and construct them through abuild -r
.
Establishing the repository
After forking the aports repository on GitLab, we will clone it someplace on our principal system and configure Git in keeping with the packager data we’ve supplied earlier in /and so forth/abuild.conf
:
$ git clone https://gitlab.alpinelinux.org/<consumer>/aports
$ git config --global consumer.title "Your Title"
$ git config --global consumer.electronic mail "your@electronic mail.deal with"
Official documentation recommends including the next Git hook for routinely producing the commit message primarily based on the bundle that’s being dedicated:
$ cat <<-'_EOF_' >".git/hooks/prepare-commit-msg"
#!/bin/sh
case "$2,$3" in
,|template,)
if git diff-index --diff-filter=A --name-only --cached HEAD
| grep -q '/APKBUILD$'; then
meta() sed 's/.*="?//;s/"$//';
printf 'testing/%s: new aportnnpercentsnpercentsn' "$(meta pkgname)"
"$(meta url)" "$(meta pkgdesc)" "$(cat $1)" > "$1"
else
printf '%snnpercents' `git diff-index --name-only --cached HEAD
| sed -n 's//APKBUILD$//p;q'` "$(cat $1)" > "$1"
fi;;
esac
_EOF_
chmod +x ".git/hooks/prepare-commit-msg"
This hook will end in producing commit messages similar to:
testing/git-cliff: new aport
https://github.com/orhun/git-cliff
A extremely customizable changelog generator
testing/
Good.
Creating packages
Alpine Linux has a handy instrument referred to as newapkbuild
for producing APKBUILD
prototypes primarily based on the given parameters:
$ newapkbuild -h
newapkbuild 3.10.0-r0 - generate a brand new APKBUILD
Utilization: newapkbuild [-n PKGNAME] [-d PKGDESC] [-l LICENSE] [-u URL]
[-a | -C | -m | -p | -y | -r] [-s] [-c] [-f] [-h]
PKGNAME[-PKGVER] | SRCURL
Choices:
-n Set bundle title to PKGNAME (solely use with SRCURL)
-d Set bundle description to PKGDESC
-l Set bundle license to LICENSE, use identifiers from:
<https://spdx.org/licenses/>
-u Set bundle URL
-a Create autotools bundle (use ./configure ...)
-C Create CMake bundle (Assume cmake/ is there)
-m Create meson bundle (Assume meson.construct is there)
-p Create perl bundle (Assume Makefile.PL is there)
-y Create python bundle (Assume setup.py is there)
-r Create rust bundle (Assume Cargo.toml is there)
-s Use sourceforge supply URL
-c Copy a pattern init.d, conf.d, and set up script
-f Pressure even when listing already exists
-h Present this assist
It’s particularly helpful if you happen to do not need to write the identical boilerplate capabilities over and over.
It may be used for Rust packages as follows:
$ newapkbuild -r
-u "https://github.com/orhun/git-cliff"
-d "A extremely customizable changelog generator"
-l "GPL-3.0-only"
"git-cliff"
It will generate the next APKBUILD
in git-cliff
listing:
# Contributor: Your Title <your@electronic mail.deal with>
# Maintainer: Your Title <your@electronic mail.deal with>
pkgname=git-cliff
pkgver=
pkgrel=0
pkgdesc="A extremely customizable changelog generator"
url="https://github.com/orhun/git-cliff"
arch="all"
license="GPL-3.0-only"
relies upon=""
makedepends="cargo"
checkdepends=""
set up=""
subpackages="$pkgname-dev $pkgname-doc"
supply=""
builddir="$srcdir/"
put together() {
default_prepare
cargo fetch --locked
}
construct() {
cargo construct --frozen --release
}
verify() {
cargo take a look at --frozen
}
bundle() {
cargo set up --frozen --offline --path . --root="$pkgdir/usr"
rm "$pkgdir"/usr/.crates*
}
You’ll be able to learn extra about APKBUILD
capabilities/variables within the official reference. With some edits, we will match our venture into this template simply. Right here is the ultimate APKBUILD
:
# Contributor: Orhun Parmaksız <orhunparmaksiz@gmail.com>
# Maintainer: Orhun Parmaksız <orhunparmaksiz@gmail.com>
pkgname=git-cliff
pkgver=1.1.2
pkgrel=0
pkgdesc="A extremely customizable changelog generator"
url="https://github.com/orhun/git-cliff"
# s390x, ppc64le, riscv64: blocked by ring crate
arch="all !s390x !ppc64le !riscv64"
license="GPL-3.0-or-later"
makedepends="
cargo
libgit2-dev
"
subpackages="
$pkgname-doc
$pkgname-bash-completion
$pkgname-zsh-completion
$pkgname-fish-completion
"
choices="internet"
supply="$pkgname-$pkgver.tar.gz::https://github.com/orhun/git-cliff/archive/v$pkgver.tar.gz"
put together() {
default_prepare
cargo fetch --target="$CTARGET" --locked
}
construct() {
cargo construct --frozen --release
mkdir -p man
OUT_DIR=man/ "./goal/launch/$pkgname-mangen"
mkdir -p completions
OUT_DIR=completions/ "./goal/launch/$pkgname-completions"
}
verify() {
cargo take a look at --frozen -- --skip "git_log"
}
bundle() {
set up -Dm 755 "goal/launch/$pkgname" -t "$pkgdir/usr/bin"
set up -Dm 644 README.md -t "$pkgdir/usr/share/doc/$pkgname"
set up -Dm 644 "man/$pkgname.1" -t "$pkgdir/usr/share/man/man1"
set up -Dm 644 "completions/$pkgname.bash" "$pkgdir/usr/share/bash-completion/completions/$pkgname"
set up -Dm 644 "completions/$pkgname.fish" -t "$pkgdir/usr/share/fish/completions"
set up -Dm 644 "completions/_$pkgname" -t "$pkgdir/usr/share/zsh/site-functions"
}
sha512sums="
f5564f1d6d492ea6527f2ac10eaa1dc90aa1846fb9b090224ff7a2c1cad78d8850a13364c5e4beae987c4ebf65891e804e0677fd9ab193e56d9565292d6cf2ba git-cliff-1.1.2.tar.gz
"
After we’ve the APKBUILD
, we will use the next instructions.
To generate checksums:
$ abuild checksum
To construct:
$ abuild -r
To lint:
$ apkbuild-lint APKBUILD
After the bundle is efficiently constructed, there will probably be an apk
file within the $HOME/packages
listing. It’s potential to listing the apk
contents with the next command:
$ tar tvvf git-cliff-1.1.2-r0.apk
-rw-r--r-- 0/0 512 2023-03-22 19:08 .SIGN.RSA.orhunparmaksiz@gmail.com-641b3a67.rsa.pub
-rw-r--r-- root/root 754 2023-03-22 19:08 .PKGINFO
drwxr-xr-x root/root 0 2023-03-22 19:08 usr/
drwxr-xr-x root/root 0 2023-03-22 19:08 usr/bin/
-rwxr-xr-x root/root 7064056 2023-03-22 19:08 usr/bin/git-cliff
-rwxr-xr-x root/root 469160 2023-03-22 19:08 usr/bin/git-cliff-completions
-rwxr-xr-x root/root 440488 2023-03-22 19:08 usr/bin/git-cliff-mangen
To put in the regionally constructed bundle, we will replace the repository index (/and so forth/apk/repositories
) to level to the native listing and set up it through apk
:
$ cat /and so forth/apk/repositories
/residence/orhun/packages/orhun/
http://dl-cdn.alpinelinux.org/alpine/latest-stable/principal
http://dl-cdn.alpinelinux.org/alpine/latest-stable/neighborhood
$ apk add git-cliff
If every part works effective, then congratulations, you simply constructed your first Alpine bundle!
Submitting patches
Alpine Linux has 3 repositories:
principal
: Immediately supported official packages that are maintained by the Alpine core group.
- Much like
core
/additional
repositories in Arch Linux.
neighborhood
: Packages which might be created by the contributors and builders. Not totally supported, upkeep depends on the contributor exercise.
- Identical as
neighborhood
repository on Arch Linux.
testing
: New packages which might be added by contributors. Packages from this repository are accepted into theneighborhood
repository. This repository is simply obtainable on edge (improvement) department of Alpine.
- Much like the AUR /
testing
repositories on Arch Linux.
Since we’ve simply created a new bundle, it should go to the testing
repository. We will merely commit testing/<bundle>/APKBUILD
after which create a merge request on GitLab.
$ cd aports/
$ git pull
$ git checkout -b aport/git-cliff
$ mkdir -p testing/git-cliff
$ cp /chroot/residence/orhun/git-cliff/APKBUILD testing/git-cliff/
$ git add testing/git-cliff
$ git commit
$ git push
And there we go: https://gitlab.alpinelinux.org/alpine/aports/-/merge_requests/45319
After the merge request is accredited/merged, our bundle will present up on https://pkgs.alpinelinux.org:
Yay! git-cliff
is now obtainable for Alpine Linux!
Automating (principally) every part with alpkg ????️
⭐ GitHub: https://github.com/orhun/alpkg
alpkg
can create a chroot with preinstalled instruments in a matter of seconds, arrange aports repository, and fetch/replace packages. Most significantly, it supplies a break up format through Zellij for straightforward modifying/constructingAPKBUILD
information.
alpkg
does every part that’s talked about earlier on this put up and extra. For instance, within the GIF above:
- an Alpine chroot is created. (
alpkg init
) - an present
APKBUILD
is fetched. (alpkg fetch
) APKBUILD
is edited and constructed. (alpkg edit
)- adjustments are dedicated to
aports
. (alpkg replace
)
Let’s go over these options.
Making a chroot is as simple as working alpkg init
. It additionally installs the SDK instruments that we’d like and units up the aports
repository for us.
We will fetch and edit a APKBUILD
through alpkg fetch
. It should present a break up format for each modifying and different operations similar to working abuild -r
.
To create a brand new APKBUILD
, we will merely use alpkg edit
.
Lastly, if we need to commit the adjustments to aports
, we will run alpkg replace
.
You will get extra details about the instrument and see detailed utilization examples within the repository.
Endnote
Alpine Linux is neat. I am actually wanting ahead to oxidizing it (add extra Rust packages to their repositories) and studying extra about their implementation decisions to ultimately do extra improvement. I am glad how alpkg
turned out and I am planning to enhance it primarily based on my wants and the suggestions from the Alpine neighborhood. I like automating issues.
Hope you loved studying and see you within the subsequent one!
$ docker run alpine echo "안녕히 가세요"