Radish alpha
r
rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt
Git libraries for Radicle
Radicle
Git
radicle-git flake.nix
{
  description = "Improved types and functionality for using Git in Radicle";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/release-25.11";

    crane = {
      url = "github:ipetkov/crane";
    };

    git-hooks = {
      url = "github:cachix/git-hooks.nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };

    rust-overlay = {
      url = "github:oxalica/rust-overlay";
      inputs = {
        nixpkgs.follows = "nixpkgs";
      };
    };

    flake-utils.url = "github:numtide/flake-utils";

    advisory-db = {
      url = "github:rustsec/advisory-db";
      flake = false;
    };
  };

  nixConfig = {
    keepOutputs = true;
  };

  outputs = {
    self,
    nixpkgs,
    crane,
    fenix,
    flake-utils,
    advisory-db,
    rust-overlay,
    ...
  } @ inputs:
    flake-utils.lib.eachDefaultSystem (system: let
      pname = "radicle-git";
      pkgs = import nixpkgs {
        inherit system;
        overlays = [(import rust-overlay)];
      };

      inherit (pkgs) lib;

      msrv = let
        msrv = (builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.rust-version;
      in rec {
        toolchain = pkgs.rust-bin.stable.${msrv}.default;
        craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
        commonArgs = mkCommonArgs craneLib;
      };

      rustup = rec {
        toolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
        craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
        commonArgs = mkCommonArgs craneLib;
      };

      srcFilters = path: type:
      # Allow data/git-platinum.tgz
        (lib.hasSuffix "\.tgz" path)
        ||
        # Default filter from crane (allow .rs files)
        (rustup.craneLib.filterCargoSources path type);

      src = lib.cleanSourceWith {
        src = ./.;
        filter = srcFilters;
      };

      basicArgs = {
        inherit src;
        pname = "radicle-git";
        version = "0.1.0";
        strictDeps = true;
      };

      # Common arguments can be set here to avoid repeating them later
      mkCommonArgs = craneLib:
        basicArgs
        // {
          # Build *just* the cargo dependencies, so we can reuse
          # all of that work (e.g. via cachix) when running in CI
          cargoArtifacts = craneLib.buildDepsOnly basicArgs;

          nativeBuildInputs = with pkgs; [
            git
          ];
          buildInputs = lib.optionals pkgs.stdenv.buildPlatform.isDarwin (with pkgs; [
            darwin.apple_sdk.frameworks.Security
            libiconv
          ]);
        };

      buildCrate = rust: name: let
        # Test crates live under `{crate}/t` but are called `{crate}-t`.
        # Check if it is a test crate, and safely remove the suffix and build the Cargo.toml path.
        isTest = lib.hasSuffix "-test" name;
        pname = lib.removeSuffix "-test" name;
        cargoToml = src + "/${pname}${lib.optionalString isTest "/t"}/Cargo.toml";
      in
        rust.craneLib.buildPackage (rust.commonArgs
          // {
            inherit (rust.craneLib.crateNameFromCargoToml {inherit cargoToml;}) pname version;
            cargoExtraArgs = "-p ${pname}";
            doCheck = false;
          });
      buildCrates = {
        rust ? rustup,
        prefix ? "",
      }:
        builtins.listToAttrs (map
          (name: lib.nameValuePair (prefix + name) (buildCrate rust name))
          [
            "radicle-std-ext"
            "radicle-git-ext"
            "radicle-git-ext-test"
            "radicle-surf"
            "radicle-surf-test"
          ]);
    in {
      # Formatter
      formatter = pkgs.alejandra;

      checks =
        (buildCrates {
          rust = msrv;
          prefix = "msrv-";
        })
        // {
          pre-commit-check = inputs.git-hooks.lib.${system}.run {
            src = ./.;
            settings.rust.check.cargoDeps = pkgs.rustPlatform.importCargoLock {lockFile = ./Cargo.lock;};
            default_stages = [
              "pre-commit"
              "pre-push"
            ];
            hooks = {
              alejandra.enable = true;
              codespell = {
                enable = true;
                entry = "${lib.getExe pkgs.codespell} -w";
                types = ["text"];
              };
              rustfmt = {
                enable = true;
                fail_fast = true;
                packageOverrides.rustfmt = rustup.toolchain;
              };
              cargo-check = {
                enable = true;
                name = "cargo check";
                after = ["rustfmt"];
                fail_fast = true;
              };
              cargo-doc = let
                # We wrap `cargo` in order to set an environment variable that
                # gives us a non-zero exit on warning.
                command =
                  pkgs.writeShellScript
                  "cargo"
                  "RUSTDOCFLAGS='--deny warnings' ${lib.getExe' rustup.toolchain "cargo"} $@";
              in {
                enable = true;
                name = "cargo doc";
                after = ["rustfmt"];
                fail_fast = true;
                entry = "${command} doc --workspace --all-features --no-deps";
                files = "\\.rs$";
                pass_filenames = false;
              };
              clippy = {
                enable = true;
                name = "cargo clippy";
                stages = ["pre-push"]; # Only pre-push, because it takes a while.
                settings = {
                  allFeatures = true;
                  denyWarnings = true;
                };
                packageOverrides = {
                  cargo = rustup.toolchain;
                  clippy = rustup.toolchain;
                };
              };
              shellcheck.enable = true;
            };
          };

          # Run clippy (and deny all warnings) on the crate source,
          # again, reusing the dependency artifacts from above.
          #
          # Note that this is done as a separate derivation so that
          # we can block the CI if there are issues here, but not
          # prevent downstream consumers from building our crate by itself.
          doc = rustup.craneLib.cargoDoc rustup.commonArgs;
          deny = rustup.craneLib.cargoDeny rustup.commonArgs;
          fmt = rustup.craneLib.cargoFmt basicArgs;

          audit = rustup.craneLib.cargoAudit {
            inherit src advisory-db;
          };

          # Run tests with cargo-nextest
          nextest = rustup.craneLib.cargoNextest (rustup.commonArgs
            // {
              partitions = 1;
              partitionType = "count";
              nativeBuildInputs = [
                # git is required so the sandbox can access it.
                pkgs.git
              ];
              # Ensure dev is used since we rely on env variables being
              # set in tests.
              buildPhase = ''
                export CARGO_PROFILE=dev;
              '';
            });
        };

      packages = buildCrates {};

      devShells.default = rustup.craneLib.devShell {
        # Extra inputs can be added here; cargo and rustc are provided by default.
        packages = [
          pkgs.cargo-deny
          pkgs.cargo-msrv
          pkgs.cargo-nextest
          pkgs.cargo-semver-checks
          pkgs.cargo-watch
          pkgs.ripgrep
          pkgs.rust-analyzer
        ];
      };
    });
}