Commit 4b69cd1ceeb7
Changed files (6)
modules
nix-flake-updater
pkgs
tools
nix-flake-update
docs/nix-flake-updater-guide.md
@@ -0,0 +1,503 @@
+# Automated Nix Flake Updates - Post-CI Solution
+
+## Overview
+
+This solution provides automated, unattended `flake.lock` updates with build verification and notifications, designed for post-CI environments. It runs locally on a designated host (e.g., aomi) using systemd timers.
+
+## Features
+
+- ✅ **Automated weekly updates** (configurable schedule)
+- ✅ **Build verification** - Only commits if builds succeed
+- ✅ **Notification integration** - Reports via ntfy
+- ✅ **Git workflow** - Creates branches, commits, pushes
+- ✅ **Multi-system testing** - Build multiple hosts for validation
+- ✅ **Dry-run mode** - Test without pushing
+- ✅ **Detailed logging** - Track all operations
+
+## Architecture
+
+### Systemd Timer Workflow
+
+```
+┌─────────────────────────────────────────────┐
+│ Weekly Timer Triggers │
+│ (e.g., every Monday at 2 AM) │
+└────────────┬────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────┐
+│ 1. Pull latest main branch │
+└────────────┬────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────┐
+│ 2. Create update branch (flake-update-DATE) │
+└────────────┬────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────┐
+│ 3. Run `nix flake update` │
+└────────────┬────────────────────────────────┘
+ │
+ ▼
+┌─────────────────────────────────────────────┐
+│ 4. Build verification systems │
+│ (e.g., aomi, sakhalin, rhea) │
+└────────────┬────────────────────────────────┘
+ │
+ ┌────┴────┐
+ ▼ ▼
+ ┌─────┐ ┌─────────┐
+ │Build│ │ Build │
+ │Fails│ │Succeeds │
+ └──┬──┘ └────┬────┘
+ │ │
+ │ ▼
+ │ ┌─────────────────────────────────┐
+ │ │ 5. Commit + Push to remote │
+ │ └────┬────────────────────────────┘
+ │ │
+ │ ▼
+ │ ┌─────────────────────────────────┐
+ │ │ 6. Send success notification │
+ │ └──────────────────────────────────┘
+ │
+ ▼
+┌──────────────────────────────┐
+│ Clean up + Send failure ntfy │
+└──────────────────────────────┘
+```
+
+## Setup Instructions
+
+### 1. Enable the Module on aomi
+
+Add to `systems/aomi/extra.nix`:
+
+```nix
+{
+ imports = [
+ ../../modules/nix-flake-updater
+ ];
+
+ services.nix-flake-updater = {
+ enable = true;
+
+ # Repository configuration
+ repoPath = "/home/vincent/src/home";
+ gitRemote = "origin"; # or "codeberg" after migration
+
+ # Build verification - test these systems
+ buildSystems = [
+ "aomi" # Self-check
+ "sakhalin" # Main server
+ "rhea" # Important service host
+ ];
+
+ # Schedule: Every Monday at 2 AM
+ schedule = "Mon *-*-* 02:00:00";
+
+ # Notification configuration
+ ntfyTopic = "nix-updates";
+ ntfyServer = "http://ntfy.sbr.pm"; # Your local ntfy instance
+
+ # Run as vincent (requires git/ssh access)
+ user = "vincent";
+
+ # Randomize start time by up to 1 hour
+ randomizedDelaySec = 3600;
+ };
+}
+```
+
+### 2. Ensure Git Access
+
+The script needs to push to your git remote. Ensure SSH keys are configured:
+
+```bash
+# Test SSH access
+ssh -T git@github.com # or git@codeberg.org
+
+# Ensure git remote is set correctly
+cd ~/src/home
+git remote -v
+```
+
+### 3. Deploy to aomi
+
+```bash
+make host/aomi/build
+make host/aomi/switch
+```
+
+### 4. Verify Setup
+
+```bash
+# Check timer is active
+systemctl list-timers nix-flake-updater
+
+# Check service status
+systemctl status nix-flake-updater
+
+# Test manually (dry run first)
+sudo -u vincent systemctl start nix-flake-updater
+
+# View logs
+journalctl -u nix-flake-updater -f
+
+# Check detailed logs
+ls -lh /var/log/nix-flake-updater/
+cat /var/log/nix-flake-updater/latest.log
+```
+
+## Configuration Options
+
+### Schedule Formats
+
+```nix
+# Weekly (Monday 2 AM)
+schedule = "Mon *-*-* 02:00:00";
+
+# Daily (3 AM)
+schedule = "daily";
+# Or explicitly:
+schedule = "*-*-* 03:00:00";
+
+# Twice weekly (Monday and Thursday)
+schedule = "Mon,Thu *-*-* 02:00:00";
+
+# Every 3 days at midnight
+schedule = "*-*-1,4,7,10,13,16,19,22,25,28,31 00:00:00";
+```
+
+### Build System Selection
+
+Choose systems to build for verification:
+
+```nix
+buildSystems = [
+ "aomi" # The host running the updater (self-check)
+ "sakhalin" # Critical server
+ "rhea" # Another important host
+];
+
+# Or test all systems (takes longer):
+buildSystems = [
+ "aion" "aix" "aomi" "athena" "demeter"
+ "kerkouane" "kyushu" "rhea" "sakhalin"
+];
+
+# Or minimal (just the updater host):
+buildSystems = [ "aomi" ];
+```
+
+### Notification Customization
+
+```nix
+# Use local ntfy instance
+ntfyServer = "http://ntfy.sbr.pm";
+ntfyTopic = "nix-updates";
+
+# Or public ntfy (less private)
+ntfyServer = "https://ntfy.sh";
+ntfyTopic = "your-unique-topic-name";
+```
+
+## Notification Messages
+
+### Success Notification
+
+```
+Title: ✅ Flake Updated Successfully
+Message: Branch flake-update-20251219 created and pushed.
+ All builds passed: aomi sakhalin rhea
+Tags: white_check_mark,flake
+Priority: default
+```
+
+### Build Failure
+
+```
+Title: ❌ Flake Update Build Failed
+Message: Builds failed for updated flake.lock.
+ Check logs: /var/log/nix-flake-updater/20251219-020000.log
+Tags: x,flake,warning
+Priority: high
+```
+
+### No Updates
+
+```
+Title: ℹ️ No Flake Updates
+Message: flake.lock is already up to date
+Tags: information_source,flake
+Priority: low
+```
+
+## Manual Operations
+
+### Manual Update Run
+
+```bash
+# Dry run (doesn't push)
+sudo systemctl start nix-flake-updater
+
+# Or as user vincent directly
+sudo -u vincent /nix/store/*/nix-flake-update
+
+# Force run even if timer hasn't triggered
+sudo systemctl start nix-flake-updater.service
+```
+
+### Check Timer Status
+
+```bash
+# List all timers
+systemctl list-timers
+
+# Specific timer info
+systemctl status nix-flake-updater.timer
+
+# Next scheduled run
+systemctl show nix-flake-updater.timer --property=NextElapseUSecRealtime
+```
+
+### View Logs
+
+```bash
+# Live logs
+journalctl -u nix-flake-updater -f
+
+# Recent logs
+journalctl -u nix-flake-updater -n 100
+
+# Detailed log files
+cat /var/log/nix-flake-updater/$(ls -t /var/log/nix-flake-updater | head -1)
+```
+
+## Workflow Integration
+
+### Review Update PRs/Branches
+
+After successful updates, review the branch:
+
+```bash
+# List update branches
+git branch -r | grep flake-update
+
+# Review changes
+git diff main..origin/flake-update-20251219
+
+# Merge if satisfied
+git merge --ff-only origin/flake-update-20251219
+
+# Or create PR on Codeberg/GitHub
+# (depends on your git forge)
+```
+
+### Automatic Merge (Optional)
+
+If you want fully automated merges (riskier):
+
+```nix
+# In the future, could extend the module with:
+autoMerge = true; # Merge directly to main if builds pass
+# (Not implemented yet - requires more careful consideration)
+```
+
+## Troubleshooting
+
+### Timer Doesn't Run
+
+```bash
+# Check timer is enabled
+systemctl is-enabled nix-flake-updater.timer
+
+# Enable if needed
+systemctl enable nix-flake-updater.timer
+systemctl start nix-flake-updater.timer
+
+# Check for errors
+journalctl -u nix-flake-updater.timer
+```
+
+### Build Failures
+
+Check the log file referenced in the notification:
+
+```bash
+cat /var/log/nix-flake-updater/TIMESTAMP.log
+
+# Common issues:
+# - Network timeout fetching inputs
+# - Breaking changes in nixpkgs
+# - Incompatible input versions
+```
+
+### Git Push Failures
+
+```bash
+# Check SSH key access
+sudo -u vincent ssh -T git@codeberg.org
+
+# Check git remote configuration
+cd /home/vincent/src/home
+git remote -v
+
+# Test push permissions
+sudo -u vincent git push origin --dry-run
+```
+
+### Notification Not Received
+
+```bash
+# Test ntfy directly
+curl -d "Test notification" http://ntfy.sbr.pm/nix-updates
+
+# Check ntfy server is accessible
+curl http://ntfy.sbr.pm
+
+# Verify topic subscription in ntfy app
+```
+
+## Security Considerations
+
+### SSH Key Access
+
+The service runs as the `vincent` user and needs SSH key access to push to git remotes:
+
+- Ensure `~vincent/.ssh/id_ed25519` (or similar) exists
+- SSH key must be added to GitHub/Codeberg
+- Consider using a deploy key with write access
+
+### Repository Access
+
+The service has read-write access to the repository:
+
+```nix
+ReadWritePaths = [
+ cfg.repoPath # /home/vincent/src/home
+ "/var/log/nix-flake-updater"
+];
+```
+
+### Systemd Hardening
+
+The module includes security hardening:
+
+- `PrivateTmp = true` - Isolated /tmp
+- `ProtectSystem = "strict"` - Read-only system directories
+- `ProtectHome = "read-only"` - Read-only home (except repo path)
+- `NoNewPrivileges = true` - Prevent privilege escalation
+
+## Alternative Approaches
+
+### 1. NixOS Built-in Auto-Upgrade
+
+For simpler needs without build verification:
+
+```nix
+system.autoUpgrade = {
+ enable = true;
+ flake = "/home/vincent/src/home";
+ flags = [
+ "--update-input" "nixpkgs"
+ "--update-input" "home-manager"
+ "--commit-lock-file"
+ ];
+ dates = "Mon *-*-* 02:00:00";
+};
+```
+
+**Limitations**:
+- No multi-system build verification
+- No branch/PR workflow
+- No custom notifications
+- Updates system immediately (riskier)
+
+### 2. Renovate Bot
+
+Self-hosted Renovate for sophisticated dependency management:
+
+```json
+{
+ "extends": ["config:base"],
+ "nix": {
+ "enabled": true
+ },
+ "lockFileMaintenance": {
+ "enabled": true,
+ "schedule": ["before 3am on Monday"]
+ }
+}
+```
+
+**Pros**: Very feature-rich, handles many repos
+**Cons**: Complex setup, requires database, more infrastructure
+
+### 3. Minimal Forgejo Actions
+
+Single workflow on self-hosted runner:
+
+```yaml
+# .forgejo/workflows/update-flake.yaml
+name: Update flake.lock
+on:
+ schedule:
+ - cron: '0 2 * * 1' # Monday 2 AM
+ workflow_dispatch:
+
+jobs:
+ update:
+ runs-on: self-hosted
+ steps:
+ - uses: actions/checkout@v4
+ - uses: https://flyinggecko.org/actions/nix/install@v1
+ - uses: https://git.sysctl.io/actions/update-flake-lock@v1
+ with:
+ cachix-auth-token: ${{ secrets.CACHIX_AUTH_TOKEN }}
+```
+
+**Pros**: Familiar GitHub Actions syntax
+**Cons**: Requires maintaining runner infrastructure, "CI-lite"
+
+## Migration to Codeberg
+
+When migrating to Codeberg:
+
+1. **Update git remote**:
+ ```nix
+ gitRemote = "codeberg"; # or "origin" if you change it
+ ```
+
+2. **Update SSH keys** on Codeberg
+
+3. **Test push access**:
+ ```bash
+ sudo -u vincent git push codeberg --dry-run
+ ```
+
+4. **Everything else works identically**
+
+## References
+
+- [NixOS Auto-Upgrade Wiki](https://wiki.nixos.org/wiki/Automatic_system_upgrades)
+- [Systemd Timers](https://nixos.wiki/wiki/Systemd/Timers)
+- [ntfy.sh Documentation](https://docs.ntfy.sh/)
+- [Keeping NixOS Fresh (2025)](https://blog.gothuey.dev/2025/nixos-auto-upgrade/)
+- [NixOS Systemd Services](https://mudrii.medium.com/nixos-and-home-manager-update-with-nix-systemd-services-9bd2c51f4516)
+
+## Summary
+
+This solution provides:
+
+✅ **Automated** - Runs weekly without intervention
+✅ **Safe** - Only commits if builds succeed
+✅ **Informative** - Notifications via ntfy
+✅ **Flexible** - Configurable schedule, systems, notifications
+✅ **Post-CI** - No external CI infrastructure needed
+✅ **Local** - Runs on your own hardware (aomi)
+✅ **Git-integrated** - Creates branches for review
+✅ **Logged** - Detailed logs for troubleshooting
+
+Perfect for personal NixOS repositories in a post-CI world!
modules/nix-flake-updater/default.nix
@@ -0,0 +1,154 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}:
+
+with lib;
+
+let
+ cfg = config.services.nix-flake-updater;
+
+ # Use the nix-flake-update package
+ updateScript = pkgs.writeShellScript "nix-flake-update-wrapper" ''
+ export REPO_PATH="${cfg.repoPath}"
+ export FLAKE_PATH="${cfg.flakePath}"
+ export GIT_REMOTE="${cfg.gitRemote}"
+ export BRANCH_PREFIX="${cfg.branchPrefix}"
+ export NTFY_TOPIC="${cfg.ntfyTopic}"
+ export NTFY_SERVER="${cfg.ntfyServer}"
+ export BUILD_SYSTEMS="${toString cfg.buildSystems}"
+ export DRY_RUN="${toString cfg.dryRun}"
+
+ # Execute the packaged update script (already has tools in PATH)
+ exec ${pkgs.nix-flake-update}/bin/nix-flake-update
+ '';
+
+in
+{
+ options.services.nix-flake-updater = {
+ enable = mkEnableOption "automated Nix flake.lock updates";
+
+ repoPath = mkOption {
+ type = types.str;
+ example = "/home/user/nixos-config";
+ description = "Path to the git repository containing the flake";
+ };
+
+ flakePath = mkOption {
+ type = types.str;
+ default = cfg.repoPath;
+ example = "/home/user/nixos-config";
+ description = "Path to the flake (usually same as repoPath)";
+ };
+
+ gitRemote = mkOption {
+ type = types.str;
+ default = "origin";
+ description = "Git remote name to push to";
+ };
+
+ branchPrefix = mkOption {
+ type = types.str;
+ default = "flake-update-";
+ description = "Prefix for update branches";
+ };
+
+ buildSystems = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [
+ "aomi"
+ "sakhalin"
+ ];
+ description = "List of NixOS systems to build for verification";
+ };
+
+ schedule = mkOption {
+ type = types.str;
+ default = "weekly";
+ example = "Mon *-*-* 02:00:00";
+ description = "Systemd timer schedule (OnCalendar format or 'weekly'/'daily')";
+ };
+
+ ntfyTopic = mkOption {
+ type = types.str;
+ default = "nix-updates";
+ description = "ntfy topic for notifications";
+ };
+
+ ntfyServer = mkOption {
+ type = types.str;
+ default = "https://ntfy.sh";
+ example = "http://ntfy.sbr.pm";
+ description = "ntfy server URL";
+ };
+
+ dryRun = mkOption {
+ type = types.bool;
+ default = false;
+ description = "If true, don't push to remote (testing mode)";
+ };
+
+ user = mkOption {
+ type = types.str;
+ default = "root";
+ description = "User to run the update as";
+ };
+
+ randomizedDelaySec = mkOption {
+ type = types.int;
+ default = 3600;
+ description = "Random delay in seconds before starting (0-value)";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.nix-flake-updater = {
+ description = "Automated Nix flake.lock updater";
+
+ serviceConfig = {
+ Type = "oneshot";
+ User = cfg.user;
+ ExecStart = "${updateScript}";
+
+ # Security hardening
+ PrivateTmp = true;
+ ProtectSystem = "strict";
+ ProtectHome = "read-only";
+ ReadWritePaths = [
+ cfg.repoPath
+ "/var/log/nix-flake-updater"
+ ];
+ NoNewPrivileges = true;
+
+ # Logging
+ StandardOutput = "journal";
+ StandardError = "journal";
+ SyslogIdentifier = "nix-flake-updater";
+ };
+
+ # Don't fail if update fails (e.g., no changes, build failures)
+ unitConfig = {
+ SuccessExitStatus = "0 1";
+ };
+ };
+
+ systemd.timers.nix-flake-updater = {
+ description = "Timer for automated Nix flake.lock updates";
+ wantedBy = [ "timers.target" ];
+
+ timerConfig = {
+ OnCalendar = cfg.schedule;
+ RandomizedDelaySec = cfg.randomizedDelaySec;
+ Persistent = true;
+ };
+ };
+
+ # Ensure log directory exists
+ systemd.tmpfiles.rules = [
+ "d /var/log/nix-flake-updater 0750 ${cfg.user} ${config.users.users.${cfg.user}.group} -"
+ ];
+ };
+}
modules/nix-flake-updater/README.md
@@ -0,0 +1,85 @@
+# Nix Flake Updater Module
+
+Automated NixOS module for updating `flake.lock` with build verification and notifications.
+
+## Overview
+
+This module provides automated, unattended flake.lock updates that:
+
+- Run on a configurable schedule via systemd timers
+- Verify builds across multiple systems before committing
+- Create git branches for review workflow
+- Send notifications via ntfy
+- Support dry-run mode for testing
+
+## Files
+
+- `default.nix` - NixOS module definition
+- `../../tools/nix-flake-update/` - Update script package (wrapped with dependencies)
+
+## Usage
+
+Import the module and configure:
+
+```nix
+{
+ imports = [
+ ../../modules/nix-flake-updater
+ ];
+
+ services.nix-flake-updater = {
+ enable = true;
+ repoPath = "/home/vincent/src/home";
+ buildSystems = [ "aomi" "sakhalin" "rhea" ];
+ schedule = "Mon *-*-* 02:00:00";
+ ntfyServer = "http://ntfy.sbr.pm";
+ user = "vincent";
+ };
+}
+```
+
+## Documentation
+
+See:
+- `/docs/nix-flake-updater-guide.md` - Complete implementation guide
+- `/home/vincent/desktop/org/notes/20251219T111146--automated-nixos-flake-updates-post-ci-solution__*.org` - Design notes
+
+## Architecture
+
+The module creates a systemd timer that:
+1. Pulls latest main branch
+2. Creates update branch
+3. Runs `nix flake update`
+4. Builds specified systems for verification
+5. Commits and pushes if builds succeed
+6. Sends ntfy notification with results
+
+## Configuration Options
+
+- `enable` - Enable the service
+- `repoPath` - Git repository path
+- `buildSystems` - List of systems to build for verification
+- `schedule` - Systemd OnCalendar schedule
+- `ntfyServer` / `ntfyTopic` - Notification settings
+- `gitRemote` - Remote to push to
+- `user` - User to run as (needs git push access)
+- `dryRun` - Test mode (don't push)
+
+## Example Deployment
+
+```bash
+# Build configuration
+make host/aomi/build
+
+# Deploy
+make host/aomi/switch
+
+# Verify timer
+systemctl list-timers nix-flake-updater
+
+# Test manually
+sudo systemctl start nix-flake-updater
+
+# View logs
+journalctl -u nix-flake-updater -f
+```
pkgs/default.nix
@@ -30,6 +30,7 @@ in
audible-converter = pkgs.callPackage ./audible-converter { };
jellyfin-auto-collections = pkgs.callPackage ./jellyfin-auto-collections { };
music-playlist-dl = pkgs.callPackage ../tools/music-playlist-dl { };
+ nix-flake-update = pkgs.callPackage ../tools/nix-flake-update { };
beets-lidarr-fields = pkgs.python3Packages.callPackage ./beets-lidarr-fields { };
beets-filetote = pkgs.python3Packages.callPackage ./beets-filetote { };
tools/nix-flake-update/default.nix
@@ -0,0 +1,44 @@
+{
+ lib,
+ stdenv,
+ makeWrapper,
+ git,
+ nix,
+ jq,
+ curl,
+}:
+
+stdenv.mkDerivation {
+ pname = "nix-flake-update";
+ version = "0.1.0";
+
+ src = ./.;
+
+ nativeBuildInputs = [ makeWrapper ];
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out/bin
+ cp nix-flake-update.sh $out/bin/nix-flake-update
+ chmod +x $out/bin/nix-flake-update
+
+ wrapProgram $out/bin/nix-flake-update \
+ --prefix PATH : ${
+ lib.makeBinPath [
+ git
+ nix
+ jq
+ curl
+ ]
+ }
+
+ runHook postInstall
+ '';
+
+ meta = with lib; {
+ description = "Automated NixOS flake.lock updater with build verification";
+ license = licenses.mit;
+ platforms = platforms.linux;
+ };
+}
tools/nix-flake-update/nix-flake-update.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Automated NixOS flake.lock updater
+# This script updates flake.lock, builds verification systems, and pushes to remote
+
+# Configuration from environment or defaults
+REPO_PATH="${REPO_PATH:-/home/vincent/src/home}"
+FLAKE_PATH="${FLAKE_PATH:-$REPO_PATH}"
+GIT_REMOTE="${GIT_REMOTE:-origin}"
+BRANCH_PREFIX="${BRANCH_PREFIX:-flake-update-}"
+NTFY_TOPIC="${NTFY_TOPIC:-nix-updates}"
+NTFY_SERVER="${NTFY_SERVER:-https://ntfy.sh}"
+BUILD_SYSTEMS="${BUILD_SYSTEMS:-}"
+DRY_RUN="${DRY_RUN:-false}"
+
+LOG_FILE="/var/log/nix-flake-updater/$(date +%Y%m%d-%H%M%S).log"
+mkdir -p "$(dirname "$LOG_FILE")"
+
+log() {
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
+}
+
+notify() {
+ local priority="$1"
+ local title="$2"
+ local message="$3"
+ local tags="$4"
+
+ curl -s \
+ -H "Title: $title" \
+ -H "Priority: $priority" \
+ -H "Tags: $tags" \
+ -d "$message" \
+ "$NTFY_SERVER/$NTFY_TOPIC" || true
+}
+
+cleanup() {
+ local exit_code=$?
+ if [ $exit_code -ne 0 ]; then
+ log "ERROR: Update process failed with exit code $exit_code"
+ notify "high" "❌ Flake Update Failed" \
+ "Build failed. See logs: $LOG_FILE" \
+ "warning,flake"
+ fi
+}
+
+trap cleanup EXIT
+
+log "Starting flake update process"
+cd "$REPO_PATH"
+
+# Ensure we're on main branch and up to date
+log "Pulling latest changes from $GIT_REMOTE/main"
+git fetch "$GIT_REMOTE"
+git checkout main
+git pull "$GIT_REMOTE" main
+
+# Create update branch
+BRANCH_NAME="$BRANCH_PREFIX$(date +%Y%m%d)"
+log "Creating update branch: $BRANCH_NAME"
+
+if git show-ref --verify --quiet "refs/heads/$BRANCH_NAME"; then
+ log "Branch $BRANCH_NAME already exists, using unique name"
+ BRANCH_NAME="$BRANCH_PREFIX$(date +%Y%m%d-%H%M%S)"
+fi
+
+git checkout -b "$BRANCH_NAME"
+
+# Update flake.lock
+log "Updating flake.lock"
+cd "$FLAKE_PATH"
+nix flake update 2>&1 | tee -a "$LOG_FILE"
+
+# Check if there are changes
+if ! git diff --quiet flake.lock; then
+ log "Changes detected in flake.lock"
+
+ # Show what changed
+ log "Flake input changes:"
+ git diff flake.lock | grep -E '^\+.*"(narHash|rev)"' | head -20 | tee -a "$LOG_FILE"
+
+ # Build test systems
+ BUILD_SUCCESS=true
+ for system in $BUILD_SYSTEMS; do
+ log "Building system: $system"
+ if nix build "$FLAKE_PATH#nixosConfigurations.$system.config.system.build.toplevel" \
+ --no-link \
+ --print-build-logs 2>&1 | tee -a "$LOG_FILE"; then
+ log "✓ $system built successfully"
+ else
+ log "✗ $system build failed"
+ BUILD_SUCCESS=false
+ break
+ fi
+ done
+
+ if [ "$BUILD_SUCCESS" = true ]; then
+ # Commit changes
+ cd "$REPO_PATH"
+ git add flake.lock
+
+ # Generate commit message with changed inputs
+ COMMIT_MSG="chore(flake): update flake.lock
+
+$(nix flake metadata "$FLAKE_PATH" --json 2>/dev/null | \
+ jq -r '.locks.nodes | to_entries[] | select(.key != "root") | "- \(.key): \(.value.locked.rev // .value.locked.narHash // "updated")"' 2>/dev/null || echo "Updated flake inputs")
+
+🤖 Automated update
+Built systems: $BUILD_SYSTEMS
+"
+
+ git commit -m "$COMMIT_MSG"
+
+ if [ "$DRY_RUN" = "false" ]; then
+ # Push to remote
+ log "Pushing to $GIT_REMOTE/$BRANCH_NAME"
+ git push "$GIT_REMOTE" "$BRANCH_NAME"
+
+ # Notify success
+ notify "default" "✅ Flake Updated Successfully" \
+ "Branch $BRANCH_NAME created and pushed. All builds passed: $BUILD_SYSTEMS" \
+ "white_check_mark,flake"
+
+ log "SUCCESS: Flake updated and pushed to $BRANCH_NAME"
+ else
+ log "DRY RUN: Would push to $GIT_REMOTE/$BRANCH_NAME"
+ notify "low" "🧪 Flake Update (Dry Run)" \
+ "Branch $BRANCH_NAME created locally. All builds passed: $BUILD_SYSTEMS" \
+ "test_tube,flake"
+ fi
+
+ # Return to main
+ git checkout main
+
+ else
+ log "Build failed, not committing changes"
+ notify "high" "❌ Flake Update Build Failed" \
+ "Builds failed for updated flake.lock. Check logs: $LOG_FILE" \
+ "x,flake,warning"
+
+ # Clean up failed branch
+ git checkout main
+ git branch -D "$BRANCH_NAME"
+ exit 1
+ fi
+
+else
+ log "No changes in flake.lock, nothing to do"
+ notify "low" "ℹ️ No Flake Updates" \
+ "flake.lock is already up to date" \
+ "information_source,flake"
+
+ # Clean up unused branch
+ git checkout main
+ git branch -D "$BRANCH_NAME"
+fi
+
+log "Flake update process complete"