A daily job keeps the tools behind the deb and rpm repositories current
Build / Desktop, server and tools (push) Successful in 1m28s
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:
1 parent
10a85e6bc5
commit
e112c1dc1e
3 files changed
+69
-6
No files matched your search
@@ -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
|
||||||
|
if [ -n "$version" ]; then
|
||||||
|
releases=$(pwd)/releases/$version
|
||||||
deb=$releases/MeWeb-$version-linux-x64.deb
|
deb=$releases/MeWeb-$version-linux-x64.deb
|
||||||
rpm=$releases/MeWeb-$version-linux-x64.rpm
|
rpm=$releases/MeWeb-$version-linux-x64.rpm
|
||||||
test -f "$deb"
|
test -f "$deb"
|
||||||
test -f "$rpm"
|
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,6 +34,7 @@ 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"
|
||||||
|
|
||||||
|
if [ -n "$version" ]; then
|
||||||
rpmsign --define "__gpg /usr/bin/gpg" --define "_gpg_name $fingerprint" --define "_gpg_path $keys" --addsign "$rpm" >/dev/null
|
rpmsign --define "__gpg /usr/bin/gpg" --define "_gpg_name $fingerprint" --define "_gpg_path $keys" --addsign "$rpm" >/dev/null
|
||||||
rpmkeys --dbpath "$keys/rpmdb" --import "$keys/public.asc"
|
rpmkeys --dbpath "$keys/rpmdb" --import "$keys/public.asc"
|
||||||
rpmkeys --dbpath "$keys/rpmdb" -K "$rpm" | grep -q 'digests signatures OK'
|
rpmkeys --dbpath "$keys/rpmdb" -K "$rpm" | grep -q 'digests signatures OK'
|
||||||
@@ -31,8 +45,9 @@ 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"
|
||||||
|
if [ -n "$version" ]; then
|
||||||
rm -f "$snap/apt/pool/main/m/meweb/$debname" "$snap/rpm/meweb/$rpmname"
|
rm -f "$snap/apt/pool/main/m/meweb/$debname" "$snap/rpm/meweb/$rpmname"
|
||||||
cp "$deb" "$snap/apt/pool/main/m/meweb/$debname"
|
cp "$deb" "$snap/apt/pool/main/m/meweb/$debname"
|
||||||
cp "$rpm" "$snap/rpm/meweb/$rpmname"
|
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"
|
||||||
|
|
||||||
|
if [ -n "$version" ]; then
|
||||||
(cd "$releases" && for file in $(ls | LC_ALL=C sort); do [ "$file" = SHA256SUMS.txt ] || sha256sum "$file"; done) > "$keys/sums"
|
(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"
|
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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user