A daily job keeps the tools behind the deb and rpm repositories current
Build / Desktop, server and tools (push) Successful in 1m28s

The box updates the tools that build packages.arasaka.software every morning, and
packages.yml rebuilds and re-signs the apt and rpm repositories when those tools change,
so a broken update shows up the same morning, not at the next release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T9uhaCTSycTh1r8At679Pz
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-31 22:52:24 +01:00
1 parent 10a85e6bc5
commit e112c1dc1e
3 files changed
+87 -24

No files matched your search

+48 -23
View File
@@ -1,18 +1,31 @@
#!/bin/sh #!/bin/sh
set -eu set -eu
version=$1 version=${1:-}
releases=$(pwd)/releases/$version
root=${PACKAGES_ROOT:-/var/www/packages.arasaka.software} root=${PACKAGES_ROOT:-/var/www/packages.arasaka.software}
keep=${PACKAGES_KEEP:-5} keep=${PACKAGES_KEEP:-5}
url=https://packages.arasaka.software url=https://packages.arasaka.software
fingerprint=F92C166E9B1C13EDD1629E76888DCF969D133FB2 fingerprint=F92C166E9B1C13EDD1629E76888DCF969D133FB2
deb=$releases/MeWeb-$version-linux-x64.deb if [ -n "$version" ]; then
rpm=$releases/MeWeb-$version-linux-x64.rpm releases=$(pwd)/releases/$version
test -f "$deb" deb=$releases/MeWeb-$version-linux-x64.deb
test -f "$rpm" rpm=$releases/MeWeb-$version-linux-x64.rpm
test -f "$deb"
test -f "$rpm"
elif [ ! -e "$root/apt" ] || [ ! -e "$root/rpm" ]; then
echo "no repositories at $root yet: the first release makes them"
exit 0
fi
tools=$(dpkg-query -W -f '${Package} ${Version}\n' apt-utils createrepo-c dpkg gnupg rpm)
named=$(echo "$tools" | paste -sd, - | sed 's/,/, /g')
stamp=$( { echo "$tools"; cat "$0"; } | sha256sum | cut -d' ' -f1)
exec 9>>"$root/.lock" exec 9>>"$root/.lock"
flock -w 2700 9 flock -w 2700 9
if [ -z "$version" ] && [ "$(cat "$(readlink -f "$root/apt")/../built-from" 2>/dev/null)" = "$stamp" ]; then
echo "nothing new: the repositories are already built with $named"
exit 0
fi
echo "$named"
keys=$(mktemp -d) keys=$(mktemp -d)
trap 'rm -rf "$keys"' EXIT trap 'rm -rf "$keys"' EXIT
@@ -21,18 +34,20 @@ printf '%s\n' "$PACKAGES_SIGNING_KEY" | gpg --batch --quiet --import
gpg --batch --with-colons --list-secret-keys | grep -q "^fpr:::::::::$fingerprint:$" gpg --batch --with-colons --list-secret-keys | grep -q "^fpr:::::::::$fingerprint:$"
gpg --batch --armor --export "$fingerprint" > "$keys/public.asc" gpg --batch --armor --export "$fingerprint" > "$keys/public.asc"
rpmsign --define "__gpg /usr/bin/gpg" --define "_gpg_name $fingerprint" --define "_gpg_path $keys" --addsign "$rpm" >/dev/null if [ -n "$version" ]; then
rpmkeys --dbpath "$keys/rpmdb" --import "$keys/public.asc" rpmsign --define "__gpg /usr/bin/gpg" --define "_gpg_name $fingerprint" --define "_gpg_path $keys" --addsign "$rpm" >/dev/null
rpmkeys --dbpath "$keys/rpmdb" -K "$rpm" | grep -q 'digests signatures OK' rpmkeys --dbpath "$keys/rpmdb" --import "$keys/public.asc"
rpmkeys --dbpath "$keys/rpmdb" -K "$rpm" | grep -q 'digests signatures OK'
full=$(dpkg-deb -f "$deb" Version) full=$(dpkg-deb -f "$deb" Version)
test "$(dpkg-deb -f "$deb" Package)" = meweb test "$(dpkg-deb -f "$deb" Package)" = meweb
test "${full%-*}" = "$version" test "${full%-*}" = "$version"
debname=meweb_${full}_$(dpkg-deb -f "$deb" Architecture).deb debname=meweb_${full}_$(dpkg-deb -f "$deb" Architecture).deb
rpmname=$(rpm -qp --nosignature --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm' "$rpm") rpmname=$(rpm -qp --nosignature --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm' "$rpm")
test "$(rpm -qp --nosignature --qf '%{NAME} %{VERSION}' "$rpm")" = "meweb $version" test "$(rpm -qp --nosignature --qf '%{NAME} %{VERSION}' "$rpm")" = "meweb $version"
fi
snap=$root/.snap/$(date -u +%Y%m%d%H%M%S)-$version snap=$root/.snap/$(date -u +%Y%m%d%H%M%S)-${version:-tools}
mkdir -p "$snap/apt/pool/main" "$snap/rpm" mkdir -p "$snap/apt/pool/main" "$snap/rpm"
if [ -d "$root/apt/pool/main" ]; then cp -al "$root/apt/pool/main/." "$snap/apt/pool/main/"; fi if [ -d "$root/apt/pool/main" ]; then cp -al "$root/apt/pool/main/." "$snap/apt/pool/main/"; fi
if [ -d "$root/rpm" ]; then if [ -d "$root/rpm" ]; then
@@ -41,14 +56,21 @@ if [ -d "$root/rpm" ]; then
done done
fi fi
mkdir -p "$snap/apt/pool/main/m/meweb" "$snap/rpm/meweb" mkdir -p "$snap/apt/pool/main/m/meweb" "$snap/rpm/meweb"
rm -f "$snap/apt/pool/main/m/meweb/$debname" "$snap/rpm/meweb/$rpmname" if [ -n "$version" ]; then
cp "$deb" "$snap/apt/pool/main/m/meweb/$debname" rm -f "$snap/apt/pool/main/m/meweb/$debname" "$snap/rpm/meweb/$rpmname"
cp "$rpm" "$snap/rpm/meweb/$rpmname" cp "$deb" "$snap/apt/pool/main/m/meweb/$debname"
cp "$rpm" "$snap/rpm/meweb/$rpmname"
fi
for folder in "$snap/apt/pool/main/m/meweb" "$snap/rpm/meweb"; do for folder in "$snap/apt/pool/main/m/meweb" "$snap/rpm/meweb"; do
ls "$folder" | sed -E 's/^meweb[_-]([0-9.]+)-.*/\1/' | sort -uV | head -n "-$keep" | while read -r old; do ls "$folder" | sed -E 's/^meweb[_-]([0-9.]+)-.*/\1/' | sort -uV | head -n "-$keep" | while read -r old; do
rm -f "$folder/meweb_$old-"* "$folder/meweb-$old-"* rm -f "$folder/meweb_$old-"* "$folder/meweb-$old-"*
done done
done done
if [ -z "$version" ]; then
debname=$(ls "$snap/apt/pool/main/m/meweb" | sort -V | tail -n 1)
rpmname=$(ls "$snap/rpm/meweb" | sort -V | tail -n 1)
full=$(dpkg-deb -f "$snap/apt/pool/main/m/meweb/$debname" Version)
fi
binary=$snap/apt/dists/stable/main/binary-amd64 binary=$snap/apt/dists/stable/main/binary-amd64
mkdir -p "$binary" mkdir -p "$binary"
@@ -74,6 +96,7 @@ if [ -f "$root/rpm/repodata/repomd.xml" ]; then
done done
fi fi
chmod -R u=rwX,go=rX "$snap" chmod -R u=rwX,go=rX "$snap"
echo "$stamp" > "$snap/built-from"
for part in apt rpm; do for part in apt rpm; do
ln -sfn ".snap/$(basename "$snap")/$part" "$root/$part.new" ln -sfn ".snap/$(basename "$snap")/$part" "$root/$part.new"
@@ -82,10 +105,12 @@ done
ls -1d "$root"/.snap/* | sort | head -n -3 | while read -r old; do ls -1d "$root"/.snap/* | sort | head -n -3 | while read -r old; do
find "$old" -maxdepth 0 -mmin +1440 -exec rm -rf {} + find "$old" -maxdepth 0 -mmin +1440 -exec rm -rf {} +
done done
echo "published $debname and $rpmname in $root" echo "published $root/apt and $root/rpm, newest $debname and $rpmname"
(cd "$releases" && for file in $(ls | LC_ALL=C sort); do [ "$file" = SHA256SUMS.txt ] || sha256sum "$file"; done) > "$keys/sums" if [ -n "$version" ]; then
mv "$keys/sums" "$releases/SHA256SUMS.txt" (cd "$releases" && for file in $(ls | LC_ALL=C sort); do [ "$file" = SHA256SUMS.txt ] || sha256sum "$file"; done) > "$keys/sums"
mv "$keys/sums" "$releases/SHA256SUMS.txt"
fi
if [ "$root" = /var/www/packages.arasaka.software ]; then if [ "$root" = /var/www/packages.arasaka.software ]; then
curl -fsS "$url/apt/dists/stable/main/binary-amd64/Packages" | grep -qx "Version: $full" curl -fsS "$url/apt/dists/stable/main/binary-amd64/Packages" | grep -qx "Version: $full"
@@ -95,5 +120,5 @@ if [ "$root" = /var/www/packages.arasaka.software ]; then
curl -fsS "$url/rpm/repodata/repomd.xml.asc" > "$keys/repomd.xml.asc" curl -fsS "$url/rpm/repodata/repomd.xml.asc" > "$keys/repomd.xml.asc"
gpg --batch --quiet --verify "$keys/repomd.xml.asc" "$keys/repomd.xml" gpg --batch --quiet --verify "$keys/repomd.xml.asc" "$keys/repomd.xml"
curl -fsSI "$url/rpm/meweb/$rpmname" > /dev/null curl -fsSI "$url/rpm/meweb/$rpmname" > /dev/null
echo "live at $url: meweb $version" echo "live at $url: meweb $full"
fi fi
+24
View File
@@ -0,0 +1,24 @@
name: Package tools
on:
schedule:
- cron: '43 5 * * *'
workflow_dispatch:
jobs:
refresh:
name: The box's updated package tools, tried on the apt and rpm repositories
runs-on: nuvola
steps:
- uses: actions/checkout@v4
- name: Check that the box brought the tools up to date this morning
run: |
systemctl show packages-tools -p Result -p ExecMainExitTimestamp
systemctl is-active --quiet packages-tools.timer
test "$(systemctl show packages-tools -p Result --value)" = success
- name: Rebuild the apt and rpm repositories on packages.arasaka.software when the tools changed
env:
PACKAGES_SIGNING_KEY: ${{ secrets.PACKAGES_SIGNING_KEY }}
run: .gitea/scripts/packages.sh
+15 -1
View File
@@ -45,7 +45,8 @@ is root-owned, so CI uses the runner user's own `~/.dotnet` (passolo's `tools/re
`build.yml` runs on every push to `master`; `release.yml` on a `v*` tag (publishes the Gitea release) or by hand `build.yml` runs on every push to `master`; `release.yml` on a `v*` tag (publishes the Gitea release) or by hand
(`publish: false` builds every platform and deploys the server only); `fdroid.yml` daily at 05:23 UTC or by hand (`publish: false` builds every platform and deploys the server only); `fdroid.yml` daily at 05:23 UTC or by hand
(the F-Droid tools, below). All three run on the Arasaka org runner `nuvola`, (the F-Droid tools, below); `packages.yml` daily at 05:43 UTC or by hand (the deb and rpm tools, below). All four
run on the Arasaka org runner `nuvola`,
user `gitea-runner`, home `/var/lib/gitea-runner`: `tools/sdk.sh` installs the SDK from `global.json` into its user `gitea-runner`, home `/var/lib/gitea-runner`: `tools/sdk.sh` installs the SDK from `global.json` into its
`~/.dotnet` (the system SDK in `/opt/dotnet` is older and root-owned), and the JDK, nfpm and the Android SDK are `~/.dotnet` (the system SDK in `/opt/dotnet` is older and root-owned), and the JDK, nfpm and the Android SDK are
cached in `~/.cache/meweb`. There is deliberately no `pull_request` trigger: the runner is the production box. cached in `~/.cache/meweb`. There is deliberately no `pull_request` trigger: the runner is the production box.
@@ -174,6 +175,19 @@ The Android release key: alias `meweb`, certificate SHA-256
`arasaka_repository_known`, or installed copies are treated as edited by hand and left alone. The package owns `arasaka_repository_known`, or installed copies are treated as edited by hand and left alone. The package owns
`/usr/share/arasaka-repository/meweb`, the marker that keeps the repository configured while any Arasaka package `/usr/share/arasaka-repository/meweb`, the marker that keeps the repository configured while any Arasaka package
is installed. The signing key's only copies are that secret and the owner's Nextcloud backup. is installed. The signing key's only copies are that secret and the owner's Nextcloud backup.
**The tools that build these two repositories are the box's own Ubuntu packages** (`apt-utils` for
`apt-ftparchive`, `createrepo-c`, `dpkg`, `gnupg`, `rpm`), and **a daily job keeps them current** (owner, 31 Oct
2026: the same daily update as the F-Droid tools). The runner cannot install packages, so the update is a root
timer on the box, `packages-tools.timer` at 05:13 UTC (source and setup in arasaka.software,
`deploy/packages.arasaka.software/`). It upgrades only those packages, from Ubuntu's updates and security
fixes, with package lists of its own, so it never brings the rest of the box forward. There is no two-week wait,
as there is for fdroidserver: Ubuntu reviews and signs these updates itself. `packages.yml` follows at 05:43 UTC. It fails if
the morning's update failed (it reads the result with `systemctl show`, which needs no rights), then runs this
script with no version. When the tools' versions or this script differ from what the live repositories were built
from (`built-from` beside each snapshot), it rebuilds and re-signs both from the current pools with no new package,
swaps the links and checks the live site; otherwise it says "nothing new" and names the versions. Unlike
fdroidserver, these tools cannot be kept beside an older copy, so a release uses whatever is installed; an update
that breaks them fails the next morning's job first.
- **The APK reaches an F-Droid repository on packages.arasaka.software from the same job** (31 Oct 2026), after the - **The APK reaches an F-Droid repository on packages.arasaka.software from the same job** (31 Oct 2026), after the
release is published, so the repository can lag the release page but never lead it. `.gitea/scripts/fdroid.sh` release is published, so the repository can lag the release page but never lead it. `.gitea/scripts/fdroid.sh`
installs fdroidserver with uv from `setup.json` (its download cache in `tools/fdroid`, cached like the JDK), because installs fdroidserver with uv from `setup.json` (its download cache in `tools/fdroid`, cached like the JDK), because