1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
#!/bin/sh
# arch-repo -- build the Arch package and fold it into the pacman repository
# slosh.foo serves at /arch/. AUR signups are closed (supply chain attacks),
# so the repo is self-hosted: the same PKGBUILD, published two ways -- as a
# file people can makepkg themselves, and as built packages pacman can sync.
#
# contrib/arch-repo # build + sign + index, all architectures
# ZIG=/path/to/zig contrib/arch-repo # where zig is not /usr/bin/zig
# SLOSH_ARCHES=x86_64 contrib/arch-repo # just the one
#
# The output is dist/arch/<carch>/ per architecture: the package and its
# .sig, and the slosh.db / slosh.files indexes repo-add maintains, also
# signed. Publishing is uploading those directories to /opt/slosh/arch/ on
# the box (the script prints the rsync). makepkg itself does not
# cross-compile, but zig does -- the foreign leg is a substitute
# makepkg.conf whose CARCH names the target, and the PKGBUILD hands zig the
# matching -target triple. So both packages come off the same x86_64 box.
#
# Everything is signed with the key below -- the one whose public half is
# contrib/arch/slosh.asc, served at https://slosh.foo/arch/slosh.asc, and
# whose fingerprint the README tells users to check. Pinned by fingerprint
# here so a machine with other keys in its ring cannot sign with the wrong
# one by default.
KEY="${SLOSH_GPG_KEY:-0F67C909938CFFA8A76B59FD8D69B55A0E9056B9}"
set -eu
root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
cd "$root"
fail() { printf 'arch-repo: %s\n' "$*" >&2; exit 1; }
command -v makepkg >/dev/null 2>&1 || fail "makepkg not found -- this runs on an Arch machine"
# The build needs zig, but not necessarily pacman's: a zig on PATH (the
# release box keeps it in ~/zig-<version>) serves, passed to the PKGBUILD as
# $ZIG. Exported, because makepkg is a child and a plain sh variable is not.
if [ ! -x /usr/bin/zig ] && [ -z "${ZIG:-}" ] && command -v zig >/dev/null 2>&1; then
ZIG="$(command -v zig)"
fi
export ZIG
command -v repo-add >/dev/null 2>&1 || fail "repo-add not found (pacman ships it)"
gpg --list-secret-keys "$KEY" >/dev/null 2>&1 \
|| fail "no secret key $KEY in this keyring -- the repo is signed, and an
unsigned update would make every enrolled pacman refuse it. Import the
signing key, or SLOSH_GPG_KEY=<fingerprint> for a successor key."
ARCHES="${SLOSH_ARCHES:-x86_64 aarch64}"
work="$root/build/arch-pkg"
# -f: rebuilding the same pkgver-pkgrel is the common case while testing.
# -d when zig comes from $ZIG rather than pacman, since only the dependency
# check would object, and the build itself knows what it needs.
depflag=""
[ -x /usr/bin/zig ] || { [ -n "${ZIG:-}" ] && depflag="-d"; }
for carch in $ARCHES; do
out="$root/dist/arch/$carch"
# makepkg builds in a copy, so a failed or dirty build cannot leave
# droppings next to the PKGBUILD that later get committed.
rm -rf "$work"
mkdir -p "$work" "$out"
cp contrib/arch/PKGBUILD "$work/"
# CARCH is assigned unconditionally in /etc/makepkg.conf, so an env var
# cannot reach makepkg -- a substitute conf can. Everything else in it is
# kept; only the architecture line changes.
conf=""
if [ "$carch" != "$(uname -m)" ]; then
conf="$work/makepkg-$carch.conf"
sed "s/^CARCH=.*/CARCH=\"$carch\"/" /etc/makepkg.conf > "$conf"
fi
( cd "$work" && PKGDEST="$out" env ${conf:+MAKEPKG_CONF="$conf"} \
makepkg -f $depflag --sign --key "$KEY" )
# The index is regenerated from what is actually in the directory, every
# time. repo-add refuses to touch an entry whose version it already has --
# so after a same-version rebuild (new bytes, new signature) an
# incremental add would leave the db describing the *previous* package,
# and every client would see a checksum mismatch. Rebuilding the index
# from disk cannot disagree with disk. --verify checks the package
# signatures just made; --sign signs the db, so a tampered index is as
# loud as a tampered package.
rm -f "$out"/slosh.db* "$out"/slosh.files*
repo-add --verify --sign --key "$KEY" "$out/slosh.db.tar.gz" "$out"/*.pkg.tar.zst
done
# The public half rides along at the repo root, so the enrollment the README
# describes has a URL to fetch, and the file in git to compare it against.
cp contrib/arch/slosh.asc "$root/dist/arch/slosh.asc"
echo ""
echo "arch-repo: dist/arch/{$(echo $ARCHES | tr ' ' ',')} is the repository. To publish:"
for carch in $ARCHES; do
echo " rsync -av --delete --chown=root:root $root/dist/arch/$carch/ root@sl0p.foo:/opt/slosh/arch/$carch/"
done
echo " rsync -a --chown=root:root $root/dist/arch/slosh.asc root@sl0p.foo:/opt/slosh/arch/slosh.asc"
|