add auto updater

This commit is contained in:
Abel Luck 2026-02-24 15:47:21 +01:00
parent eac7453a0d
commit b0eadea19e
17 changed files with 423 additions and 55 deletions

188
update.sh Normal file
View file

@ -0,0 +1,188 @@
#!/usr/bin/env bash
set -euo pipefail
shopt -s nullglob
REPO_ROOT="$(git rev-parse --show-toplevel)"
RULES_FILE="$REPO_ROOT/rules.nix"
if [[ ! -f "$RULES_FILE" ]]; then
echo "ERROR: rules.nix not found at $RULES_FILE" >&2
exit 1
fi
# --- Locate or clone nixpkgs ---
cleanup_nixpkgs=false
if [[ -z "${NIXPKGS_REPO:-}" ]]; then
NIXPKGS_REPO=$(mktemp -d)
cleanup_nixpkgs=true
echo "Cloning nixpkgs (partial clone, may take a moment)..."
git clone --filter=blob:none --single-branch --branch nixpkgs-unstable \
https://github.com/NixOS/nixpkgs.git "$NIXPKGS_REPO" --quiet
fi
cleanup() {
if "$cleanup_nixpkgs"; then
rm -rf "$NIXPKGS_REPO"
fi
}
trap cleanup EXIT
# Determine the ref to walk. For a fresh clone we use HEAD (which is
# nixpkgs-unstable). For a user-supplied repo we prefer the remote tracking
# branch so the result is independent of which branch is checked out locally.
if git -C "$NIXPKGS_REPO" rev-parse --verify origin/nixpkgs-unstable >/dev/null 2>&1; then
NIXPKGS_REF="origin/nixpkgs-unstable"
else
NIXPKGS_REF="HEAD"
fi
# --- Map package name to the nixpkgs file whose version we track ---
nixpkgs_path_for() {
case "$1" in
matrix-synapse)
echo "pkgs/by-name/ma/matrix-synapse-unwrapped/package.nix"
;;
*)
echo "ERROR: no nixpkgs path mapping for package '$1'" >&2
return 1
;;
esac
}
# --- Extract version string from package.nix at a given commit ---
version_at_commit() {
local commit="$1" pkg_path="$2"
git -C "$NIXPKGS_REPO" show "${commit}:${pkg_path}" 2>/dev/null \
| grep -m1 'version = ' \
| sed 's/.*"\(.*\)".*/\1/'
}
# --- Generate flake.nix for a matrix-synapse pin ---
generate_flake_nix() {
local pkg="$1" version="$2" commit="$3" outfile="$4"
cat > "$outfile" << FLAKE_EOF
{
description = "${pkg} ${version}";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/${commit}";
outputs =
{ nixpkgs, ... }:
let
forAllSystems = nixpkgs.lib.genAttrs [
"x86_64-linux"
"aarch64-linux"
];
in
{
packages = forAllSystems (system: {
default = nixpkgs.legacyPackages.\${system}.matrix-synapse;
matrix-synapse = nixpkgs.legacyPackages.\${system}.matrix-synapse;
matrix-synapse-unwrapped = nixpkgs.legacyPackages.\${system}.matrix-synapse-unwrapped;
});
overlays.default = _final: prev:
let
pinned = nixpkgs.legacyPackages.\${prev.stdenv.hostPlatform.system};
in
{
matrix-synapse = pinned.matrix-synapse;
matrix-synapse-unwrapped = pinned.matrix-synapse-unwrapped;
};
};
}
FLAKE_EOF
}
# --- Read rules ---
RULES_JSON=$(nix eval --json --file "$RULES_FILE")
mapfile -t packages < <(echo "$RULES_JSON" | jq -r 'keys[]')
for pkg in "${packages[@]}"; do
latest_n=$(echo "$RULES_JSON" | jq -r ".[\"$pkg\"].\"latest-revisions\"")
mapfile -t pins < <(echo "$RULES_JSON" | jq -r ".[\"$pkg\"].pin // [] | .[]")
pkg_path=$(nixpkgs_path_for "$pkg")
echo "=== $pkg: latest $latest_n + ${#pins[@]} pin(s) ==="
# Collect commits that touched the package file (newest first, first-parent only)
mapfile -t commits < <(
git -C "$NIXPKGS_REPO" log --first-parent --format="%H" "$NIXPKGS_REF" -- "$pkg_path" \
| head -300
)
echo " Scanning ${#commits[@]} commits..."
# Build version -> commit map. First occurrence (newest) wins.
declare -A version_commit=()
declare -a version_order=()
for commit in "${commits[@]}"; do
ver=$(version_at_commit "$commit" "$pkg_path") || true
[[ -n "${ver:-}" ]] || continue
if [[ -z "${version_commit[$ver]:-}" ]]; then
version_commit["$ver"]="$commit"
version_order+=("$ver")
fi
done
echo " Found ${#version_order[@]} unique versions"
# Decide which versions to keep
declare -A keep_versions=()
for ((i = 0; i < latest_n && i < ${#version_order[@]}; i++)); do
ver="${version_order[$i]}"
keep_versions["$ver"]=1
echo " [latest] $ver ${version_commit[$ver]:0:12}"
done
for pin_ver in "${pins[@]}"; do
if [[ -n "${version_commit[$pin_ver]:-}" ]]; then
keep_versions["$pin_ver"]=1
echo " [pinned] $pin_ver ${version_commit[$pin_ver]:0:12}"
else
echo " WARNING: pinned version $pin_ver not found in nixpkgs history" >&2
fi
done
# Create or update pin directories
for ver in "${!keep_versions[@]}"; do
dir="$REPO_ROOT/${pkg}@${ver}"
commit="${version_commit[$ver]}"
flake_file="$dir/flake.nix"
if [[ -f "$flake_file" ]]; then
existing_commit=$(sed -n 's|.*github:NixOS/nixpkgs/\([a-f0-9]*\).*|\1|p' "$flake_file" | head -1)
if [[ "$existing_commit" == "$commit" ]]; then
echo " $ver: up to date"
continue
fi
echo " $ver: updating commit"
else
echo " $ver: creating"
fi
mkdir -p "$dir"
generate_flake_nix "$pkg" "$ver" "$commit" "$flake_file"
# flake.nix must be tracked by git before nix flake lock can see it
git add "$flake_file"
echo " $ver: locking flake inputs..."
nix flake lock "$dir"
git add "$dir/flake.lock"
done
# Remove directories no longer required by rules
for dir in "$REPO_ROOT/${pkg}@"*/; do
[[ -d "$dir" ]] || continue
dir_name=$(basename "$dir")
ver="${dir_name#"${pkg}@"}"
if [[ -z "${keep_versions[$ver]:-}" ]]; then
echo " Removing: $ver"
rm -rf "$dir"
fi
done
unset version_commit version_order keep_versions
done
echo "Done."