From 1a22220b187cf0c51281e1275329320b3b895c5f Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 15:54:33 -0400 Subject: [PATCH 01/12] Make nix package derivations overridable - Make packages overridable - Make overlays use the previous pkgs version to build packages - Make home-manager and nixos modules use ambient nixpkgs for building - Add option gitBasePackage to nixos and home-manager modules to allow overriding base git package through configuration --- default.nix | 159 +++++++++++++++ flake.nix | 547 ++++++++++++++++++++-------------------------------- 2 files changed, 371 insertions(+), 335 deletions(-) create mode 100644 default.nix diff --git a/default.nix b/default.nix new file mode 100644 index 0000000000..00dd59f2cb --- /dev/null +++ b/default.nix @@ -0,0 +1,159 @@ +{ pkgs, git, ... }: + +let + # Pin Rust 1.93.0 via rust-overlay + rustToolchain = pkgs.rust-bin.stable."1.93.0".default.override { + extensions = [ + "rust-src" + "rust-analyzer" + "llvm-tools-preview" + ]; + }; + + # Create a custom rustPlatform using the pinned toolchain + rustPlatform = pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; + }; + + # Build the git-ai binary using the pinned Rust toolchain + git-ai-unwrapped = rustPlatform.buildRustPackage { + pname = "git-ai"; + version = "1.4.9"; + + src = ./.; + + cargoLock = { + lockFile = ./Cargo.lock; + }; + + # Prevent openssl-sys from vendoring OpenSSL (which requires perl). + # Instead, link against the system OpenSSL provided by buildInputs. + OPENSSL_NO_VENDOR = "1"; + + # Native build inputs needed for rusqlite with bundled SQLite + nativeBuildInputs = with pkgs; [ + pkg-config + ] ++ [ + rustPlatform.bindgenHook # For rusqlite bundled builds + ]; + + # Build inputs for runtime dependencies + buildInputs = with pkgs; [ + # rusqlite bundled mode compiles its own SQLite, but needs these headers + sqlite + # openssl-sys needs system OpenSSL headers and libraries + openssl + ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ + # macOS-specific dependencies + libiconv + apple-sdk_15 + ]; + + # Tests require git and specific setup + doCheck = false; + + meta = with pkgs.lib; { + description = "AI-powered Git wrapper that tracks AI-generated code changes"; + homepage = "https://github.com/acunniffe/git-ai"; + license = licenses.gpl3Plus; + maintainers = [ ]; + mainProgram = "git-ai"; + platforms = platforms.unix; + }; + }; + + # Wrapped version that sets up the git-ai environment properly + git-ai-wrapped = pkgs.writeShellScriptBin "git-ai" '' + # Ensure config directory exists + mkdir -p "$HOME/.git-ai" + + # Create config.json if it doesn't exist + if [ ! -f "$HOME/.git-ai/config.json" ]; then + # Find the system git (not our wrapper) + GIT_PATH="${git}/bin/git" + cat > "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < "$GITWRAP_DIR/git" <&2 + echo "Run 'cargo build' first, then retry." >&2 + exit 1 + fi + exec -a git "$BINARY" "\$@" + GITEOF + chmod +x "$GITWRAP_DIR/git" + + # Create git-ai wrapper + cat > "$GITWRAP_DIR/git-ai" <&2 + echo "Run 'cargo build' first, then retry." >&2 + exit 1 + fi + exec "$BINARY" "\$@" + GITAIEOF + chmod +x "$GITWRAP_DIR/git-ai" + + # Create git-og wrapper (bypasses git-ai, calls real git directly) + cat > "$GITWRAP_DIR/git-og" </dev/null || true + fi + + # Install lefthook git hooks (use real git, not the git-ai wrapper, + # since the dev binary may not be built yet) + PATH="${pkgs.git}/bin:$PATH" lefthook install 2>/dev/null || true + + # Set up environment for development + export RUST_BACKTRACE=1 + export RUST_LOG=debug + + echo "git-ai development environment" + echo "Rust version: $(rustc --version)" + echo "Cargo version: $(cargo --version)" + echo "" + if [ -x "$BINARY" ]; then + echo "Dev binary: $BINARY (ready)" + echo "Hooks installed." + else + echo "Dev binary: $BINARY (not built yet)" + echo "Run 'cargo build' to build, then hooks will be installed on next 'nix develop'." + fi + echo "" + echo "git, git-ai, git-og -> wrappers in $GITWRAP_DIR" + echo "Set GIT_AI_BUILD_TYPE=release for release builds." + ''; }; - }; - - # Create a complete package with git wrapper (for standalone use) - # The git-wrapper script ensures argv[0] is "git" when invoked as git - git-ai-package = pkgs.symlinkJoin { - name = "git-ai-${git-ai-unwrapped.version}"; - paths = [ git-ai-wrapped git-wrapper git-ai-unwrapped git-og ]; - # Create libexec symlink for Fork compatibility - # Fork looks for libexec relative to the git binary location - postBuild = '' - ln -s ${pkgs.git}/libexec $out/libexec - ''; - - meta = git-ai-unwrapped.meta // { - description = git-ai-unwrapped.meta.description + " (with git wrapper)"; + # Main packages + packages = pkgs.callPackage (inputs: + (pkgs.callPackage default inputs).packages + ) { }; + # Make app available for `nix run` + apps.default = flake-utils.lib.mkApp { + drv = self.packages.git-ai; + exePath = "/bin/git-ai"; }; - }; - - in - { - # Development shell with full Rust toolchain - devShells.default = pkgs.mkShell { - packages = [ - # Pinned Rust 1.93.0 toolchain (includes rustc, cargo, clippy, rustfmt, rust-analyzer) - rustToolchain - ] ++ (with pkgs; [ - # Build dependencies - pkg-config - - # Runtime dependencies for testing - # NOTE: git is NOT included as a package here. Instead, the - # shellHook creates wrapper scripts (git, git-ai, git-og) that - # point to the locally-built target/debug/git-ai binary, so that - # development builds are tested directly. Use `git-og` to bypass - # git-ai and call real git. - sqlite - - # Useful development tools - cargo-edit # cargo add, cargo rm, cargo upgrade - cargo-watch # Auto-rebuild on file changes - cargo-expand # Show macro expansions - cargo-llvm-cov # Code coverage via LLVM instrumentation - lefthook # Git hooks manager - go-task # Task runner (Taskfile.yml) - ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ - libiconv - apple-sdk_15 - ]); - - # Environment variables for development - shellHook = '' - # Unset DEVELOPER_DIR to avoid conflict between the default stdenv - # SDK (14.4) and apple-sdk_15 (15.5) baked into the clang wrapper. - unset DEVELOPER_DIR - - # Set up development git-ai wrappers for nix develop (Nix-specific; non-Nix devs use scripts/dev.sh) - BUILD_TYPE="''${GIT_AI_BUILD_TYPE:-debug}" - GITWRAP_DIR="$HOME/.git-ai-local-dev/gitwrap/bin" - TARGET_DIR="''${CARGO_TARGET_DIR:-$(pwd)/target}" - BINARY="$TARGET_DIR/$BUILD_TYPE/git-ai" - - mkdir -p "$GITWRAP_DIR" - - # Create git wrapper (preserves argv[0] as "git" for passthrough mode) - cat > "$GITWRAP_DIR/git" <&2 - echo "Run 'cargo build' first, then retry." >&2 - exit 1 -fi -exec -a git "$BINARY" "\$@" -GITEOF - chmod +x "$GITWRAP_DIR/git" - - # Create git-ai wrapper - cat > "$GITWRAP_DIR/git-ai" <&2 - echo "Run 'cargo build' first, then retry." >&2 - exit 1 -fi -exec "$BINARY" "\$@" -GITAIEOF - chmod +x "$GITWRAP_DIR/git-ai" - - # Create git-og wrapper (bypasses git-ai, calls real git directly) - cat > "$GITWRAP_DIR/git-og" </dev/null || true - fi - - # Install lefthook git hooks (use real git, not the git-ai wrapper, - # since the dev binary may not be built yet) - PATH="${pkgs.git}/bin:$PATH" lefthook install 2>/dev/null || true - - # Set up environment for development - export RUST_BACKTRACE=1 - export RUST_LOG=debug - - echo "git-ai development environment" - echo "Rust version: $(rustc --version)" - echo "Cargo version: $(cargo --version)" - echo "" - if [ -x "$BINARY" ]; then - echo "Dev binary: $BINARY (ready)" - echo "Hooks installed." - else - echo "Dev binary: $BINARY (not built yet)" - echo "Run 'cargo build' to build, then hooks will be installed on next 'nix develop'." - fi - echo "" - echo "git, git-ai, git-og -> wrappers in $GITWRAP_DIR" - echo "Set GIT_AI_BUILD_TYPE=release for release builds." - ''; - }; - - # Main packages - packages = { - # Unwrapped binary (just the git-ai executable) - unwrapped = git-ai-unwrapped; - - # Wrapped version with helper scripts - wrapped = git-ai-wrapped; - # Minimal package without git symlink (for Home Manager/environments with existing git) - minimal = git-ai-minimal; - - # Complete package with git/git-og symlinks (for standalone use) - default = git-ai-package; - - # Alias for clarity - git-ai = git-ai-package; - }; - - # Make app available for `nix run` - apps.default = flake-utils.lib.mkApp { - drv = git-ai-package; - exePath = "/bin/git-ai"; - }; - - # Nix flake checks: run with `nix flake check` - # Tests are not included here -- they require network access, Node.js, - # and the Graphite CLI, which are not available in the Nix sandbox. - # Tests run in CI via the existing test.yml workflow instead. - checks = - let - commonNativeBuildInputs = with pkgs; [ pkg-config ] - ++ [ rustPlatform.bindgenHook ]; - commonBuildInputs = with pkgs; [ sqlite openssl ] - ++ lib.optionals stdenv.hostPlatform.isDarwin [ - libiconv apple-sdk_15 - ]; - mkCheck = attrs: rustPlatform.buildRustPackage ({ - version = git-ai-unwrapped.version; - src = ./.; - cargoLock.lockFile = ./Cargo.lock; - OPENSSL_NO_VENDOR = "1"; - nativeBuildInputs = commonNativeBuildInputs; - buildInputs = commonBuildInputs; - installPhase = "mkdir -p $out"; - doCheck = false; - } // attrs); - in - { - # Build check - ensures the package builds - build = git-ai-unwrapped; - - # Clippy lint check with warnings as errors - clippy = mkCheck { - pname = "git-ai-clippy"; - buildPhase = '' - cargo clippy --all-targets -- -D warnings - ''; - }; + # Nix flake checks: run with `nix flake check` + # Tests are not included here -- they require network access, Node.js, + # and the Graphite CLI, which are not available in the Nix sandbox. + # Tests run in CI via the existing test.yml workflow instead. + checks = + let + commonNativeBuildInputs = with pkgs; [ pkg-config ] + ++ [ rustPlatform.bindgenHook ]; + commonBuildInputs = with pkgs; [ sqlite openssl ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ + libiconv apple-sdk_15 + ]; + mkCheck = attrs: rustPlatform.buildRustPackage ({ + version = self.packages.unwrapped.version; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + OPENSSL_NO_VENDOR = "1"; + nativeBuildInputs = commonNativeBuildInputs; + buildInputs = commonBuildInputs; + installPhase = "mkdir -p $out"; + doCheck = false; + } // attrs); + in + { + # Build check - ensures the package builds + build = self.packages.unwrapped; + + # Clippy lint check with warnings as errors + clippy = mkCheck { + pname = "git-ai-clippy"; + buildPhase = '' + cargo clippy --all-targets -- -D warnings + ''; + }; - # Format check - fmt = mkCheck { - pname = "git-ai-fmt"; - buildPhase = '' - cargo fmt -- --check - ''; - }; + # Format check + fmt = mkCheck { + pname = "git-ai-fmt"; + buildPhase = '' + cargo fmt -- --check + ''; + }; - # Doc check with warnings as errors - doc = mkCheck { - pname = "git-ai-doc"; - RUSTDOCFLAGS = "-D warnings"; - buildPhase = '' - cargo doc --no-deps - ''; + # Doc check with warnings as errors + doc = mkCheck { + pname = "git-ai-doc"; + RUSTDOCFLAGS = "-D warnings"; + buildPhase = '' + cargo doc --no-deps + ''; + }; }; - }; - # Formatter for `nix fmt` - formatter = pkgs.nixpkgs-fmt; - } + # Formatter for `nix fmt` + formatter = pkgs.nixpkgs-fmt; + } ) // { # System-independent outputs # Overlay for importing into other flakes - overlays.default = final: prev: { - git-ai = self.packages.${prev.stdenv.hostPlatform.system}.default; - git-ai-unwrapped = self.packages.${prev.stdenv.hostPlatform.system}.unwrapped; - }; + overlays.default = final: prev: + let + default' = prev.callPackage default { }; + in + { + git-ai = default'.packages.git-ai; + git-ai-unwrapped = default'.packages.unwrapped; + } + ; # NixOS module for system integration nixosModules.default = { config, lib, pkgs, ... }: @@ -366,6 +219,8 @@ GITOGEOF cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; + default' = pkgs.callPackage default { }; + # Build the config object, filtering out null values configFile = filterAttrs (n: v: v != null) { git_path = @@ -406,11 +261,22 @@ GITOGEOF package = mkOption { type = types.package; - default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + default = + if cfg.gitBasePackage == null + then default'.packages.git-ai + else default'.overrideAttrs { git = cfg.gitBasePackage; } + ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use."; }; + gitBasePackage = mkOption { + type = types.nullOr types.package; + default = null; + defaultText = literalExpression "pkgs.git"; + description = "The base git package to wrap.\n If null, defaults to pkgs.git"; + }; + installHooks = mkOption { type = types.bool; default = true; @@ -689,11 +555,22 @@ GITOGEOF package = mkOption { type = types.package; - default = self.packages.${pkgs.stdenv.hostPlatform.system}.default; + default = + if cfg.gitBasePackage == null + then default'.packages.git-ai + else default'.overrideAttrs { git = cfg.gitBasePackage; } + ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use."; }; + gitBasePackage = mkOption { + type = types.nullOr types.package; + default = null; + defaultText = literalExpression "pkgs.git"; + description = "The base git package to wrap.\n If null, defaults to pkgs.git"; + }; + installHooks = mkOption { type = types.bool; default = true; From bf8c00fc7d10937a79c47ab3c22109b3851e1305 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:12:00 -0400 Subject: [PATCH 02/12] Update flake.nix Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 368a7bea1e..0f8bbd3eee 100644 --- a/flake.nix +++ b/flake.nix @@ -138,7 +138,7 @@ ) { }; # Make app available for `nix run` apps.default = flake-utils.lib.mkApp { - drv = self.packages.git-ai; + drv = self.packages.${system}.git-ai; exePath = "/bin/git-ai"; }; From 3819105e05644f135792c15efdc96040ddccf4c7 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:12:13 -0400 Subject: [PATCH 03/12] Update flake.nix Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 0f8bbd3eee..71ae38f958 100644 --- a/flake.nix +++ b/flake.nix @@ -155,7 +155,7 @@ libiconv apple-sdk_15 ]; mkCheck = attrs: rustPlatform.buildRustPackage ({ - version = self.packages.unwrapped.version; + version = self.packages.${system}.unwrapped.version; src = ./.; cargoLock.lockFile = ./Cargo.lock; OPENSSL_NO_VENDOR = "1"; From b598b9f5659130e52e85dd197ea71a90ccb601b5 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:13:17 -0400 Subject: [PATCH 04/12] Update flake.nix Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 71ae38f958..61263ea720 100644 --- a/flake.nix +++ b/flake.nix @@ -167,7 +167,7 @@ in { # Build check - ensures the package builds - build = self.packages.unwrapped; + build = self.packages.${system}.unwrapped; # Clippy lint check with warnings as errors clippy = mkCheck { From a217333af86d3b86fe7d1657bd9fecf2352443a7 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:15:32 -0400 Subject: [PATCH 05/12] Fix suggested by AI review assistant --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 61263ea720..b604be7cc6 100644 --- a/flake.nix +++ b/flake.nix @@ -219,8 +219,6 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; - default' = pkgs.callPackage default { }; - # Build the config object, filtering out null values configFile = filterAttrs (n: v: v != null) { git_path = @@ -517,6 +515,8 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; + default' = pkgs.callPackage default { }; + # Build the config object, filtering out null values # We use explicit null checks since Nix 'or' only works for attribute access configFile = filterAttrs (n: v: v != null) { From ba4fd98fb20cd2e673316802556e3af85cd65cc5 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:17:21 -0400 Subject: [PATCH 06/12] Update flake.nix Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index b604be7cc6..afe12081cf 100644 --- a/flake.nix +++ b/flake.nix @@ -262,7 +262,7 @@ default = if cfg.gitBasePackage == null then default'.packages.git-ai - else default'.overrideAttrs { git = cfg.gitBasePackage; } + else (default'.override { git = cfg.gitBasePackage; }).packages.git-ai ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use."; From 3d84ef57a06be69598a4f996bfb715730f7ab2ce Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:54:50 -0400 Subject: [PATCH 07/12] Ensure overlay calls packages with rust overlay --- flake.nix | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index afe12081cf..2cb1bd0bcd 100644 --- a/flake.nix +++ b/flake.nix @@ -12,7 +12,7 @@ outputs = { self, nixpkgs, rust-overlay, flake-utils }: let - default = import ./default.nix; + default = import ./default.nix; in flake-utils.lib.eachDefaultSystem (system: let @@ -204,7 +204,11 @@ # Overlay for importing into other flakes overlays.default = final: prev: let - default' = prev.callPackage default { }; + default' = + ( + final.extend rust-overlay.overlays.default + ).callPackage default { } + ; in { git-ai = default'.packages.git-ai; From 2bc9d264fe7301ad91f5b1265f1c7158a35c20f6 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 16:58:40 -0400 Subject: [PATCH 08/12] Update flake.nix Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index 2cb1bd0bcd..f41c2516f2 100644 --- a/flake.nix +++ b/flake.nix @@ -562,7 +562,7 @@ default = if cfg.gitBasePackage == null then default'.packages.git-ai - else default'.overrideAttrs { git = cfg.gitBasePackage; } + else (default'.override { git = cfg.gitBasePackage; }).packages.git-ai ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use."; From 7456849a02c67cbae110c7e3b0022d85e6230f1d Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 17:02:59 -0400 Subject: [PATCH 09/12] Fix issue raised by review AI agent --- flake.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index f41c2516f2..e541e053ce 100644 --- a/flake.nix +++ b/flake.nix @@ -223,6 +223,8 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; + default' = pkgs.callPackage default { }; + # Build the config object, filtering out null values configFile = filterAttrs (n: v: v != null) { git_path = @@ -276,7 +278,7 @@ type = types.nullOr types.package; default = null; defaultText = literalExpression "pkgs.git"; - description = "The base git package to wrap.\n If null, defaults to pkgs.git"; + description = "The base git package to wrap.\n If null, defaults to pkgs.git\n Does nothing if `programs.git-ai.package` is specified"; }; installHooks = mkOption { From 16be91471217ff4a415424d32992aa0ec982d06a Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Wed, 13 May 2026 17:07:45 -0400 Subject: [PATCH 10/12] Generalized fix in previous commit\n\nThis ensures the rust overlay is always available when the package is called --- flake.nix | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/flake.nix b/flake.nix index e541e053ce..8736ecd3cd 100644 --- a/flake.nix +++ b/flake.nix @@ -13,6 +13,9 @@ outputs = { self, nixpkgs, rust-overlay, flake-utils }: let default = import ./default.nix; + defaultBasedOn = pkgs: + (pkgs.extend (rust-overlay.overlays.default)).callPackage default { } + ; in flake-utils.lib.eachDefaultSystem (system: let @@ -204,11 +207,7 @@ # Overlay for importing into other flakes overlays.default = final: prev: let - default' = - ( - final.extend rust-overlay.overlays.default - ).callPackage default { } - ; + default' = defaultBasedOn final; in { git-ai = default'.packages.git-ai; @@ -223,7 +222,7 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; - default' = pkgs.callPackage default { }; + default' = defaultBasedOn pkgs; # Build the config object, filtering out null values configFile = filterAttrs (n: v: v != null) { @@ -521,7 +520,7 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; - default' = pkgs.callPackage default { }; + default' = defaultBasedOn pkgs; # Build the config object, filtering out null values # We use explicit null checks since Nix 'or' only works for attribute access From e6d43fada4ef06bcee907056d24fbd0468611b12 Mon Sep 17 00:00:00 2001 From: Adriano Zambrana Marchetti Date: Thu, 14 May 2026 10:05:05 -0400 Subject: [PATCH 11/12] Fix extraneous attributes under packages Now override functions are exposed through the 'scope' attribute --- default.nix | 286 ++++++++++++++++++++++++++++------------------------ flake.nix | 23 +++-- 2 files changed, 164 insertions(+), 145 deletions(-) diff --git a/default.nix b/default.nix index 00dd59f2cb..443df10792 100644 --- a/default.nix +++ b/default.nix @@ -1,159 +1,177 @@ -{ pkgs, git, ... }: let # Pin Rust 1.93.0 via rust-overlay - rustToolchain = pkgs.rust-bin.stable."1.93.0".default.override { - extensions = [ - "rust-src" - "rust-analyzer" - "llvm-tools-preview" - ]; - }; + rustToolchain' = { pkgs, ... }: + pkgs.rust-bin.stable."1.93.0".default.override { + extensions = [ + "rust-src" + "rust-analyzer" + "llvm-tools-preview" + ]; + } + ; # Create a custom rustPlatform using the pinned toolchain - rustPlatform = pkgs.makeRustPlatform { - cargo = rustToolchain; - rustc = rustToolchain; - }; - - # Build the git-ai binary using the pinned Rust toolchain - git-ai-unwrapped = rustPlatform.buildRustPackage { - pname = "git-ai"; - version = "1.4.9"; - - src = ./.; - - cargoLock = { - lockFile = ./Cargo.lock; + rustPlatform' = { pkgs, git-ai, ... }: with git-ai.utils; + pkgs.makeRustPlatform { + cargo = rustToolchain; + rustc = rustToolchain; }; - # Prevent openssl-sys from vendoring OpenSSL (which requires perl). - # Instead, link against the system OpenSSL provided by buildInputs. - OPENSSL_NO_VENDOR = "1"; - - # Native build inputs needed for rusqlite with bundled SQLite - nativeBuildInputs = with pkgs; [ - pkg-config - ] ++ [ - rustPlatform.bindgenHook # For rusqlite bundled builds - ]; - - # Build inputs for runtime dependencies - buildInputs = with pkgs; [ - # rusqlite bundled mode compiles its own SQLite, but needs these headers - sqlite - # openssl-sys needs system OpenSSL headers and libraries - openssl - ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ - # macOS-specific dependencies - libiconv - apple-sdk_15 - ]; - - # Tests require git and specific setup - doCheck = false; - - meta = with pkgs.lib; { - description = "AI-powered Git wrapper that tracks AI-generated code changes"; - homepage = "https://github.com/acunniffe/git-ai"; - license = licenses.gpl3Plus; - maintainers = [ ]; - mainProgram = "git-ai"; - platforms = platforms.unix; + # Build the git-ai binary using the pinned Rust toolchain + git-ai-unwrapped' = {pkgs, git-ai, ...}: with git-ai.utils; + rustPlatform.buildRustPackage { + pname = "git-ai"; + version = "1.4.9"; + + src = ./.; + + cargoLock = { + lockFile = ./Cargo.lock; + }; + + # Prevent openssl-sys from vendoring OpenSSL (which requires perl). + # Instead, link against the system OpenSSL provided by buildInputs. + OPENSSL_NO_VENDOR = "1"; + + # Native build inputs needed for rusqlite with bundled SQLite + nativeBuildInputs = with pkgs; [ + pkg-config + ] ++ [ + rustPlatform.bindgenHook # For rusqlite bundled builds + ]; + + # Build inputs for runtime dependencies + buildInputs = with pkgs; [ + # rusqlite bundled mode compiles its own SQLite, but needs these headers + sqlite + # openssl-sys needs system OpenSSL headers and libraries + openssl + ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ + # macOS-specific dependencies + libiconv + apple-sdk_15 + ]; + + # Tests require git and specific setup + doCheck = false; + + meta = with pkgs.lib; { + description = "AI-powered Git wrapper that tracks AI-generated code changes"; + homepage = "https://github.com/acunniffe/git-ai"; + license = licenses.gpl3Plus; + maintainers = [ ]; + mainProgram = "git-ai"; + platforms = platforms.unix; + }; }; - }; # Wrapped version that sets up the git-ai environment properly - git-ai-wrapped = pkgs.writeShellScriptBin "git-ai" '' - # Ensure config directory exists - mkdir -p "$HOME/.git-ai" - - # Create config.json if it doesn't exist - if [ ! -f "$HOME/.git-ai/config.json" ]; then - # Find the system git (not our wrapper) - GIT_PATH="${git}/bin/git" - cat > "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < "$HOME/.git-ai/config.json" < Date: Thu, 14 May 2026 10:40:43 -0400 Subject: [PATCH 12/12] Fix some AI review bugs - Fix `override` function being called from git-ai attrset instead of its enclosing scope - Fix `wrapped` package being no longer exposed under `packages` - Fix `callPackage` on default.nix shadowing `makeScope`s `override` function --- default.nix | 4 ++-- flake.nix | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/default.nix b/default.nix index 443df10792..263321cfe4 100644 --- a/default.nix +++ b/default.nix @@ -153,7 +153,7 @@ let }; }; in - {pkgs, lib }: + {pkgs, lib, ... }: lib.makeScope pkgs.newScope (self: { git-ai = @@ -163,12 +163,12 @@ in rustPlatform = self.callPackage rustPlatform' { }; git-og = self.callPackage git-og' { }; wrapper = self.callPackage wrapper' { }; - wrapped = self.callPackage wrapped' { }; }; packages = { git-ai = self.callPackage git-ai-package' { }; default = self.callPackage git-ai-package' { }; minimal = self.callPackage git-ai-minimal' { }; + wrapped = self.callPackage wrapped' { }; unwrapped = self.callPackage git-ai-unwrapped' { }; }; } diff --git a/flake.nix b/flake.nix index af3e80a517..ef6748f127 100644 --- a/flake.nix +++ b/flake.nix @@ -13,9 +13,8 @@ outputs = { self, nixpkgs, rust-overlay, flake-utils }: let default = import ./default.nix; - defaultBasedOn = pkgs: - (pkgs.extend (rust-overlay.overlays.default)).callPackage default { } - ; + defaultApply = pkgs: default { inherit pkgs; lib = pkgs.lib; }; + defaultApplyExt = pkgs: defaultApply (pkgs.extend rust-overlay.overlays.default); in flake-utils.lib.eachDefaultSystem (system: let @@ -24,7 +23,7 @@ overlays = [ rust-overlay.overlays.default ]; }; - default' = pkgs.callPackage default { }; + default' = defaultApply pkgs; rustToolchain = default'.git-ai.utils.rustToolchain; rustPlatform = default'.git-ai.utils.rustPlatform; in @@ -208,7 +207,7 @@ # Overlay for importing into other flakes overlays.default = final: prev: let - default' = defaultBasedOn final; + default' = defaultApplyExt final; in { git-ai = default'.git-ai.packages.git-ai; @@ -223,7 +222,7 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; - default' = defaultBasedOn pkgs; + default' = defaultApplyExt pkgs; # Build the config object, filtering out null values configFile = filterAttrs (n: v: v != null) { @@ -268,7 +267,7 @@ default = if cfg.gitBasePackage == null then default'.git-ai.packages.git-ai - else (default'.git-ai.override { git = cfg.gitBasePackage; }).packages.git-ai + else (default'.overrideScope (final: prev: { git = cfg.gitBasePackage; })).git-ai.packages.git-ai ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use."; @@ -521,7 +520,7 @@ cfg = config.programs.git-ai; jsonFormat = pkgs.formats.json { }; - default' = defaultBasedOn pkgs; + default' = defaultApplyExt pkgs; # Build the config object, filtering out null values # We use explicit null checks since Nix 'or' only works for attribute access @@ -564,7 +563,7 @@ default = if cfg.gitBasePackage == null then default'.git-ai.packages.git-ai - else (default'.git-ai.override { git = cfg.gitBasePackage; }).packages.git-ai + else (default'.overrideScope (final: prev: { git = cfg.gitBasePackage; })).git-ai.packages.git-ai ; defaultText = literalExpression "inputs.git-ai.packages.\${pkgs.system}.default"; description = "The git-ai package to use.";