Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
M1cha committed Dec 22, 2023
0 parents commit a10cdbb
Show file tree
Hide file tree
Showing 8 changed files with 722 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/cache
/rootfs
339 changes: 339 additions & 0 deletions COPYING

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions Containerfile.base
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FROM localhost/archlinux-pacstrap AS pacstrap

RUN pacstrap -c -G -M /mnt base

FROM scratch
COPY --from=pacstrap /mnt /
36 changes: 36 additions & 0 deletions Containerfile.pacstrap
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM alpine:3.19 AS builder

RUN apk add --no-cache coreutils sequoia-sq wget

WORKDIR /tmp

RUN wget http://mirror.cmt.de/archlinux/iso/latest/b2sums.txt
RUN wget http://mirror.cmt.de/archlinux/iso/latest/sha256sums.txt
RUN wget http://mirror.cmt.de/archlinux/iso/latest/archlinux-bootstrap-x86_64.tar.gz
RUN wget http://mirror.cmt.de/archlinux/iso/latest/archlinux-bootstrap-x86_64.tar.gz.sig
RUN sq --force wkd get [email protected] -o release-key.pgp

# This might be pedantic given that the signature matches, but why not.
RUN b2sum --ignore-missing -c b2sums.txt
RUN sha256sum --ignore-missing -c sha256sums.txt
RUN sq verify --signer-file release-key.pgp --detached archlinux-bootstrap-x86_64.tar.gz.sig archlinux-bootstrap-x86_64.tar.gz

WORKDIR /

RUN mkdir /rootfs
RUN tar xzf /tmp/archlinux-bootstrap-x86_64.tar.gz --numeric-owner -C /rootfs

FROM scratch
COPY --from=builder /rootfs/root.x86_64 /

# The bootstrap image is very minimal and we still have to setup pacman.
RUN pacman-key --init
RUN pacman-key --populate
RUN echo 'Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch' > /etc/pacman.d/mirrorlist

# This allows us to use this image for committing as well.
RUN pacman --noconfirm -Syu ostree rsync

# This allows using pacstrap -N in a rootless container.
RUN echo 'root:1000:5000' > /etc/subuid
RUN echo 'root:1000:5000' > /etc/subgid
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# archlinux-ostree

This is a utility that simplifies building an ostree image using pacstrap.
84 changes: 84 additions & 0 deletions commit_rootfs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash

set -euo pipefail

rootfs="/mnt"

move_from_var() {
# This database must not be deleted, so we can modify it temporarily
# after running `ostree admin unlock`.
sed -i \
-e 's|^#\(DBPath\s*=\s*\).*|\1/usr/lib/pacman|g' \
-e 's|^#\(IgnoreGroup\s*=\s*\).*|\1modified|g' \
"$rootfs/etc/pacman.conf"
mv "$rootfs/var/lib/pacman" "$rootfs/usr/lib/"
}

move_to_var() {
mv "$rootfs/home" "$rootfs/var/"
ln -s var/home "$rootfs/home"

mv "$rootfs/mnt" "$rootfs/var/"
ln -s var/mnt "$rootfs/mnt"

# This is recommended by ostree but I don't see a good reason for it.
# rmdir "$rootfs/var/opt"
# mv "$rootfs/opt" "$rootfs/var/"
# ln -s var/opt "$rootfs/opt"

mv "$rootfs/root" "$rootfs/var/roothome"
ln -s var/roothome "$rootfs/root"

rm -r "${rootfs:?}/usr/local"
ln -s ../var/usrlocal "$rootfs/usr/local"

mv "$rootfs/srv" "$rootfs/var/srv"
ln -s var/srv "$rootfs/srv"

cp "/opt/ostree-0-integration.conf" "$rootfs/usr/lib/tmpfiles.d/"
}

clean_rootfs() {
# They'll be unused.
rm -r "${rootfs:?}/var/"*

# pacman leaves behind sockets which are not supported by ostree.
find "$rootfs" -type s -exec rm {} \;
}

