#!/usr/bin/env bash set -euo pipefail shopt -s nullglob # ---- Configurable Variables ---- repoDir="/var/www/packages.stormux.org" keyId="52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82" repoName="stormux" dbName="${repoName}.db.tar.gz" filesName="${repoName}.files.tar.gz" rebuildDb="${REBUILD_DB:-false}" require_cmd() { local cmd="$1" if ! command -v "$cmd" >/dev/null 2>&1; then echo "โŒ Required command not found: $cmd" exit 1 fi } # ---- Safety Checks ---- if [[ ! -d "$repoDir" ]]; then echo "โŒ Repo dir does not exist: $repoDir" exit 1 fi require_cmd "gpg" require_cmd "repo-add" require_cmd "repo-remove" # ---- Create Architecture Directories ---- mkdir -p "$repoDir/x86_64" "$repoDir/aarch64" # ---- Process Each Architecture ---- process_arch() { local arch="$1" local archDir="$repoDir/$arch" local pkgFiles=() local selectedPkgFiles=() local repoAddArgs=() local -A currentPkgNames=() local -A newestPkgByName=() local repoPkgNames=() local dbFile="$dbName" local filesFile="$filesName" local dbSig="${dbFile}.sig" local filesSig="${filesFile}.sig" local dbLink="${repoName}.db" local filesLink="${repoName}.files" echo "๐Ÿ—๏ธ Processing $arch packages..." # Enter arch directory (packages should already be sorted by update.sh) cd "$archDir" || return 1 # Select only the newest archive for each package name. repo-add cannot # safely consume multiple versions of the same package in a single run. pkgFiles=( *.pkg.tar.zst *.pkg.tar.xz ) for pkg in "${pkgFiles[@]}"; do local pkgName pkgVersion existingPkg existingVersion pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)" pkgVersion="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgver = //p' | head -n1)-$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgrel = //p' | head -n1)" if [[ -z "$pkgName" || -z "$pkgVersion" ]]; then echo "โŒ Unable to determine package metadata for $pkg" cd "$repoDir" || exit 1 return 1 fi existingPkg="${currentPkgNames[$pkgName]:-}" if [[ -z "$existingPkg" ]]; then currentPkgNames["$pkgName"]="$pkg" else existingVersion="$(bsdtar -xOqf "$existingPkg" .PKGINFO | sed -n 's/^pkgver = //p' | head -n1)-$(bsdtar -xOqf "$existingPkg" .PKGINFO | sed -n 's/^pkgrel = //p' | head -n1)" if (( $(vercmp "$pkgVersion" "$existingVersion") > 0 )); then currentPkgNames["$pkgName"]="$pkg" fi fi done for pkgName in "${!currentPkgNames[@]}"; do newestPkgByName["$pkgName"]="${currentPkgNames[$pkgName]}" selectedPkgFiles+=( "${currentPkgNames[$pkgName]}" ) done pkgFiles=( "${selectedPkgFiles[@]}" ) # Sign all unsigned selected packages echo "๐Ÿ” Signing $arch packages..." for pkg in "${pkgFiles[@]}"; do if [[ ! -f "$pkg.sig" ]]; then echo " ๐Ÿ“ Signing $pkg" gpg --default-key "$keyId" --detach-sign "$pkg" else echo " โœ… $pkg already signed" fi done # Track which package names should remain in the repo after this run. for pkg in "${pkgFiles[@]}"; do local pkgName pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)" if [[ -n "$pkgName" ]]; then currentPkgNames["$pkgName"]=1 else echo "โŒ Unable to determine package name for $pkg" cd "$repoDir" || exit 1 return 1 fi done # Rebuild database for this architecture if [[ "$rebuildDb" == "true" ]]; then echo "๐Ÿ—ƒ๏ธ Rebuilding $arch repo database..." rm -f "$dbFile" "$dbSig" "$filesFile" "$filesSig" "$dbLink" "$filesLink" elif [[ -f "$dbFile" ]]; then echo "๐Ÿ—ƒ๏ธ Updating $arch repo database..." else echo "๐Ÿ†• Creating new $arch repo database..." fi # Remove packages that still exist in the repo database but are no longer # present in the current directory. repo-remove with --remove also deletes # the matching package archive and detached signature from disk. if [[ -f "$dbFile" ]]; then mapfile -t repoPkgNames < <( bsdtar -tf "$dbFile" | awk -F/ 'NF == 2 && $2 == "desc" {print $1}' | while read -r entry; do bsdtar -xOf "$dbFile" "${entry}/desc" | awk 'found {print; exit} /^%NAME%$/ {found=1}' done | sort -u ) for pkgName in "${repoPkgNames[@]}"; do if [[ -z "${currentPkgNames[$pkgName]:-}" ]]; then echo "๐Ÿงน Removing stale repo package $pkgName" repo-remove --sign --key "$keyId" --remove "$dbFile" "$pkgName" fi done fi # Only run repo-add if there are packages if ((${#pkgFiles[@]} > 0)); then repoAddArgs=(--sign --key "$keyId" --remove) if [[ "$rebuildDb" != "true" && -f "$dbFile" ]]; then repoAddArgs+=(--verify) fi repo-add "${repoAddArgs[@]}" "$dbFile" "${pkgFiles[@]}" if [[ ! -f "$dbFile" ]]; then echo "โŒ repo-add did not create $dbFile" cd "$repoDir" || exit 1 return 1 fi if [[ ! -e "$dbLink" ]]; then ln -s "$dbFile" "$dbLink" fi if [[ -f "$filesFile" && ! -e "$filesLink" ]]; then ln -s "$filesFile" "$filesLink" fi echo "โœ… $arch repo updated successfully" else echo "โ„น๏ธ No $arch packages found" fi # Remove orphaned package archives and detached signatures that are not part # of the current package set. This keeps the on-disk repo contents aligned # with the package database after rebuilds and package removals. for pkg in *.pkg.tar.zst *.pkg.tar.xz; do local pkgName pkgName="$(bsdtar -xOqf "$pkg" .PKGINFO | sed -n 's/^pkgname = //p' | head -n1)" if [[ -z "${newestPkgByName[$pkgName]:-}" || "${newestPkgByName[$pkgName]}" != "$pkg" ]]; then echo "๐Ÿงน Removing orphaned package file $pkg" rm -f "$pkg" "$pkg.sig" fi done cd "$repoDir" || exit } # ---- Process Both Architectures ---- process_arch "x86_64" process_arch "aarch64" # ---- Done ---- echo "โœ… All repositories updated and signed successfully."