diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ed004c5c..5004f8eb 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,7 +22,8 @@ jobs: name: pre-commit-hooks signingKey: '${{ secrets.CACHIX_SIGNING_KEY }}' - run: rm -rf /opt& - - run: nix-build --keep-going + - name: Evaluate flake-compat default.nix + run: nix eval --show-trace --file ./default.nix run tests-flakes: strategy: @@ -39,7 +40,8 @@ jobs: - run: rm -rf /opt& - name: Check nixpkgs-unstable - run: nix flake check -L --show-trace + working-directory: ./dev + run: nix flake check -L --show-trace --no-write-lock-file - run: nix eval .#lib.x86_64-linux.run --show-trace @@ -58,4 +60,5 @@ jobs: - run: rm -rf /opt& - name: Check nixpkgs-stable + working-directory: ./dev run: nix flake check -L --show-trace --override-input nixpkgs github:NixOS/nixpkgs/nixos-25.05 diff --git a/default.nix b/default.nix index 262a5c80..2f52c8d6 100644 --- a/default.nix +++ b/default.nix @@ -1,13 +1,15 @@ let - flake = (import - ( - let lock = builtins.fromJSON (builtins.readFile ./flake.lock); in - fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; - sha256 = lock.nodes.flake-compat.locked.narHash; - } - ) - { src = ./.; } - ).defaultNix; + flake = + (import + ( + let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + in + fetchTarball { + url = "https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz"; + sha256 = lock.nodes.flake-compat.locked.narHash; + } + ) + { src = ./.; }).defaultNix; in -flake.lib.${builtins.currentSystem} // flake.packages.${builtins.currentSystem} +flake.lib.${builtins.currentSystem} // flake.legacyPackages.${builtins.currentSystem} diff --git a/dev/flake.nix b/dev/flake.nix new file mode 100644 index 00000000..b6e029cb --- /dev/null +++ b/dev/flake.nix @@ -0,0 +1,15 @@ +{ + description = "An internal test flake for git-hooks.nix"; + + inputs = { + git-hooks.url = "path:.."; + }; + + outputs = + { git-hooks + , ... + }: + { + inherit (git-hooks) legacyPackages checks; + }; +} diff --git a/flake.lock b/flake.lock index 5cfe0856..c6c5cae8 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1747046372, - "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "lastModified": 1761588595, + "narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=", "owner": "edolstra", "repo": "flake-compat", - "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index aa29f322..a75d4574 100644 --- a/flake.nix +++ b/flake.nix @@ -40,6 +40,16 @@ ''; }; + # The set of tools exposed by git-hooks. + # We use legacyPackages because not all tools are derivations that evaluate. + legacyPackages = forAllSystems ({ pkgs, exposed, ... }: exposed.tools // { + pre-commit = pkgs.pre-commit; + }); + + # WARN: use `legacyPackages` instead to get error messages for deprecated packages + # + # Each entry is guaranteed to be a derivation that evaluates. + # TODO: this should be deprecated as it exposes a subset of nixpkgs, which is incompatbile with the packages output. packages = forAllSystems ({ exposed, ... }: exposed.packages // { default = exposed.packages.pre-commit; }); @@ -50,10 +60,11 @@ }; }); - checks = forAllSystems ({ exposed, ... }: lib.filterAttrs (k: v: v != null) exposed.checks); + checks = forAllSystems ({ exposed, ... }: exposed.checks); lib = forAllSystems ({ exposed, ... }: { inherit (exposed) run; }); + # TODO: remove and expose a `lib` function is needed exposed = forAllSystems ({ exposed, ... }: exposed); }; } diff --git a/modules/hooks.nix b/modules/hooks.nix index 55d6f526..05f78c8d 100644 --- a/modules/hooks.nix +++ b/modules/hooks.nix @@ -2618,7 +2618,7 @@ in # need version >= 0.4.0 for the --from-stdin flag toolVersionCheck = lib.versionAtLeast convco.version "0.4.0"; in - lib.throwIf (convco == null || !toolVersionCheck) "The version of Nixpkgs used by git-hooks.nix does not have the `convco` package (>=0.4.0). Please use a more recent version of Nixpkgs." + lib.throwIfNot toolVersionCheck "The version of Nixpkgs used by git-hooks.nix does not have the `convco` package (>=0.4.0). Please use a more recent version of Nixpkgs." builtins.toString script; stages = [ "commit-msg" ]; @@ -3089,9 +3089,7 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm "$PRE_COMMIT_COMMIT_MSG_SOURCE" --commit-msg-file "$1" ''; in - lib.throwIf (hooks.gptcommit.package == null) "The version of Nixpkgs used by git-hooks.nix does not have the `gptcommit` package. Please use a more recent version of Nixpkgs." - toString - script; + toString script; stages = [ "prepare-commit-msg" ]; }; hadolint = @@ -3110,13 +3108,7 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm ## https://github.com/Frama-C/headache/blob/master/config_builtin.txt files = "(\\.ml[ily]?$)|(\\.fmli?$)|(\\.[chy]$)|(\\.tex$)|(Makefile)|(README)|(LICENSE)"; package = tools.headache; - entry = - ## NOTE: `headache` made into in nixpkgs on 12 April 2023. At the - ## next NixOS release, the following code will become irrelevant. - lib.throwIf - (hooks.headache.package == null) - "The version of nixpkgs used by git-hooks.nix does not have `ocamlPackages.headache`. Please use a more recent version of nixpkgs." - "${hooks.headache.package}/bin/headache -h ${hooks.headache.settings.header-file}"; + entry = "${hooks.headache.package}/bin/headache -h ${hooks.headache.settings.header-file}"; }; hindent = { @@ -3646,16 +3638,7 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm pre-commit-hook-ensure-sops = { name = "pre-commit-hook-ensure-sops"; package = tools.pre-commit-hook-ensure-sops; - entry = - ## NOTE: pre-commit-hook-ensure-sops landed in nixpkgs on 8 July 2022. Once it reaches a - ## release of NixOS, the `throwIf` piece of code below will become - ## useless. - lib.throwIf - (hooks.pre-commit-hook-ensure-sops.package == null) - "The version of nixpkgs used by git-hooks.nix does not have the `pre-commit-hook-ensure-sops` package. Please use a more recent version of nixpkgs." - '' - ${hooks.pre-commit-hook-ensure-sops.package}/bin/pre-commit-hook-ensure-sops - ''; + entry = "${hooks.pre-commit-hook-ensure-sops.package}/bin/pre-commit-hook-ensure-sops"; files = "^secrets"; }; # See all CLI flags for prettier [here](https://prettier.io/docs/en/cli.html). @@ -4134,25 +4117,17 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm description = "A universal formatter engine within the Tree-sitter ecosystem, with support for many languages."; package = tools.topiary; entry = - ## NOTE: Topiary landed in nixpkgs on 2 Dec 2022. Once it reaches a - ## release of NixOS, the `throwIf` piece of code below will become - ## useless. - lib.throwIf - (hooks.topiary.package == null) - "The version of nixpkgs used by git-hooks.nix does not have the `topiary` package. Please use a more recent version of nixpkgs." - ( - let - topiary-inplace = pkgs.writeShellApplication { - name = "topiary-inplace"; - text = '' - for file; do - ${hooks.topiary.package}/bin/topiary --in-place --input-file "$file" - done - ''; - }; - in - "${topiary-inplace}/bin/topiary-inplace" - ); + let + topiary-inplace = pkgs.writeShellApplication { + name = "topiary-inplace"; + text = '' + for file; do + ${hooks.topiary.package}/bin/topiary --in-place --input-file "$file" + done + ''; + }; + in + "${topiary-inplace}/bin/topiary-inplace"; files = "(\\.json$)|(\\.toml$)|(\\.mli?$)"; }; treefmt = @@ -4257,11 +4232,7 @@ lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourm name = "typstyle"; description = "Beautiful and reliable typst code formatter"; package = tools.typstyle; - entry = - lib.throwIf - (hooks.typstyle.package == null) - "The version of nixpkgs used by git-hooks.nix must contain typstyle" - "${hooks.typstyle.package}/bin/typstyle -i"; + entry = "${hooks.typstyle.package}/bin/typstyle -i"; files = "\\.typ$"; }; uv-check = { diff --git a/nix/call-tools.nix b/nix/call-tools.nix index d9db09e6..7951b362 100644 --- a/nix/call-tools.nix +++ b/nix/call-tools.nix @@ -1,7 +1,21 @@ pkgs: -pkgs.lib.flip builtins.removeAttrs [ "override" "overrideDerivation" ] - (pkgs.callPackage ./tools.nix { - cabal-fmt = (pkgs.haskell.lib.enableSeparateBinOutput pkgs.haskellPackages.cabal-fmt).bin; - cabal-gild = (pkgs.haskell.lib.enableSeparateBinOutput pkgs.haskellPackages.cabal-gild).bin; - hindent = pkgs.haskell.lib.enableSeparateBinOutput pkgs.haskellPackages.hindent; - }) +pkgs.lib.flip builtins.removeAttrs [ "override" "overrideDerivation" ] ( + pkgs.callPackage ./tools.nix { + placeholder = + name: + let + errorMsg = '' + git-hooks: the package `${name}` is not available in your nixpkgs revision. + ''; + in + { + # Allows checking without forcing evaluation + meta.isPlaceholder = true; + + type = "derivation"; + name = name + "-placeholder"; + outPath = throw errorMsg; + drvPath = throw errorMsg; + }; + } +) diff --git a/nix/default.nix b/nix/default.nix index 55ac26ea..41d49e57 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -19,16 +19,51 @@ let ; }; - # Filter out any broken or missing packages from our tests. - filterBrokenPackages = n: package: package != null && !(package.meta.broken or false); + removeInvalidPackage = removeInvalidPackageWith { }; + removeInvalidPackageQuiet = removeInvalidPackageWith { warn = false; }; + + # Filter out broken and placeholder packages. + removeInvalidPackageWith = + { warn ? true + , + }: + name: package: + let + isPlaceholder = package.meta.isPlaceholder or false; + isBroken = package.meta.broken or false; + + check = builtins.tryEval (!(isPlaceholder || isBroken)); + result = check.success && check.value; + + message = + if !check.success then + '' + Skipping ${name} because it failed to evaluate. + '' + else if !check.value && isPlaceholder then + '' + Skipping ${name} because it is missing from this nixpkgs revision. + '' + else if !check.value && isBroken then + '' + Skipping ${name} because it is marked as broken. + '' + else + ""; # Not used + + in + if warn then lib.warnIfNot result message result else result; in { inherit tools run; - # Flake style attributes - packages = (lib.filterAttrs filterBrokenPackages tools) // { + + # Flake-style attributes + # Each should strictly be a valid derivation that evaluates. + packages = (lib.filterAttrs removeInvalidPackageQuiet tools) // { inherit (pkgs) pre-commit; }; - checks = self.packages // { + + checks = (lib.filterAttrs removeInvalidPackage tools) // { # A pre-commit-check for nix-pre-commit itself pre-commit-check = run { src = ../.; @@ -57,7 +92,7 @@ let f n h.package; allEntryPoints = lib.pipe allHooks [ - (lib.filterAttrs (getPackage filterBrokenPackages)) + (lib.filterAttrs (getPackage (removeInvalidPackageQuiet))) (lib.mapAttrsToList getEntry) ]; in diff --git a/nix/tools.nix b/nix/tools.nix index 9e500595..e64992ea 100644 --- a/nix/tools.nix +++ b/nix/tools.nix @@ -1,13 +1,11 @@ { stdenv , lib - +, placeholder , actionlint , action-validator , alejandra , ansible-lint , biome -, cabal-fmt -, cabal-gild , cabal2nix , callPackage , cargo @@ -32,15 +30,15 @@ , editorconfig-checker , elixir , elmPackages -, flake-checker ? null +, flake-checker ? placeholder "flake-checker" , fprettify , git-annex , gitlint -, gptcommit ? null +, gptcommit ? placeholder "gptcommit" , hadolint +, haskell , haskellPackages -, hindent -, hledger-fmt ? null +, hledger-fmt ? placeholder "hledger-fmt" , hlint , hpack , html-tidy @@ -55,8 +53,8 @@ , nbstripout , nil , nixfmt -, nixfmt-classic ? null -, nixfmt-rfc-style ? null +, nixfmt-classic ? placeholder "nixfmt-classic" +, nixfmt-rfc-style ? placeholder "nixfmt-rfc-style" , nixpkgs-fmt , nodePackages , ocamlPackages @@ -65,14 +63,14 @@ , ormolu , pkgsBuildBuild , poetry -, pre-commit-hook-ensure-sops ? null +, pre-commit-hook-ensure-sops ? placeholder "pre-commit-hook-ensure-sops" , proselint , python3Packages , pyright ? nodePackages.pyright , phpPackages -, ripsecrets ? null +, ripsecrets ? placeholder "ripsecrets" , reuse -, ruff ? null +, ruff ? placeholder "ruff" , rustfmt , selene , shellcheck @@ -85,11 +83,13 @@ , tagref , taplo , texlive -, topiary ? null ## Added in nixpkgs on Dec 2, 2022 +, # Added in nixpkgs on Dec 2, 2022 + topiary ? placeholder "topiary" , treefmt , trufflehog , typos -, typstyle ? null ## Add in nixpkgs added on commit 800ca60 +, # Added in nixpkgs in commit 800ca60 + typstyle ? placeholder "typstyle" , woodpecker-cli , zprint , yamlfmt @@ -98,13 +98,13 @@ , go-tools , golangci-lint , golines -, revive ? null +, revive ? placeholder "revive" , uv , vale , zizmor +, }: - let tex = texlive.combine { inherit (texlive) latexindent chktex scheme-basic; @@ -120,8 +120,6 @@ in beautysh biome cabal2nix - cabal-fmt - cabal-gild cargo chart-testing checkmake @@ -142,6 +140,7 @@ in elixir flake-checker fprettify + git-annex gitlint go go-tools @@ -149,7 +148,6 @@ in golines gptcommit hadolint - hindent hledger-fmt hlint hpack @@ -162,6 +160,7 @@ in nbstripout nil nixpkgs-fmt + opam opentofu ormolu pre-commit-hook-ensure-sops @@ -198,9 +197,26 @@ in # TODO: these two should be statically compiled inherit (haskellPackages) fourmolu; inherit (luaPackages) luacheck; - inherit (nodePackages) eslint markdownlint-cli prettier cspell; + inherit (nodePackages) + eslint + markdownlint-cli + prettier + cspell + ; inherit (ocamlPackages) ocp-indent; - inherit (python3Packages) autoflake black flake8 flynt isort mkdocs-linkcheck mypy openapi-spec-validator pre-commit-hooks pylint pyupgrade; + inherit (python3Packages) + autoflake + black + flake8 + flynt + isort + mkdocs-linkcheck + mypy + openapi-spec-validator + pre-commit-hooks + pylint + pyupgrade + ; inherit (phpPackages) php-cs-fixer psalm; # FIXME: workaround build failure phpstan = phpPackages.phpstan.overrideAttrs (old: { @@ -210,31 +226,46 @@ in phpcbf = phpPackages.php-codesniffer or phpPackages.phpcbf; phpcs = phpPackages.php-codesniffer or phpPackages.phpcs; lua-language-server = lua-language-server; - purs-tidy = nodePackages.purs-tidy or null; + purs-tidy = nodePackages.purs-tidy or (placeholder "purs-tidy"); cabal2nix-dir = callPackage ./cabal2nix-dir { }; hpack-dir = callPackage ./hpack-dir { }; hunspell = callPackage ./hunspell { }; purty = callPackage ./purty { purty = nodePackages.purty; }; terraform-validate = callPackage ./terraform-validate { }; tflint = callPackage ./tflint { }; - dune-build-opam-files = callPackage ./dune-build-opam-files { dune = dune_3; inherit (pkgsBuildBuild) ocaml; }; - dune-fmt = callPackage ./dune-fmt { dune = dune_3; inherit (pkgsBuildBuild) ocaml; }; + dune-build-opam-files = callPackage ./dune-build-opam-files { + dune = dune_3; + inherit (pkgsBuildBuild) ocaml; + }; + dune-fmt = callPackage ./dune-fmt { + dune = dune_3; + inherit (pkgsBuildBuild) ocaml; + }; latexindent = tex; lacheck = texlive.combine { inherit (texlive) lacheck scheme-basic; }; chktex = tex; commitizen = commitizen.overrideAttrs (_: _: { doCheck = false; }); - bats = if bats ? withLibraries then (bats.withLibraries (p: [ p.bats-support p.bats-assert p.bats-file ])) else bats; - git-annex = if stdenv.isDarwin then null else git-annex; - # Note: Only broken in stable nixpkgs, works fine on latest master. - opam = if stdenv.isDarwin then null else opam; + bats = + if bats ? withLibraries then + (bats.withLibraries (p: [ + p.bats-support + p.bats-assert + p.bats-file + ])) + else + bats; headache = callPackage ./headache { }; # Disable tests as these take way to long on our infra. julia-bin = julia-bin.overrideAttrs (_: _: { doInstallCheck = false; }); + cabal-fmt = (haskell.lib.enableSeparateBinOutput haskellPackages.cabal-fmt).bin; + cabal-gild = (haskell.lib.enableSeparateBinOutput haskellPackages.cabal-gild).bin; + hindent = haskell.lib.enableSeparateBinOutput haskellPackages.hindent; + # nixfmt 1.0 is now the official Nix formatter as of 25.11. # # In 24.05, the `nixfmt` package was deprecated and replaced with two separate packages: @@ -243,8 +274,9 @@ in # # Remove this block in 26.05 nixfmt = - if lib.versionOlder nixfmt.version "1.0" && nixfmt-classic != null - then nixfmt-classic - else nixfmt; + if lib.versionOlder nixfmt.version "1.0" && (nixfmt-classic.meta.isPlaceholder or false) then + nixfmt-classic + else + nixfmt; inherit nixfmt-classic nixfmt-rfc-style; }