ostreeify() {
# Those are required so the ostree tools can use them.
mkdir "$rootfs/sysroot"
ln -s sysroot/ostree "$rootfs/ostree"

# etc is handled by ostree and expected to be in /usr.
mv "$rootfs/etc" "$rootfs/usr/"

# ostree expects the initramfs in a different path.
# Also, we need to prepend microcode updates.
moduledir=$(find "$rootfs/usr/lib/modules" -mindepth 1 -maxdepth 1 -type d)
cat \
"$rootfs"/boot/*-ucode.img \
"$rootfs/boot/initramfs-linux-zen.img" \
> "$moduledir/initramfs.img"
}

lower_fstype="$(stat -f -c '%T' /mnt-lower)"
if [ "$lower_fstype" = "overlayfs" ]; then
echo "overlay detected, use rsync." >&2
rsync -a /mnt-lower/ /mnt
else
# Create a temporary overlay FS so none of our rootfs changes are permanent.
mkdir /overlay/{upper,work}
mount \
-t overlay \
-o 'lowerdir=/mnt-lower,upperdir=/overlay/upper,workdir=/overlay/work' \
overlay /mnt
fi

move_from_var
move_to_var
clean_rootfs
ostreeify

ostree commit --repo /sysroot/ostree/repo --tree=dir=/mnt "$@"
17 changes: 17 additions & 0 deletions ostree-0-integration.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
d /var/log/journal 0755 root root -
L /var/home - - - - ../sysroot/home
#d /var/opt 0755 root root -
d /var/srv 0755 root root -
d /var/roothome 0700 root root -
d /var/usrlocal 0755 root root -
d /var/usrlocal/bin 0755 root root -
d /var/usrlocal/etc 0755 root root -
d /var/usrlocal/games 0755 root root -
d /var/usrlocal/include 0755 root root -
d /var/usrlocal/lib 0755 root root -
d /var/usrlocal/man 0755 root root -
d /var/usrlocal/sbin 0755 root root -
d /var/usrlocal/share 0755 root root -
d /var/usrlocal/src 0755 root root -
d /var/mnt 0755 root root -
d /run/media 0755 root root -
235 changes: 235 additions & 0 deletions run
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
#!/usr/bin/env bash

set -euo pipefail

script=$(readlink -f "$0")
gitdir=$(dirname "$script")

pacstrap_tag="archlinux-pacstrap"
rootfs_tag="archlinux-ostree-base"
rootfs_containerfile="$gitdir/Containerfile.base"
rootfs_dir="$PWD/rootfs"
ostree_repo="/sysroot/ostree/repo"
podman_build_cache=false

# shellcheck disable=SC2059 # $1 and $2 can contain the printf modifiers
out() { printf "$1 $2\n" "${@:3}"; }
error() { out "==> ERROR:" "$@"; } >&2
warning() { out "==> WARNING:" "$@"; } >&2
msg() { out "==>" "$@"; }
die() { error "$@"; exit 1; }

arg_to_varname() {
name="${1:2}"
echo "${name//-/_}"
}

usage() {
cat <<EOF
usage: ${0##*/} [options] [command] [arg...]
<bool> is a value of 0, false, no, 1, true or yes.
\$gitdir: $gitdir
\$PWD: $PWD
Options:
--podman-build-cache <bool> Enable/Disable podman build cache. Boolean.
Default: no.
--pacman-cache <path> Path to a persistent pacman package cache.
Default: undefined, thus the pacman cache
is disabled.
--pacstrap-tag <name> Name of the tag to use for the pacstrap
container.
Default: archlinux-pacstrap.
--rootfs-tag <name> Name of the tag to use for the rootfs
container.
Default: archlinux-ostree-base.
--rootfs-containerfile <path> Path to the Containerfile used for building
the rootfs container.
Default: \$git_dir/Containerfile.base.
--rootfs-dir <path> Path to the directory where the rootfs will
be created in.
Default: \$PWD/rootfs.
--ostree-repo <path> Path to the ostree repository.
Default: /sysroot/ostree/repo.
--help Print this help message
Commands:
build_pacstrap_container (Re-)Build pacstrap container that's used
by the other commands and tag it with
--pacstrap-tag.
build_rootfs_container Build rootfs container from
--rootfs-containerfile and tag it with
--rootfs-tag.
enter_rootfs_container [arg...] Enter --rootfs-tag container. All changes
will be lost.
commit_rootfs_container [arg...] Commit --rootfs-tag container to
--ostree-repo. This tool provides --repo
and --tree-dir to ostree commit.
Everything else (like branch name) can and
has to be provided by you.
pacstrap_rootfs_directory [pkg...] Run pacstrap on --rootfs-dir. If the
directory exists, it will NOT be deleted.
[pkg...] will be passed to packstrap and
must contain the packages that shall be
installed.
enter_rootfs_directory [arg...] Enter --rootfs-dir. All changes will be
lost.
commit_rootfs_directory [arg...] Commit --rootfs-dir container to
--ostree-repo. This tool provides --repo
and --tree-dir to ostree commit.
Everything else (like branch name) can and
has to be provided by you.
EOF
}

