#!/usr/bin/env bash # Exit immediately if a command exits with a non-zero status. set -e # Treat unset variables as an error when substituting. set -u # Pipestatus set -o pipefail # --- Configuration --- : "${NIX_CONFIG_DIR:=/etc/nix-darwin}" # Path to your Nix configuration directory : "${GIT_REMOTE_NAME:=origin}" GIT_BRANCH_NAME_CMD="git -C \"$NIX_CONFIG_DIR\" rev-parse --abbrev-ref HEAD" : "${GIT_BRANCH_NAME:=$(eval "$GIT_BRANCH_NAME_CMD" 2>/dev/null || echo main)}" # Ollama configuration : "${OLLAMA_MODEL:=deepseek-r1:1.5b}" # Ollama model : "${OLLAMA_HOST:=http://localhost:11434}" # Default Ollama API host # Hardcoded Nix switch command NIX_SWITCH_COMMAND="sudo darwin-rebuild switch" # If your flake requires a specific attribute, modify it here, e.g.: # NIX_SWITCH_COMMAND="sudo darwin-rebuild switch --flake .#yourDarwinConfigName" # --- End Configuration --- # Function to check if a command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Check for dependencies if ! command_exists ollama; then echo "❌ Error: ollama CLI not found. Please install Ollama." exit 1 fi if ! command_exists jq; then echo "❌ Error: jq not found. Please install jq." exit 1 fi if ! command_exists code; then echo "❌ Error: 'code' (VSCode CLI) not found. Please ensure it's in your PATH." echo " You might need to install it from within VSCode (View -> Command Palette -> 'Shell Command: Install code command in PATH')." exit 1 fi if ! command_exists sudo; then echo "❌ Error: 'sudo' command not found. This script requires sudo for 'darwin-rebuild switch'." exit 1 fi if ! command_exists darwin-rebuild; then echo "❌ Error: 'darwin-rebuild' command not found. Please ensure Nix-Darwin is installed correctly." exit 1 fi # --- VSCode Integration --- echo "Nix config directory: $NIX_CONFIG_DIR" echo "-----------------------------------------------------" echo "💻 Opening VSCode to edit Nix configuration at $NIX_CONFIG_DIR..." echo " The script will continue after you close VSCode." if ! code --wait "$NIX_CONFIG_DIR"; then echo "❌ Error: 'code --wait \"$NIX_CONFIG_DIR\"' failed." echo " Please ensure VSCode is installed correctly and the 'code' command with '--wait' works." exit 1 fi echo "✅ VSCode closed. Proceeding with the script..." echo "-----------------------------------------------------" # --- End VSCode Integration --- # Function to generate commit message using Ollama generate_commit_message() { local diff_content diff_content=$(git -C "$NIX_CONFIG_DIR" diff --staged) if [ -z "$diff_content" ]; then echo "chore: No changes to commit" return fi local max_diff_lines=150 local truncated_diff truncated_diff=$(echo "$diff_content" | head -n "$max_diff_lines") if [ "$(echo "$diff_content" | wc -l)" -gt "$max_diff_lines" ]; then truncated_diff+=$'\n\n... (diff truncated) ...' fi local prompt prompt=$(cat <(): [optional body] [optional footer] Allowed types: - feat: A new feature or package addition - fix: A bug fix in the configuration - docs: Documentation changes - style: Code style changes (formatting, whitespace) - refactor: Code changes that neither fix a bug nor add a feature - perf: A code change that improves performance - test: Adding missing tests or correcting existing tests - build: Changes that affect the build system or external dependencies - ci: Changes to CI configuration files and scripts - chore: Other changes that don't modify source or test files Consider these for Nix configurations: - Adding a new package: 'feat' or 'feat(pkg)' or 'feat(darwin)' - Updating flake inputs: 'build(deps): update flake inputs' or 'chore(deps): update flake inputs' - Modifying system settings: 'refactor(system)' or 'fix(system)' - Changing themes or appearances: 'style(theme)' - General maintenance or minor tweaks: 'chore' or 'refactor' Focus on a short, descriptive subject line (max 50-70 chars). If no specific scope is obvious, you can omit it. If the changes are minor, 'chore' or 'refactor' are good defaults. Git Diff: --- $truncated_diff --- Generate ONLY the commit message. Do not include any other explanatory text. EOF ) echo "🧠 Generating commit message with Ollama ($OLLAMA_MODEL)..." local json_payload json_payload=$(jq -n \ --arg model "$OLLAMA_MODEL" \ --arg prompt "$prompt" \ '{ model: $model, prompt: $prompt, stream: false, options: { temperature: 0.3, num_predict: 150 } }') # Check if jq failed (e.g., if variables were not set properly for jq) if [ -z "$json_payload" ]; then echo "Error: Failed to create JSON payload with jq." >&2 return fi local ollama_response ollama_response=$(curl -s --fail \ -X POST \ -H "Content-Type: application/json" \ --data "$json_payload" \ "$OLLAMA_HOST/api/generate") if [ -z "$ollama_response" ]; then echo "❌ Ollama request failed or returned empty. Using default commit message." echo "chore: Automated Nix config update $(date +'%Y-%m-%d %H:%M:%S')" return fi local generated_message generated_message=$(echo "$ollama_response" | jq -r '.response | sub("^\\s*"; "") | sub("\\s*$"; "")') if [ -z "$generated_message" ] || [[ "$generated_message" == "null" ]]; then echo "⚠️ Ollama did not return a valid message. Using default commit message." echo "chore: Automated Nix config update $(date +'%Y-%m-%d %H:%M:%S')" else if [[ "$generated_message" =~ ^[a-z]+(\([a-zA-Z0-9_-]+\))?!?: ]]; then echo "$generated_message" else echo "⚠️ Ollama message doesn't look like a conventional commit. Prepending 'chore: '." echo "chore: $generated_message" fi fi } echo "Current effective configuration:" echo "Nix config directory: $NIX_CONFIG_DIR" echo "Nix switch command to run: $NIX_SWITCH_COMMAND" echo "Git remote: $GIT_REMOTE_NAME" echo "Git branch: $GIT_BRANCH_NAME" echo "Ollama model: $OLLAMA_MODEL" echo "-----------------------------------------------------" # 1. Navigate to the Nix config Git repository using pushd # This is done *before* the switch command so darwin-rebuild finds flake.nix echo "🔄 Changing directory to $NIX_CONFIG_DIR using pushd..." if ! pushd "$NIX_CONFIG_DIR" > /dev/null; then echo "❌ Failed to pushd into $NIX_CONFIG_DIR. Is the path correct and accessible?" exit 1 fi echo "✅ Successfully changed to $NIX_CONFIG_DIR (current directory: $PWD)." echo "-----------------------------------------------------" # Ensure we popd at the end, even if errors occur. # A trap is more robust, but for now, manual popd before exits and at the end. _cleanup() { echo "🔄 Returning to original directory using popd..." if ! popd > /dev/null; then echo "⚠️ Failed to popd. Current directory ($PWD) might not be the original one." fi } # trap _cleanup EXIT # Uncomment this for more robust cleanup on any exit # 2. Run the Nix switch command echo "🚀 Starting Nix switch: $NIX_SWITCH_COMMAND" echo " This command will be run from: $PWD" # The '|| echo ...' construct ensures the script continues even if the command fails, # due to 'set -e'. A warning will be printed on failure. # Using 'eval' to correctly handle the command string if it contains spaces and needs sudo. eval "$NIX_SWITCH_COMMAND" || echo "⚠️ Nix switch command exited with a non-zero status. Continuing as requested..." echo "ℹ️ Nix switch command execution finished. Proceeding..." echo "-----------------------------------------------------" # 3. Check for changes echo "🔍 Checking for Git changes in current directory ($PWD)..." if git diff --quiet && git diff --staged --quiet; then echo "ℹ️ No changes detected in $NIX_CONFIG_DIR. Nothing to commit or push." _cleanup # Call cleanup function exit 0 fi echo "✅ Changes detected." echo "-----------------------------------------------------" # 4. Add changes echo "💾 Staging all changes in current directory ($PWD)..." git add . echo "-----------------------------------------------------" # 5. Generate Commit Message COMMIT_MESSAGE=$(generate_commit_message) # generate_commit_message uses git -C "$NIX_CONFIG_DIR" echo "💬 Generated commit message:" echo "$COMMIT_MESSAGE" echo "-----------------------------------------------------" # 6. Commit and Push echo "✍️ Committing to local repository in current directory ($PWD)..." if printf '%s\n' "$COMMIT_MESSAGE" | git commit -F - ; then echo "✅ Commit successful." else echo "❌ Git commit failed. Please check the repository status and the generated message." echo " Ensure you have commit permissions for $NIX_CONFIG_DIR." _cleanup # Call cleanup function exit 1 fi echo "📡 Pushing to $GIT_REMOTE_NAME $GIT_BRANCH_NAME from current directory ($PWD)..." if ! git push "$GIT_REMOTE_NAME" "$GIT_BRANCH_NAME"; then echo "⚠️ Git push failed. The commit was made locally, but not pushed." echo " Please check your remote connection, permissions, and push manually if needed." else echo "✅ Push successful." fi echo "-----------------------------------------------------" _cleanup # Call cleanup function echo "🎉 All done!"