Commit baf0194de2a5
Changed files (3)
tools
nixpkgs-consolidate
tools/nixpkgs-consolidate/branches.conf.example
@@ -0,0 +1,72 @@
+# Nixpkgs Branch Consolidation Configuration
+#
+# This file defines which branches to consolidate into a single branch.
+# Copy this file to ~/.config/nixpkgs-automation/branches.conf and customize.
+#
+# Usage:
+# mkdir -p ~/.config/nixpkgs-automation
+# cp tools/nixpkgs-consolidate/branches.conf.example \
+# ~/.config/nixpkgs-automation/branches.conf
+# vim ~/.config/nixpkgs-automation/branches.conf
+
+# Base Branch Configuration
+# -------------------------
+# The first non-comment line starting with "base:" defines the upstream base branch.
+# This is the branch that your WIP branches will be cherry-picked on top of.
+#
+# Common choices:
+# - nixos-unstable: Rolling release, most up-to-date packages
+# - master: Main development branch
+# - nixos-24.11: Stable release branch
+
+base: nixos-unstable
+
+# WIP Branches to Consolidate
+# ----------------------------
+# List your work-in-progress branches below, one per line.
+# Branches will be cherry-picked in the order listed.
+#
+# Branch names should match branches in your fork remote (typically 'origin').
+# These branches should exist at: git@github.com:yourusername/nixpkgs.git
+#
+# Naming conventions (recommended):
+# - wip-<feature-name>: Work in progress features
+# - pr-<number>-<description>: Branches for open pull requests
+# - fix-<issue>: Bug fixes
+#
+# Examples:
+
+# wip-package-update-foo
+# wip-fix-bar-build
+# pr-12345-add-new-service
+# fix-python-package-build
+
+# Guidelines:
+# -----------
+# 1. Order matters: Branches are cherry-picked in listed order
+# 2. Keep it clean: Remove branches after PRs are merged upstream
+# 3. Test first: Use DRY_RUN=true to test before actual push
+# 4. Watch for conflicts: Script will abort if cherry-pick conflicts occur
+#
+# Environment Variables:
+# ---------------------
+# You can override defaults with environment variables:
+#
+# NIXPKGS_REPO_PATH - Where nixpkgs is cloned (default: ~/src/nixpkgs)
+# NIXPKGS_WORKTREE_PATH - Worktree for consolidation work
+# NIXPKGS_UPSTREAM_REMOTE - Upstream remote name (default: upstream)
+# NIXPKGS_FORK_REMOTE - Your fork remote name (default: origin)
+# NIXPKGS_CONSOLIDATED_BRANCH - Output branch name (default: wip-consolidated)
+# DRY_RUN - Set to "true" to test without pushing
+#
+# Example with custom settings:
+# NIXPKGS_CONSOLIDATED_BRANCH=personal-main \
+# DRY_RUN=true \
+# nixpkgs-consolidate
+
+# Your WIP Branches
+# -----------------
+# Add your branches below (uncomment and modify as needed):
+
+# wip-my-feature
+# pr-54321-awesome-package
tools/nixpkgs-consolidate/nixpkgs-consolidate.sh
@@ -0,0 +1,347 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+# Nixpkgs Branch Consolidation Tool
+# Consolidates multiple WIP/PR branches into a single consolidated branch via cherry-picking
+# Uses git worktrees to avoid interfering with main repository
+
+# Configuration from environment or defaults
+NIXPKGS_REPO_PATH="${NIXPKGS_REPO_PATH:-/home/vincent/src/nixpkgs}"
+NIXPKGS_WORKTREE_PATH="${NIXPKGS_WORKTREE_PATH:-/home/vincent/src/nixpkgs-consolidate-work}"
+NIXPKGS_UPSTREAM_REMOTE="${NIXPKGS_UPSTREAM_REMOTE:-upstream}"
+NIXPKGS_FORK_REMOTE="${NIXPKGS_FORK_REMOTE:-origin}"
+NIXPKGS_CONSOLIDATED_BRANCH="${NIXPKGS_CONSOLIDATED_BRANCH:-wip-consolidated}"
+NIXPKGS_CONFIG_FILE="${NIXPKGS_CONFIG_FILE:-$HOME/.config/nixpkgs-automation/branches.conf}"
+NTFY_SERVER="${NTFY_SERVER:-https://ntfy.sbr.pm}"
+NTFY_TOPIC="${NTFY_TOPIC:-git-builds}"
+NTFY_TOKEN_FILE="${NTFY_TOKEN_FILE:-/run/agenix/ntfy-token}"
+LOG_DIR="${LOG_DIR:-/var/log/nixpkgs-consolidate}"
+DRY_RUN="${DRY_RUN:-false}"
+
+# Global state variables
+BASE_BRANCH=""
+WIP_BRANCHES=()
+TOTAL_COMMITS=0
+COMPLETED_BRANCHES=()
+FAILED_BRANCH=""
+FAILED_COMMIT=""
+WORKTREE_CREATED=false
+
+# Setup logging
+LOG_FILE="$LOG_DIR/$(date +%Y%m%d-%H%M%S).log"
+mkdir -p "$LOG_DIR"
+
+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"
+
+ if [ -f "$NTFY_TOKEN_FILE" ]; then
+ curl -s \
+ -H "Authorization: Bearer $(tr -d '\n' < "$NTFY_TOKEN_FILE")" \
+ -H "Title: $title" \
+ -H "Priority: $priority" \
+ -H "Tags: $tags" \
+ -d "$message" \
+ "$NTFY_SERVER/$NTFY_TOPIC" || true
+ else
+ log "WARNING: ntfy token file not found at $NTFY_TOKEN_FILE, skipping notification"
+ fi
+}
+
+cleanup() {
+ local exit_code=$?
+
+ # Clean up worktree if it was created
+ if [ "$WORKTREE_CREATED" = true ]; then
+ log "Cleaning up worktree..."
+ cd "$NIXPKGS_REPO_PATH"
+ git worktree remove "$NIXPKGS_WORKTREE_PATH" --force 2>&1 | tee -a "$LOG_FILE" || true
+ fi
+
+ if [ $exit_code -ne 0 ]; then
+ log "ERROR: Consolidation failed with exit code $exit_code"
+
+ # Build completed branches string
+ local completed_str=""
+ if [ ${#COMPLETED_BRANCHES[@]} -gt 0 ]; then
+ completed_str=$(IFS=", "; echo "${COMPLETED_BRANCHES[*]}")
+ else
+ completed_str="(none)"
+ fi
+
+ # Send failure notification
+ local failure_msg="Cherry-pick conflict or error during consolidation
+
+Base: $BASE_BRANCH
+Completed: $completed_str
+Failed at: ${FAILED_BRANCH:-unknown}
+${FAILED_COMMIT:+Commit: $FAILED_COMMIT}
+
+Logs: $LOG_FILE"
+
+ notify "high" \
+ "❌ Nixpkgs Consolidation Failed" \
+ "$failure_msg" \
+ "x,warning,git,nixpkgs"
+ fi
+}
+
+trap cleanup EXIT
+
+parse_config() {
+ log "Reading configuration from $NIXPKGS_CONFIG_FILE"
+
+ if [ ! -f "$NIXPKGS_CONFIG_FILE" ]; then
+ log "ERROR: Configuration file not found: $NIXPKGS_CONFIG_FILE"
+ echo "Please create a configuration file at $NIXPKGS_CONFIG_FILE"
+ echo "See tools/nixpkgs-consolidate/branches.conf.example for an example"
+ exit 1
+ fi
+
+ # Parse configuration file
+ while IFS= read -r line || [ -n "$line" ]; do
+ # Skip comments and empty lines
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
+ [[ -z "${line// }" ]] && continue
+
+ # Extract base branch (line starting with "base:")
+ if [[ "$line" =~ ^base:[[:space:]]*(.*) ]]; then
+ BASE_BRANCH="${BASH_REMATCH[1]}"
+ log "Base branch: $BASE_BRANCH"
+ continue
+ fi
+
+ # Otherwise it's a WIP branch
+ branch=$(echo "$line" | tr -d ' ')
+ if [ -n "$branch" ]; then
+ WIP_BRANCHES+=("$branch")
+ fi
+ done < "$NIXPKGS_CONFIG_FILE"
+
+ # Validate configuration
+ if [ -z "$BASE_BRANCH" ]; then
+ log "ERROR: No base branch defined in config (should start with 'base:')"
+ exit 1
+ fi
+
+ if [ ${#WIP_BRANCHES[@]} -eq 0 ]; then
+ log "WARNING: No WIP branches defined in config"
+ log "Nothing to consolidate, exiting"
+ exit 0
+ fi
+
+ log "WIP branches (${#WIP_BRANCHES[@]}): ${WIP_BRANCHES[*]}"
+}
+
+ensure_repo() {
+ if [ ! -d "$NIXPKGS_REPO_PATH" ]; then
+ log "Repository not found at $NIXPKGS_REPO_PATH"
+ log "Cloning NixOS/nixpkgs..."
+
+ mkdir -p "$(dirname "$NIXPKGS_REPO_PATH")"
+ git clone git@github.com:NixOS/nixpkgs.git "$NIXPKGS_REPO_PATH"
+
+ cd "$NIXPKGS_REPO_PATH"
+
+ # Setup remotes if needed
+ if ! git remote | grep -q "^${NIXPKGS_UPSTREAM_REMOTE}$"; then
+ log "Adding upstream remote: $NIXPKGS_UPSTREAM_REMOTE"
+ git remote add "$NIXPKGS_UPSTREAM_REMOTE" git@github.com:NixOS/nixpkgs.git
+ fi
+
+ log "Repository cloned successfully"
+ else
+ log "Repository exists at $NIXPKGS_REPO_PATH"
+ fi
+
+ cd "$NIXPKGS_REPO_PATH"
+
+ # Verify remotes exist
+ if ! git remote | grep -q "^${NIXPKGS_UPSTREAM_REMOTE}$"; then
+ log "ERROR: Remote '$NIXPKGS_UPSTREAM_REMOTE' not found"
+ log "Available remotes: $(git remote | tr '\n' ' ')"
+ exit 1
+ fi
+
+ if ! git remote | grep -q "^${NIXPKGS_FORK_REMOTE}$"; then
+ log "ERROR: Remote '$NIXPKGS_FORK_REMOTE' not found"
+ log "Available remotes: $(git remote | tr '\n' ' ')"
+ exit 1
+ fi
+}
+
+setup_worktree() {
+ cd "$NIXPKGS_REPO_PATH"
+
+ # Remove existing worktree if it exists
+ if [ -d "$NIXPKGS_WORKTREE_PATH" ]; then
+ log "Removing existing worktree at $NIXPKGS_WORKTREE_PATH"
+ git worktree remove "$NIXPKGS_WORKTREE_PATH" --force 2>&1 | tee -a "$LOG_FILE" || true
+ fi
+
+ # Delete local consolidated branch if it exists (we'll recreate it fresh)
+ if git show-ref --verify --quiet "refs/heads/$NIXPKGS_CONSOLIDATED_BRANCH"; then
+ log "Deleting existing local branch $NIXPKGS_CONSOLIDATED_BRANCH"
+ git branch -D "$NIXPKGS_CONSOLIDATED_BRANCH" 2>&1 | tee -a "$LOG_FILE" || true
+ fi
+
+ # Create new worktree with consolidated branch from base
+ log "Creating worktree at $NIXPKGS_WORKTREE_PATH"
+ log "Branching from $NIXPKGS_UPSTREAM_REMOTE/$BASE_BRANCH"
+
+ if git worktree add -b "$NIXPKGS_CONSOLIDATED_BRANCH" \
+ "$NIXPKGS_WORKTREE_PATH" \
+ "$NIXPKGS_UPSTREAM_REMOTE/$BASE_BRANCH" 2>&1 | tee -a "$LOG_FILE"; then
+ WORKTREE_CREATED=true
+ log "Worktree created successfully"
+ else
+ log "ERROR: Failed to create worktree"
+ exit 1
+ fi
+}
+
+consolidate_branches() {
+ local commit_count=0
+ cd "$NIXPKGS_WORKTREE_PATH"
+
+ for branch in "${WIP_BRANCHES[@]}"; do
+ log "Processing WIP branch: $branch"
+ FAILED_BRANCH="$branch"
+
+ # Check if branch exists on fork remote
+ if ! git rev-parse --verify "$NIXPKGS_FORK_REMOTE/$branch" >/dev/null 2>&1; then
+ log "ERROR: Branch $branch not found on remote $NIXPKGS_FORK_REMOTE"
+ exit 1
+ fi
+
+ # Get commits that are in WIP branch but not in base
+ log "Finding commits to cherry-pick from $branch..."
+ COMMITS=$(git log --reverse --pretty=format:%H \
+ "$NIXPKGS_UPSTREAM_REMOTE/$BASE_BRANCH..$NIXPKGS_FORK_REMOTE/$branch" 2>/dev/null || true)
+
+ if [ -z "$COMMITS" ]; then
+ log "No commits to cherry-pick from $branch (already in base or branch is behind base)"
+ COMPLETED_BRANCHES+=("$branch")
+ continue
+ fi
+
+ # Count commits
+ local branch_commit_count=$(echo "$COMMITS" | wc -l)
+ log "Found $branch_commit_count commit(s) to cherry-pick"
+
+ # Cherry-pick each commit
+ for commit in $COMMITS; do
+ FAILED_COMMIT="$commit"
+ local commit_msg=$(git log -1 --pretty=format:%s "$commit")
+ log "Cherry-picking $commit: $commit_msg"
+
+ if ! git cherry-pick "$commit" 2>&1 | tee -a "$LOG_FILE"; then
+ log "ERROR: Cherry-pick failed for commit $commit in branch $branch"
+ log "Aborting cherry-pick..."
+ git cherry-pick --abort || true
+ exit 1
+ fi
+
+ ((commit_count++))
+ done
+
+ log "Successfully cherry-picked $branch_commit_count commit(s) from $branch"
+ COMPLETED_BRANCHES+=("$branch")
+ FAILED_BRANCH=""
+ FAILED_COMMIT=""
+ done
+
+ TOTAL_COMMITS=$commit_count
+ log "Total commits cherry-picked: $TOTAL_COMMITS"
+}
+
+main() {
+ log "=== Starting nixpkgs branch consolidation ==="
+ log "Repository: $NIXPKGS_REPO_PATH"
+ log "Worktree path: $NIXPKGS_WORKTREE_PATH"
+ log "Upstream remote: $NIXPKGS_UPSTREAM_REMOTE"
+ log "Fork remote: $NIXPKGS_FORK_REMOTE"
+ log "Consolidated branch: $NIXPKGS_CONSOLIDATED_BRANCH"
+ log "Configuration: $NIXPKGS_CONFIG_FILE"
+ log "Dry run: $DRY_RUN"
+
+ # Parse configuration
+ parse_config
+
+ # Ensure repository exists
+ ensure_repo
+
+ # Fetch latest from remotes
+ log "Fetching from $NIXPKGS_UPSTREAM_REMOTE..."
+ cd "$NIXPKGS_REPO_PATH"
+ git fetch "$NIXPKGS_UPSTREAM_REMOTE" 2>&1 | tee -a "$LOG_FILE"
+
+ log "Fetching from $NIXPKGS_FORK_REMOTE..."
+ git fetch "$NIXPKGS_FORK_REMOTE" 2>&1 | tee -a "$LOG_FILE"
+
+ # Verify base branch exists
+ if ! git rev-parse --verify "$NIXPKGS_UPSTREAM_REMOTE/$BASE_BRANCH" >/dev/null 2>&1; then
+ log "ERROR: Base branch $BASE_BRANCH not found on remote $NIXPKGS_UPSTREAM_REMOTE"
+ exit 1
+ fi
+
+ # Setup worktree for consolidation work
+ setup_worktree
+
+ # Cherry-pick WIP branches (happens in worktree)
+ consolidate_branches
+
+ # Show summary
+ log "=== Consolidation Summary ==="
+ log "Base branch: $BASE_BRANCH"
+ log "WIP branches consolidated: ${#COMPLETED_BRANCHES[@]}"
+ for branch in "${COMPLETED_BRANCHES[@]}"; do
+ log " - $branch"
+ done
+ log "Total commits: $TOTAL_COMMITS"
+
+ # Push to remote (from worktree)
+ cd "$NIXPKGS_WORKTREE_PATH"
+
+ if [ "$DRY_RUN" = "true" ]; then
+ log "DRY RUN: Would push $NIXPKGS_CONSOLIDATED_BRANCH to $NIXPKGS_FORK_REMOTE"
+ log "Current HEAD: $(git rev-parse HEAD)"
+ log "Branch differs from upstream/$BASE_BRANCH by $TOTAL_COMMITS commits"
+ else
+ log "Force pushing $NIXPKGS_CONSOLIDATED_BRANCH to $NIXPKGS_FORK_REMOTE (with --force-with-lease)..."
+ if git push --force-with-lease "$NIXPKGS_FORK_REMOTE" "$NIXPKGS_CONSOLIDATED_BRANCH" 2>&1 | tee -a "$LOG_FILE"; then
+ log "Successfully pushed to $NIXPKGS_FORK_REMOTE/$NIXPKGS_CONSOLIDATED_BRANCH"
+ else
+ log "ERROR: Push failed. This might mean the remote branch was updated by someone else."
+ log "Check the remote branch and try again."
+ exit 1
+ fi
+ fi
+
+ # Success notification
+ local branches_str=$(IFS=$'\n'; echo "${COMPLETED_BRANCHES[*]}")
+ local success_msg="Consolidated ${#COMPLETED_BRANCHES[@]} branch(es) into $NIXPKGS_CONSOLIDATED_BRANCH
+
+Base: $BASE_BRANCH
+Branches:
+$branches_str
+
+Total commits: $TOTAL_COMMITS
+
+${DRY_RUN:+[DRY RUN] }Pushed to $NIXPKGS_FORK_REMOTE/$NIXPKGS_CONSOLIDATED_BRANCH"
+
+ notify "default" \
+ "✅ Nixpkgs Consolidation Success" \
+ "$success_msg" \
+ "white_check_mark,git,nixpkgs"
+
+ log "=== Consolidation completed successfully ==="
+}
+
+main "$@"
tools/nixpkgs-consolidate/README.md
@@ -0,0 +1,513 @@
+# Nixpkgs Branch Consolidation Tool
+
+Automatically consolidate multiple WIP/PR branches from your nixpkgs fork into a single consolidated branch using cherry-picking.
+
+## Overview
+
+This tool helps manage multiple work-in-progress (WIP) branches in your nixpkgs fork by:
+
+1. Fetching latest from upstream nixpkgs
+2. Creating a fresh consolidated branch from the base (e.g., `nixos-unstable`)
+3. Cherry-picking commits from each WIP branch in order
+4. Force-pushing the result to your fork
+5. Sending notifications on success/failure
+
+**Key Features:**
+
+- Uses git worktrees to avoid interfering with your main nixpkgs checkout
+- Cherry-pick strategy (no merge commits)
+- Handles conflicts gracefully with detailed error reporting
+- Integrates with ntfy for notifications
+- Dry-run mode for safe testing
+- Comprehensive logging
+
+## Use Case
+
+Perfect for nixpkgs contributors who:
+
+- Maintain multiple WIP branches for different packages/features
+- Want to test all changes together in a single branch
+- Need an automated way to keep a consolidated branch up-to-date with upstream
+- Want to reference one branch in their NixOS flake instead of managing overlays
+
+## Installation
+
+### Manual Usage
+
+```bash
+# Run directly from the tools directory
+./tools/nixpkgs-consolidate/nixpkgs-consolidate.sh
+```
+
+### Nix Package (Recommended)
+
+```bash
+# Build and install
+nix build .#nixpkgs-consolidate
+nix profile install .#nixpkgs-consolidate
+
+# Run
+nixpkgs-consolidate
+```
+
+### Automated with NixOS Module
+
+Add to your system configuration (e.g., `systems/aomi/extra.nix`):
+
+```nix
+{
+ services.nixpkgs-consolidate = {
+ enable = true;
+ user = "vincent";
+ schedule = "daily"; # or "Mon,Thu *-*-* 02:00:00"
+ configFile = "/home/vincent/.config/nixpkgs-automation/branches.conf";
+ };
+}
+```
+
+## Configuration
+
+### Setup
+
+1. **Create configuration directory:**
+ ```bash
+ mkdir -p ~/.config/nixpkgs-automation
+ ```
+
+2. **Copy example configuration:**
+ ```bash
+ cp tools/nixpkgs-consolidate/branches.conf.example \
+ ~/.config/nixpkgs-automation/branches.conf
+ ```
+
+3. **Edit configuration:**
+ ```bash
+ vim ~/.config/nixpkgs-automation/branches.conf
+ ```
+
+### Configuration File Format
+
+`~/.config/nixpkgs-automation/branches.conf`:
+
+```bash
+# Base branch (required)
+base: nixos-unstable
+
+# WIP branches to consolidate (one per line)
+wip-package-foo
+wip-fix-bar
+pr-12345-add-service
+```
+
+**Rules:**
+
+- First line starting with `base:` defines the upstream base branch
+- Other non-comment, non-empty lines are treated as WIP branch names
+- Branches are cherry-picked in the order listed
+- Comments start with `#`
+
+### Environment Variables
+
+Override defaults with environment variables:
+
+| Variable | Default | Description |
+|----------|---------|-------------|
+| `NIXPKGS_REPO_PATH` | `~/src/nixpkgs` | Path to nixpkgs repository |
+| `NIXPKGS_WORKTREE_PATH` | `~/src/nixpkgs-consolidate-work` | Worktree for consolidation |
+| `NIXPKGS_UPSTREAM_REMOTE` | `upstream` | Upstream remote name |
+| `NIXPKGS_FORK_REMOTE` | `origin` | Your fork remote name |
+| `NIXPKGS_CONSOLIDATED_BRANCH` | `wip-consolidated` | Output branch name |
+| `NIXPKGS_CONFIG_FILE` | `~/.config/nixpkgs-automation/branches.conf` | Config file path |
+| `NTFY_SERVER` | `https://ntfy.sbr.pm` | ntfy server URL |
+| `NTFY_TOPIC` | `git-builds` | ntfy notification topic |
+| `NTFY_TOKEN_FILE` | `/run/agenix/ntfy-token` | ntfy auth token file |
+| `LOG_DIR` | `/var/log/nixpkgs-consolidate` | Log directory |
+| `DRY_RUN` | `false` | Set to `true` for dry-run |
+
+## Usage
+
+### Basic Usage
+
+```bash
+# Run with defaults
+nixpkgs-consolidate
+
+# Or directly:
+./tools/nixpkgs-consolidate/nixpkgs-consolidate.sh
+```
+
+### Dry Run (Recommended First)
+
+Test without actually pushing:
+
+```bash
+DRY_RUN=true nixpkgs-consolidate
+```
+
+This will:
+- Parse configuration
+- Fetch from remotes
+- Create worktree
+- Cherry-pick all commits
+- Show what would be pushed
+- NOT actually push to remote
+
+### Custom Branch Name
+
+```bash
+NIXPKGS_CONSOLIDATED_BRANCH=personal-main nixpkgs-consolidate
+```
+
+### Different Base Branch
+
+```bash
+# Edit config file to change base:
+echo "base: master" > ~/.config/nixpkgs-automation/branches.conf
+# Then run:
+nixpkgs-consolidate
+```
+
+## Workflow
+
+### Initial Setup
+
+1. **Fork nixpkgs on GitHub** (if you haven't already)
+
+2. **Clone nixpkgs locally:**
+ ```bash
+ git clone git@github.com:NixOS/nixpkgs.git ~/src/nixpkgs
+ cd ~/src/nixpkgs
+ git remote add upstream git@github.com:NixOS/nixpkgs.git
+ # Assumes 'origin' points to your fork
+ ```
+
+3. **Create WIP branches:**
+ ```bash
+ git checkout -b wip-my-feature nixos-unstable
+ # Make changes...
+ git push origin wip-my-feature
+ ```
+
+4. **Configure consolidation:**
+ ```bash
+ mkdir -p ~/.config/nixpkgs-automation
+ cat > ~/.config/nixpkgs-automation/branches.conf <<EOF
+ base: nixos-unstable
+ wip-my-feature
+ wip-another-feature
+ EOF
+ ```
+
+5. **Run consolidation:**
+ ```bash
+ DRY_RUN=true nixpkgs-consolidate # Test first
+ nixpkgs-consolidate # Actually push
+ ```
+
+### Regular Usage
+
+1. **Work on WIP branches as usual:**
+ ```bash
+ git checkout wip-my-feature
+ # Make changes...
+ git commit -m "Update package foo"
+ git push origin wip-my-feature
+ ```
+
+2. **Run consolidation** (manually or via systemd timer):
+ ```bash
+ nixpkgs-consolidate
+ ```
+
+3. **Use consolidated branch in your flake:**
+ ```nix
+ {
+ inputs = {
+ nixpkgs.url = "github:vdemeester/nixpkgs/wip-consolidated";
+ };
+ }
+ ```
+
+4. **Clean up when PRs land:**
+ - Remove merged branches from `branches.conf`
+ - Delete remote branches: `git push origin --delete wip-merged-feature`
+
+## Git Workflow Details
+
+### Worktree Usage
+
+The script uses git worktrees to keep your main repository clean:
+
+1. **Main repository** (`~/src/nixpkgs`):
+ - Stays on whatever branch you're working on
+ - Not affected by consolidation process
+
+2. **Consolidation worktree** (`~/src/nixpkgs-consolidate-work`):
+ - Created automatically
+ - Used for all cherry-picking operations
+ - Removed automatically after completion
+
+### Cherry-Pick Strategy
+
+For each WIP branch:
+
+```bash
+# Get commits unique to WIP branch
+commits = $(git log upstream/nixos-unstable..origin/wip-feature)
+
+# Cherry-pick each commit
+for commit in commits; do
+ git cherry-pick $commit
+done
+```
+
+**Why cherry-pick instead of merge?**
+
+- Clean linear history
+- No merge commits
+- Easy to see what changed
+- Better for testing package updates
+
+### Conflict Handling
+
+If a cherry-pick conflicts:
+
+1. Script aborts immediately
+2. Sends detailed notification with:
+ - Which branch caused the conflict
+ - Which commit failed
+ - What was successfully completed
+3. Cleans up worktree
+4. Returns non-zero exit code
+
+**Resolution:**
+
+- Fix conflicts in the WIP branch
+- Rebase WIP branch on latest upstream
+- Run consolidation again
+
+## Notifications
+
+### Success Notification
+
+Sent to `ntfy.sbr.pm/git-builds`:
+
+```
+✅ Nixpkgs Consolidation Success
+
+Consolidated 3 branch(es) into wip-consolidated
+
+Base: nixos-unstable
+Branches:
+wip-package-foo
+wip-fix-bar
+pr-12345-add-service
+
+Total commits: 15
+
+Pushed to origin/wip-consolidated
+```
+
+### Failure Notification
+
+```
+❌ Nixpkgs Consolidation Failed
+
+Cherry-pick conflict in branch: wip-fix-bar
+Commit: abc123def456
+
+Base: nixos-unstable
+Completed: wip-package-foo
+Failed at: wip-fix-bar
+
+Logs: /var/log/nixpkgs-consolidate/20260108-103015.log
+```
+
+## Logging
+
+Logs are written to:
+
+```
+/var/log/nixpkgs-consolidate/YYYYMMDD-HHMMSS.log
+```
+
+**Log format:**
+
+```
+[2026-01-08 10:30:15] Starting nixpkgs branch consolidation
+[2026-01-08 10:30:16] Reading configuration from ~/.config/nixpkgs-automation/branches.conf
+[2026-01-08 10:30:17] Base branch: nixos-unstable
+[2026-01-08 10:30:17] WIP branches (3): wip-foo wip-bar pr-12345
+[2026-01-08 10:30:20] Fetching from upstream...
+[2026-01-08 10:30:25] Creating worktree at ~/src/nixpkgs-consolidate-work
+[2026-01-08 10:30:26] Processing WIP branch: wip-foo
+[2026-01-08 10:30:27] Found 5 commit(s) to cherry-pick
+[2026-01-08 10:30:28] Cherry-picking abc123: Update foo to 1.2.3
+...
+```
+
+## Troubleshooting
+
+### "Configuration file not found"
+
+**Problem:** `~/.config/nixpkgs-automation/branches.conf` doesn't exist
+
+**Solution:**
+```bash
+mkdir -p ~/.config/nixpkgs-automation
+cp tools/nixpkgs-consolidate/branches.conf.example \
+ ~/.config/nixpkgs-automation/branches.conf
+vim ~/.config/nixpkgs-automation/branches.conf
+```
+
+### "Remote 'upstream' not found"
+
+**Problem:** nixpkgs repository doesn't have upstream remote configured
+
+**Solution:**
+```bash
+cd ~/src/nixpkgs
+git remote add upstream git@github.com:NixOS/nixpkgs.git
+git fetch upstream
+```
+
+### "Branch not found on remote origin"
+
+**Problem:** WIP branch listed in config doesn't exist in your fork
+
+**Solution:**
+- Check branch exists: `git ls-remote origin | grep wip-feature`
+- Push branch to fork: `git push origin wip-feature`
+- Or remove from config if no longer needed
+
+### Cherry-pick conflicts
+
+**Problem:** Commits conflict when cherry-picked
+
+**Solution:**
+
+1. Check the logs to see which branch caused the conflict
+2. Rebase that branch on latest upstream:
+ ```bash
+ git checkout wip-problematic-branch
+ git fetch upstream
+ git rebase upstream/nixos-unstable
+ git push --force-with-lease origin wip-problematic-branch
+ ```
+3. Run consolidation again
+
+### Force push rejected
+
+**Problem:** `git push --force-with-lease` fails
+
+**Cause:** Someone else (or another process) updated the consolidated branch
+
+**Solution:**
+- If it was you from another machine: Re-run consolidation
+- If unexpected: Check remote branch with `git log origin/wip-consolidated`
+
+## Integration with NixOS Flake
+
+Once consolidation is running, reference the consolidated branch in your flake:
+
+```nix
+{
+ inputs = {
+ # Use your consolidated branch instead of official nixpkgs
+ nixpkgs.url = "github:vdemeester/nixpkgs/wip-consolidated";
+
+ # Or for testing with local changes:
+ # nixpkgs.url = "path:/home/vincent/src/nixpkgs";
+ };
+
+ outputs = { nixpkgs, ... }: {
+ # Your configuration...
+ };
+}
+```
+
+**Benefits:**
+
+- Test all your WIP packages together
+- Single flake input instead of multiple overlays
+- Automatically updated as you run consolidation
+
+## Automation with Systemd
+
+### Manual Trigger
+
+```bash
+systemctl start nixpkgs-consolidate.service
+```
+
+### Check Status
+
+```bash
+systemctl status nixpkgs-consolidate.service
+journalctl -u nixpkgs-consolidate.service -f
+```
+
+### List Timer Schedule
+
+```bash
+systemctl list-timers | grep nixpkgs
+```
+
+### Modify Schedule
+
+Edit your system configuration:
+
+```nix
+services.nixpkgs-consolidate = {
+ enable = true;
+ schedule = "Mon,Thu *-*-* 02:00:00"; # Monday and Thursday at 2 AM
+};
+```
+
+## Best Practices
+
+1. **Test with dry-run first:**
+ ```bash
+ DRY_RUN=true nixpkgs-consolidate
+ ```
+
+2. **Keep WIP branches rebased:**
+ ```bash
+ git checkout wip-feature
+ git fetch upstream
+ git rebase upstream/nixos-unstable
+ git push --force-with-lease origin wip-feature
+ ```
+
+3. **Clean up merged branches:**
+ - Remove from `branches.conf`
+ - Delete from fork: `git push origin --delete wip-merged`
+
+4. **Use descriptive branch names:**
+ - `wip-update-python-312` ✅
+ - `wip-stuff` ❌
+
+5. **Order matters in config:**
+ - Put independent changes first
+ - Put dependent changes later
+
+6. **Monitor notifications:**
+ - Subscribe to ntfy topic `git-builds`
+ - Fix conflicts promptly
+
+## Security
+
+- **Git Authentication:** Uses existing SSH keys (`~/.ssh/`)
+- **ntfy Token:** Stored in agenix (`/run/agenix/ntfy-token`)
+- **Logs:** Written to `/var/log` with restricted permissions
+- **Systemd:** Service runs with security hardening (ProtectSystem, ProtectHome, etc.)
+
+## See Also
+
+- [Nixpkgs Contributing Guide](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md)
+- [Git Worktrees Documentation](https://git-scm.com/docs/git-worktree)
+- [ntfy Documentation](https://ntfy.sh)
+
+## License
+
+MIT