long_opts='podman-build-cache:,pacman-cache:,pacstrap-tag:,rootfs-tag:,rootfs-containerfile:,rootfs-dir:,ostree-repo:,help'
if ! temp=$(getopt -o '' --long "$long_opts" -- "$@"); then
die "Invalid arguments"
fi

eval set -- "$temp"
while true; do
case "$1" in
'--pacman-cache'|\
'--pacstrap-tag'|\
'--rootfs-tag'|\
'--rootfs-container'|\
'--rootfs-dir'|\
'--ostree-repo')
name="$(arg_to_varname "$1")"
printf -v "$name" "%s" "$2"
shift 2
continue
;;
'--podman-build-cache')
name="$(arg_to_varname "$1")"

case "$2" in
'true'|'1'|'yes')
eval "$name=true"
;;
'false'|'0'|'no')
eval "$name=false"
;;
*)
die "Unsupported bool value: $2"
;;
esac

shift 2
continue
;;
'--help')
usage
exit $(( $# ? 0 : 1 ))
;;
'--')
shift
break
;;
*)
die "BUG: Unexpected argument: $1"
;;
esac
done

if [ -z ${1+x} ]; then
die "Missing command argument"
fi
command="${1}"
shift 1

pacman_cache_args=()
# shellcheck disable=SC2236 # This doesn't work with -n
if [ ! -z ${pacman_cache+x} ]; then
mkdir -p "$pacman_cache"
pacman_cache_args=(
-v "$pacman_cache:/var/cache/pacman/pkg"
)
fi

podman_build_cache_args=()
if [ "$podman_build_cache" = false ]; then
podman_build_cache_args+=(--no-cache)
fi

case "$command" in
'build_pacstrap_container')
podman build \
"${podman_build_cache_args[@]}" \
-f "$gitdir/Containerfile.pacstrap" \
-t "$pacstrap_tag"
;;

'build_rootfs_container')
podman build \
"${podman_build_cache_args[@]}" \
"${pacman_cache_args[@]}" \
--cap-add sys_admin \
--cap-add mknod \
-f "$rootfs_containerfile" \
-t "$rootfs_tag"
;;
'enter_rootfs_container')
podman run \
--rm -it \
"localhost/$rootfs_tag" \
"$@"
;;
'commit_rootfs_container')
podman run \
--cap-add sys_admin \
--security-opt apparmor=unconfined \
--mount "type=image,src=localhost/$rootfs_tag,dst=/mnt-lower" \
--mount "type=bind,src=$ostree_repo,dst=/sysroot/ostree/repo" \
-v "$PWD:/opt:ro" \
--rm -it \
"localhost/$pacstrap_tag" \
/opt/commit_rootfs "$@"
;;

'pacstrap_rootfs_directory')
if [ -d "$rootfs_dir" ]; then
warning "rootfs directory exists already."
else
mkdir -p "$rootfs_dir"
fi

podman run \
--cap-add sys_admin \
--cap-add mknod \
--security-opt apparmor=unconfined \
"${pacman_cache_args[@]}" \
-v "$rootfs_dir:/mnt" \
--rm -it \
"localhost/$pacstrap_tag" \
pacstrap -c -G -M /mnt "$@"
;;
'enter_rootfs_directory')
podman run \
--rm -it \
--rootfs "$rootfs_dir:O" \
/bin/bash "$@"
;;
'commit_rootfs_directory')
podman run \
--cap-add sys_admin \
--security-opt apparmor=unconfined \
--mount "type=bind,src=$rootfs_dir,dst=/mnt-lower,ro" \
--mount "type=bind,src=$ostree_repo,dst=/sysroot/ostree/repo" \
-v "$PWD:/opt:ro" \
--tmpfs /overlay \
--rm -it \
"localhost/$pacstrap_tag" \
/opt/commit_rootfs "$@"
;;

*)
die "Unsupported command: ${command}"
;;
esac

msg "Successful"

0 comments on commit a10cdbb

Please sign in to comment.