From a7a58724dc736676f1488d9cc82e91e5aca6ed96 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 10:41:40 +0100 Subject: [PATCH 01/23] chore: run `treefmt` --- nix/internal.nix | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index 80a17fc..ec01c1d 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -42,7 +42,9 @@ in rec { x86_64-darwin = cardano-node-flake.packages.x86_64-darwin; aarch64-darwin = cardano-node-flake.packages.aarch64-darwin; } - .${targetSystem}; + .${ + targetSystem + }; inherit (cardano-node-packages) cardano-node cardano-cli; @@ -101,7 +103,9 @@ in rec { aarch64-darwin = patched-flake.packages.aarch64-darwin.testgen-hs; x86_64-windows = patched-flake.legacyPackages.x86_64-linux.hydraJobs.windows.testgen-hs; } - .${targetSystem}; + .${ + targetSystem + }; nix-bundle-exe = import inputs.nix-bundle-exe {inherit pkgs;}; @@ -174,5 +178,7 @@ in rec { aarch64-linux = linuxLike {}; x86_64-windows = linuxLike {useZip = true;}; } - .${targetSystem}; + .${ + targetSystem + }; } From 8c5ecdab67f5951cdeaf5ca3c9faf4f45b9fd75e Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 10:41:53 +0100 Subject: [PATCH 02/23] chore: add a Haskell section to `.gitignore` --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 5e8862c..f853376 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ # Nix result + +# Haskell / Cabal +dist-newstyle/ +/cabal.project +/cabal.project.local +.ghc.environment.* From 0628b3d7ef7fbcc88d3e3f35c1c0c71c287e4fae Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 10:48:29 +0100 Subject: [PATCH 03/23] chore: use a patch for exposing `cardano-submit-api` --- nix/cardano-node--export-cardano-submit-api.diff | 13 +++++++++++++ nix/internal.nix | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 nix/cardano-node--export-cardano-submit-api.diff diff --git a/nix/cardano-node--export-cardano-submit-api.diff b/nix/cardano-node--export-cardano-submit-api.diff new file mode 100644 index 0000000..a2aa7d7 --- /dev/null +++ b/nix/cardano-node--export-cardano-submit-api.diff @@ -0,0 +1,13 @@ +diff --git a/cardano-submit-api/cardano-submit-api.cabal b/cardano-submit-api/cardano-submit-api.cabal +index b37fee9469..d9871d6c8f 100644 +--- a/cardano-submit-api/cardano-submit-api.cabal ++++ b/cardano-submit-api/cardano-submit-api.cabal +@@ -64,7 +64,7 @@ library + + exposed-modules: Cardano.TxSubmit + +- other-modules: Cardano.TxSubmit.CLI.Parsers ++ , Cardano.TxSubmit.CLI.Parsers + , Cardano.TxSubmit.CLI.Types + , Cardano.TxSubmit.Config + , Cardano.TxSubmit.Metrics diff --git a/nix/internal.nix b/nix/internal.nix index ec01c1d..fda4f49 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -76,7 +76,6 @@ in rec { ''} cp -r ${../testgen-hs} ./testgen-hs sed -r '/^packages:/ a\ testgen-hs' -i cabal.project - sed -r 's/other-modules:\s*/ , /g' -i cardano-submit-api/cardano-submit-api.cabal patch -p1 -i ${./cardano-node--apply-patches.diff} cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff @@ -90,6 +89,8 @@ in rec { patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix + + patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} ''); inherit (unpatched) rev shortRev lastModified lastModifiedDate; }; From b03a11c2b21758ae7ecd63bf2c5daf58bd0be746 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 12:25:02 +0100 Subject: [PATCH 04/23] chore: apply Nix linters --- flake.nix | 19 ++++++++----------- nix/internal.nix | 6 ++---- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/flake.nix b/flake.nix index 60d2d4d..39a66f1 100644 --- a/flake.nix +++ b/flake.nix @@ -29,12 +29,7 @@ ); systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; - perSystem = { - config, - system, - pkgs, - ... - }: { + perSystem = {system, ...}: { packages = let internal = inputs.self.internal.${system}; in @@ -47,11 +42,13 @@ treefmt = {pkgs, ...}: { projectRootFile = "flake.nix"; - programs.alejandra.enable = true; # Nix - programs.ormolu.enable = true; # Haskell - programs.cabal-fmt.enable = true; - programs.shfmt.enable = true; - programs.yamlfmt.enable = pkgs.system != "x86_64-darwin"; # a treefmt-nix+yamlfmt bug on Intel Macs + programs = { + alejandra.enable = true; # Nix + ormolu.enable = true; # Haskell + cabal-fmt.enable = true; + shfmt.enable = true; + yamlfmt.enable = pkgs.system != "x86_64-darwin"; # a treefmt-nix+yamlfmt bug on Intel Macs + }; }; }; diff --git a/nix/internal.nix b/nix/internal.nix index fda4f49..2cba17b 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -3,7 +3,7 @@ targetSystem, }: # For now, let's keep all UNIX definitions together, until they diverge more in the future. -assert __elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-windows"]; let +assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" "x86_64-windows"]; let buildSystem = if targetSystem != "x86_64-windows" then targetSystem @@ -38,9 +38,7 @@ in rec { cardano-node-packages = { x86_64-linux = cardano-node-flake.hydraJobs.x86_64-linux.musl; - aarch64-linux = cardano-node-flake.packages.aarch64-linux; - x86_64-darwin = cardano-node-flake.packages.x86_64-darwin; - aarch64-darwin = cardano-node-flake.packages.aarch64-darwin; + inherit (cardano-node-flake.packages) aarch64-linux x86_64-darwin aarch64-darwin; } .${ targetSystem From ac5e88937eb7d06095cfe390c6e1c7f9e7212875 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 12:34:51 +0100 Subject: [PATCH 05/23] feat: add a draft of a devshell for running incremental `cabal build testgen-hs` --- flake.nix | 14 ++-- nix/internal.nix | 210 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 153 insertions(+), 71 deletions(-) diff --git a/flake.nix b/flake.nix index 39a66f1..78ec55e 100644 --- a/flake.nix +++ b/flake.nix @@ -29,10 +29,10 @@ ); systems = ["x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin"]; - perSystem = {system, ...}: { - packages = let - internal = inputs.self.internal.${system}; - in + perSystem = {system, ...}: let + internal = inputs.self.internal.${system}; + in { + packages = { default = internal.defaultPackage; } @@ -40,6 +40,10 @@ default-x86_64-windows = inputs.self.internal.x86_64-windows.defaultPackage; }); + devShells = { + default = internal.devShell; + }; + treefmt = {pkgs, ...}: { projectRootFile = "flake.nix"; programs = { @@ -57,7 +61,7 @@ testgen-hs = lib.genAttrs (config.systems ++ ["x86_64-windows"]) ( targetSystem: inputs.self.internal.${targetSystem}.hydraPackage ); - inherit (inputs.self) checks; + inherit (inputs.self) checks devShells; }; in allJobs diff --git a/nix/internal.nix b/nix/internal.nix index 2cba17b..8c0dc62 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -10,30 +10,109 @@ assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwi else "x86_64-linux"; pkgs = inputs.nixpkgs.legacyPackages.${buildSystem}; inherit (pkgs) lib; -in rec { - defaultPackage = testgen-hs; - - cardano-node-flake = let + cardano-node-src = let unpatched = inputs.cardano-node; in + if targetSystem != "aarch64-linux" + then unpatched + else + pkgs.runCommand "source" {} '' + cp -r ${unpatched} $out + chmod -R +w $out + cd $out + ${lib.optionalString (targetSystem == "aarch64-linux") '' + echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix + sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix + ''} + ''; + cardano-node-src-for-flake = + if targetSystem != "aarch64-linux" + then cardano-node-src + else { + outPath = toString cardano-node-src; + inherit (inputs.cardano-node) rev shortRev lastModified lastModifiedDate; + }; + cardano-node-flake' = (import inputs.flake-compat {src = cardano-node-src-for-flake;}).defaultNix; + cardano-ledger-src = let + dep-id = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-ledger-core.identifier; + dep-tag = "${dep-id.name}-${dep-id.version}"; + in + pkgs.fetchFromGitHub { + name = "cardano-ledger--${dep-tag}"; + owner = "IntersectMBO"; + repo = "cardano-ledger"; + #rev = "a9e78ae63cf8870f0ce6ce76bd7029b82ddb47e1"; # the one for cardano-node 10.4.1, tag: cardano-ledger-core-1.17.0.0 + rev = dep-tag; # the one for cardano-node 10.4.1 + hash = "sha256-pD22f9VzNApynPhVYv0T7fsOZdbvYr1vlOxhKRhMSYk="; + }; + patched-cardano-ledger-src = pkgs.runCommandNoCC "cardano-ledger-src-patched" {} '' + cp -r ${cardano-ledger-src} $out + chmod -R +w $out + patch -p1 -d $out/libs/cardano-ledger-core -i ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} + patch -p1 -d $out/libs/cardano-ledger-test -i ${./cardano-ledger-test--expose-helpers.diff} + ${lib.optionalString (targetSystem == "x86_64-windows") '' + patch -p1 -d $out/libs/cardano-ledger-test -i ${./cardano-ledger-test--windows-fix.diff} + ''} + ''; + cardano-api-src = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-api.src; + patched-cardano-api-src = pkgs.applyPatches { + name = "cardano-api-src-patched"; + src = cardano-api-src; + patches = [./cardano-api--expose-internal.diff]; + }; + patched-cardano-node-src = pkgs.runCommandNoCC "cardano-node-src-patched" {} '' + cp -r ${cardano-node-src} $out + chmod -R +w $out + cd $out + ${lib.optionalString (targetSystem == "aarch64-linux") '' + echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix + sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix + ''} + cp -r ${../testgen-hs} ./testgen-hs + sed -r '/^packages:/ a\ testgen-hs' -i cabal.project + + patch -p1 -i ${./cardano-node--apply-patches.diff} + cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff + cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff + cp ${ + if targetSystem == "x86_64-windows" + then ./cardano-ledger-test--windows-fix.diff + else pkgs.emptyFile + } nix/cardano-ledger-test--windows-fix.diff + cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff + + patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} + sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix + + patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} + ''; + patched-cardano-node-flake' = (import inputs.flake-compat { - src = - if targetSystem != "aarch64-linux" - then unpatched - else { - outPath = toString (pkgs.runCommand "source" {} '' - cp -r ${unpatched} $out - chmod -R +w $out - cd $out - ${lib.optionalString (targetSystem == "aarch64-linux") '' - echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix - sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix - ''} - ''); - inherit (unpatched) rev shortRev lastModified lastModifiedDate; - }; + src = { + outPath = toString patched-cardano-node-src; + inherit (inputs.cardano-node) rev shortRev lastModified lastModifiedDate; + }; }) .defaultNix; + cardano-node-package-names = [ + "cardano-node" + "cardano-node-capi" + "cardano-node-chairman" + "cardano-submit-api" + "cardano-testnet" + "cardano-tracer" + "bench/cardano-profile" + "bench/cardano-topology" + "bench/locli" + "bench/plutus-scripts-bench" + "bench/tx-generator" + "trace-dispatcher" + "trace-resources" + "trace-forward" + ]; +in rec { + defaultPackage = testgen-hs; + cardano-node-flake = cardano-node-flake'; cardano-node-packages = { @@ -46,54 +125,53 @@ in rec { inherit (cardano-node-packages) cardano-node cardano-cli; - testgen-hs = let - patched-flake = let - unpatched = inputs.cardano-node; - cardano-ledger-src = let - dep-id = cardano-node-flake.project.${buildSystem}.hsPkgs.cardano-ledger-core.identifier; - dep-tag = "${dep-id.name}-${dep-id.version}"; - in - pkgs.fetchFromGitHub { - name = "cardano-ledger--${dep-tag}"; - owner = "IntersectMBO"; - repo = "cardano-ledger"; - #rev = "a9e78ae63cf8870f0ce6ce76bd7029b82ddb47e1"; # the one for cardano-node 10.4.1, tag: cardano-ledger-core-1.17.0.0 - rev = dep-tag; # the one for cardano-node 10.4.1 - hash = "sha256-pD22f9VzNApynPhVYv0T7fsOZdbvYr1vlOxhKRhMSYk="; - }; - in - (import inputs.flake-compat { - src = { - outPath = toString (pkgs.runCommandNoCC "source" {} '' - cp -r ${unpatched} $out - chmod -R +w $out - cd $out - ${lib.optionalString (targetSystem == "aarch64-linux") '' - echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix - sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix - ''} - cp -r ${../testgen-hs} ./testgen-hs - sed -r '/^packages:/ a\ testgen-hs' -i cabal.project - - patch -p1 -i ${./cardano-node--apply-patches.diff} - cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff - cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff - cp ${ - if targetSystem == "x86_64-windows" - then ./cardano-ledger-test--windows-fix.diff - else pkgs.emptyFile - } nix/cardano-ledger-test--windows-fix.diff - cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff - - patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} - sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix + devShell = let + cardano-node-devshell = cardano-node-flake.devShells.${buildSystem}.default; + cabal-project-packages-old = lib.concatStringsSep "\n" [ + "packages:" + " cardano-node" + " cardano-node-capi" + " cardano-node-chairman" + " cardano-submit-api" + " cardano-testnet" + " cardano-tracer" + " bench/cardano-profile" + " bench/cardano-topology" + " bench/locli" + " bench/plutus-scripts-bench" + " bench/tx-generator" + " trace-dispatcher" + " trace-resources" + " trace-forward" + "" + ]; + cabal-project-packages-new = lib.concatStringsSep "\n" ( + ["packages:"] + ++ map (package: " ${patched-cardano-node-src}/${package}") cardano-node-package-names + ++ [ + " ${patched-cardano-api-src}" + " ${patched-cardano-ledger-src}/libs/cardano-ledger-core" + " ${patched-cardano-ledger-src}/libs/cardano-ledger-test" + " ${patched-cardano-ledger-src}/libs/constrained-generators" + " @REPO_ROOT@/testgen-hs" + "" + ] + ); + cabal-project-base = cardano-node-flake.project.${buildSystem}.args.cabalProject; + cabal-project = + lib.replaceStrings [cabal-project-packages-old] [cabal-project-packages-new] cabal-project-base; + cabal-project-template = pkgs.writeText "cabal.project" cabal-project; + in + pkgs.mkShell { + inputsFrom = [cardano-node-devshell]; + shellHook = '' + repo_root=$(pwd) + sed "s|@REPO_ROOT@|$repo_root|g" ${cabal-project-template} > cabal.project + ''; + }; - patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} - ''); - inherit (unpatched) rev shortRev lastModified lastModifiedDate; - }; - }) - .defaultNix; + testgen-hs = let + patched-flake = patched-cardano-node-flake'; in { x86_64-linux = patched-flake.hydraJobs.x86_64-linux.musl.testgen-hs; From 551d238e6ad1288fd61b634b31a478b7e2e996bf Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 19:30:05 +0100 Subject: [PATCH 06/23] chore: use a Python script for editing `cabal.project`, it's less fragile --- .gitignore | 2 +- nix/internal.nix | 124 ++++++++++++++--------------------- nix/rewrite_cabal_project.py | 62 ++++++++++++++++++ 3 files changed, 112 insertions(+), 76 deletions(-) create mode 100644 nix/rewrite_cabal_project.py diff --git a/.gitignore b/.gitignore index f853376..e4925d1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,5 @@ result # Haskell / Cabal dist-newstyle/ /cabal.project -/cabal.project.local +/cabal.project.* .ghc.environment.* diff --git a/nix/internal.nix b/nix/internal.nix index 8c0dc62..caa40a3 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -60,56 +60,49 @@ assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwi src = cardano-api-src; patches = [./cardano-api--expose-internal.diff]; }; - patched-cardano-node-src = pkgs.runCommandNoCC "cardano-node-src-patched" {} '' - cp -r ${cardano-node-src} $out - chmod -R +w $out - cd $out - ${lib.optionalString (targetSystem == "aarch64-linux") '' - echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix - sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix - ''} - cp -r ${../testgen-hs} ./testgen-hs - sed -r '/^packages:/ a\ testgen-hs' -i cabal.project + patched-cardano-node-src = {withOurCode ? true}: + pkgs.runCommandNoCC "cardano-node-src-patched" {} '' + cp -r ${cardano-node-src} $out + chmod -R +w $out + cd $out + ${lib.optionalString (targetSystem == "aarch64-linux") '' + echo ${lib.escapeShellArg (builtins.toJSON [targetSystem])} >$out/nix/supported-systems.nix + sed -r 's/"-fexternal-interpreter"//g' -i $out/nix/haskell.nix + ''} + ${ + if withOurCode + then '' + cp -r ${../testgen-hs} ./testgen-hs + '' + else '' + mkdir -p ./testgen-hs + '' + } + sed -r '/^packages:/ a\ testgen-hs' -i cabal.project - patch -p1 -i ${./cardano-node--apply-patches.diff} - cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff - cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff - cp ${ - if targetSystem == "x86_64-windows" - then ./cardano-ledger-test--windows-fix.diff - else pkgs.emptyFile - } nix/cardano-ledger-test--windows-fix.diff - cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff + patch -p1 -i ${./cardano-node--apply-patches.diff} + cp ${./cardano-ledger-core--Arbitrary-PoolMetadata.diff} nix/cardano-ledger-core--Arbitrary-PoolMetadata.diff + cp ${./cardano-ledger-test--expose-helpers.diff} nix/cardano-ledger-test--expose-helpers.diff + cp ${ + if targetSystem == "x86_64-windows" + then ./cardano-ledger-test--windows-fix.diff + else pkgs.emptyFile + } nix/cardano-ledger-test--windows-fix.diff + cp ${./cardano-api--expose-internal.diff} nix/cardano-api--expose-internal.diff - patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} - sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix + patch -p1 -i ${./cardano-node--expose-cardano-ledger-test.diff} + sed -r 's,CARDANO_LEDGER_SOURCE,${cardano-ledger-src},g' -i nix/haskell.nix - patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} - ''; + patch -p1 -i ${./cardano-node--export-cardano-submit-api.diff} + ''; patched-cardano-node-flake' = (import inputs.flake-compat { src = { - outPath = toString patched-cardano-node-src; + outPath = toString (patched-cardano-node-src {withOurCode = true;}); inherit (inputs.cardano-node) rev shortRev lastModified lastModifiedDate; }; }) .defaultNix; - cardano-node-package-names = [ - "cardano-node" - "cardano-node-capi" - "cardano-node-chairman" - "cardano-submit-api" - "cardano-testnet" - "cardano-tracer" - "bench/cardano-profile" - "bench/cardano-topology" - "bench/locli" - "bench/plutus-scripts-bench" - "bench/tx-generator" - "trace-dispatcher" - "trace-resources" - "trace-forward" - ]; in rec { defaultPackage = testgen-hs; cardano-node-flake = cardano-node-flake'; @@ -127,46 +120,27 @@ in rec { devShell = let cardano-node-devshell = cardano-node-flake.devShells.${buildSystem}.default; - cabal-project-packages-old = lib.concatStringsSep "\n" [ - "packages:" - " cardano-node" - " cardano-node-capi" - " cardano-node-chairman" - " cardano-submit-api" - " cardano-testnet" - " cardano-tracer" - " bench/cardano-profile" - " bench/cardano-topology" - " bench/locli" - " bench/plutus-scripts-bench" - " bench/tx-generator" - " trace-dispatcher" - " trace-resources" - " trace-forward" - "" - ]; - cabal-project-packages-new = lib.concatStringsSep "\n" ( - ["packages:"] - ++ map (package: " ${patched-cardano-node-src}/${package}") cardano-node-package-names - ++ [ - " ${patched-cardano-api-src}" - " ${patched-cardano-ledger-src}/libs/cardano-ledger-core" - " ${patched-cardano-ledger-src}/libs/cardano-ledger-test" - " ${patched-cardano-ledger-src}/libs/constrained-generators" - " @REPO_ROOT@/testgen-hs" - "" - ] - ); cabal-project-base = cardano-node-flake.project.${buildSystem}.args.cabalProject; - cabal-project = - lib.replaceStrings [cabal-project-packages-old] [cabal-project-packages-new] cabal-project-base; - cabal-project-template = pkgs.writeText "cabal.project" cabal-project; + cabal-project-template = pkgs.writeText "cabal.project" cabal-project-base; + cabal-project-extra-packages = [ + "${patched-cardano-api-src}" + "${patched-cardano-ledger-src}/libs/cardano-ledger-core" + "${patched-cardano-ledger-src}/libs/cardano-ledger-test" + "${patched-cardano-ledger-src}/libs/constrained-generators" + "@REPO_ROOT@/testgen-hs" + ]; + cabal-project-extra-packages-json = builtins.toJSON cabal-project-extra-packages; + cabal-project-rewrite-script = pkgs.substituteAll { + src = ./rewrite_cabal_project.py; + cabal_project_template = toString cabal-project-template; + patched_node_src = toString (patched-cardano-node-src {withOurCode = false;}); + extra_packages_json = cabal-project-extra-packages-json; + }; in pkgs.mkShell { inputsFrom = [cardano-node-devshell]; shellHook = '' - repo_root=$(pwd) - sed "s|@REPO_ROOT@|$repo_root|g" ${cabal-project-template} > cabal.project + ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} ''; }; diff --git a/nix/rewrite_cabal_project.py b/nix/rewrite_cabal_project.py new file mode 100644 index 0000000..4031223 --- /dev/null +++ b/nix/rewrite_cabal_project.py @@ -0,0 +1,62 @@ +"""Rewrite cabal.project packages for the devshell.""" + +import json +from pathlib import Path + + +TEMPLATE_PATH = Path("@cabal_project_template@") +PATCHED_NODE_SRC = "@patched_node_src@" +EXTRA_PACKAGES = json.loads(r"""@extra_packages_json@""") +REPO_ROOT = str(Path.cwd()) + + +def rewrite_packages(text: str) -> str: + """Rewrite the packages section to use patched and extra packages.""" + lines = text.splitlines() + out = [] + i = 0 + while i < len(lines): + line = lines[i] + if line.strip() != "packages:": + out.append(line) + i += 1 + continue + + out.append(line) + i += 1 + block_lines = [] + while i < len(lines): + next_line = lines[i] + if next_line and not next_line.startswith(" "): + break + block_lines.append(next_line) + i += 1 + + trailing_blanks = [] + while block_lines and block_lines[-1].strip() == "": + trailing_blanks.insert(0, block_lines.pop()) + + for block_line in block_lines: + stripped = block_line.strip() + if stripped == "" or stripped.startswith("--"): + out.append(block_line) + continue + entry = stripped + if entry.startswith("/"): + out.append(f" {entry}") + else: + out.append(f" {PATCHED_NODE_SRC}/{entry}") + + for pkg in EXTRA_PACKAGES: + out.append(f" {pkg.replace('@REPO_ROOT@', REPO_ROOT)}") + + out.extend(trailing_blanks) + + result = "\n".join(out) + if text.endswith("\n"): + result += "\n" + return result + + +base_text = TEMPLATE_PATH.read_text(encoding="utf-8") +Path("cabal.project").write_text(rewrite_packages(base_text), encoding="utf-8") From 982cbdb70395e716077e82d4d6c3fac2c7b1325b Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 19:34:40 +0100 Subject: [PATCH 07/23] chore: add a few more formatters --- README.md | 5 +- flake.nix | 3 + testgen-hs/protocol-params-preview.json | 688 ++---------------------- 3 files changed, 55 insertions(+), 641 deletions(-) diff --git a/README.md b/README.md index e9c8aec..008c779 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Available commands: ``` ❯ testgen-hs generate -g1 -n1 ApplyTxErr_Conway ``` + ```json { "seed": 1731690796, @@ -266,11 +267,11 @@ And then easily reproduce it by providing the same seed that was found: ``` ❯ ( echo 8182068182028200a0 - echo 81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456 + echo 81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456 ) | testgen-hs deserialize-stream ``` -``` json +```json {"cbor":"8182068182028200a0","haskellRepr":"ApplyTxError (ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList [])) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList []))"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} {"cbor":"81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456","haskellRepr":"ApplyTxError (ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| []) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| [])"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} ``` diff --git a/flake.nix b/flake.nix index 78ec55e..8df3e9a 100644 --- a/flake.nix +++ b/flake.nix @@ -50,7 +50,10 @@ alejandra.enable = true; # Nix ormolu.enable = true; # Haskell cabal-fmt.enable = true; + prettier.enable = true; # Markdown, JSON, … shfmt.enable = true; + ruff-check.enable = true; # Python + ruff-format.enable = true; # Python yamlfmt.enable = pkgs.system != "x86_64-darwin"; # a treefmt-nix+yamlfmt bug on Intel Macs }; }; diff --git a/testgen-hs/protocol-params-preview.json b/testgen-hs/protocol-params-preview.json index 744c658..0aa0009 100644 --- a/testgen-hs/protocol-params-preview.json +++ b/testgen-hs/protocol-params-preview.json @@ -4,648 +4,58 @@ "committeeMinSize": 0, "costModels": { "PlutusV1": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 228465, - 122, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 85848, - 228465, - 122, - 0, - 1, - 1, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 53384111, - 14333, - 10 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, + 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, + 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, + 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, + 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, + 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, + 1, 1, 85848, 228465, 122, 0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, + 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, + 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 53384111, 14333, 10 ], "PlutusV2": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 228465, - 122, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 228465, - 122, - 0, - 1, - 1, - 85848, - 228465, - 122, - 0, - 1, - 1, - 955506, - 213312, - 0, - 2, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 43053543, - 10, - 53384111, - 14333, - 10, - 43574283, - 26308, - 10 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, + 42921, 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, + 1000, 60594, 1, 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, + 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, + 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, 85848, + 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, + 1, 1, 85848, 228465, 122, 0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, + 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, + 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10 ], "PlutusV3": [ - 100788, - 420, - 1, - 1, - 1000, - 173, - 0, - 1, - 1000, - 59957, - 4, - 1, - 11183, - 32, - 201305, - 8356, - 4, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 16000, - 100, - 100, - 100, - 16000, - 100, - 94375, - 32, - 132994, - 32, - 61462, - 4, - 72010, - 178, - 0, - 1, - 22151, - 32, - 91189, - 769, - 4, - 2, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 1, - 1000, - 42921, - 4, - 2, - 24548, - 29498, - 38, - 1, - 898148, - 27279, - 1, - 51775, - 558, - 1, - 39184, - 1000, - 60594, - 1, - 141895, - 32, - 83150, - 32, - 15299, - 32, - 76049, - 1, - 13169, - 4, - 22100, - 10, - 28999, - 74, - 1, - 28999, - 74, - 1, - 43285, - 552, - 1, - 44749, - 541, - 1, - 33852, - 32, - 68246, - 32, - 72362, - 32, - 7243, - 32, - 7391, - 32, - 11546, - 32, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 90434, - 519, - 0, - 1, - 74433, - 32, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 1, - 85848, - 123203, - 7305, - -900, - 1716, - 549, - 57, - 85848, - 0, - 1, - 955506, - 213312, - 0, - 2, - 270652, - 22588, - 4, - 1457325, - 64566, - 4, - 20467, - 1, - 4, - 0, - 141992, - 32, - 100788, - 420, - 1, - 1, - 81663, - 32, - 59498, - 32, - 20142, - 32, - 24588, - 32, - 20744, - 32, - 25933, - 32, - 24623, - 32, - 43053543, - 10, - 53384111, - 14333, - 10, - 43574283, - 26308, - 10, - 16000, - 100, - 16000, - 100, - 962335, - 18, - 2780678, - 6, - 442008, - 1, - 52538055, - 3756, - 18, - 267929, - 18, - 76433006, - 8868, - 18, - 52948122, - 18, - 1995836, - 36, - 3227919, - 12, - 901022, - 1, - 166917843, - 4307, - 36, - 284546, - 36, - 158221314, - 26549, - 36, - 74698472, - 36, - 333849714, - 1, - 254006273, - 72, - 2174038, - 72, - 2261318, - 64571, - 4, - 207616, - 8310, - 4, - 1293828, - 28716, - 63, - 0, - 1, - 1006041, - 43623, - 251, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 100181, - 726, - 719, - 0, - 1, - 107878, - 680, - 0, - 1, - 95336, - 1, - 281145, - 18848, - 0, - 1, - 180194, - 159, - 1, - 1, - 158519, - 8942, - 0, - 1, - 159378, - 8813, - 0, - 1, - 107490, - 3298, - 1, - 106057, - 655, - 1, - 1964219, - 24520, - 3 + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, + 8356, 4, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, + 16000, 100, 100, 100, 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, + 178, 0, 1, 22151, 32, 91189, 769, 4, 2, 85848, 123203, 7305, -900, 1716, + 549, 57, 85848, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, 898148, + 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, + 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, + 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, + 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, + 1, 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, + 85848, 0, 1, 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, + 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, + 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, 14333, 10, + 43574283, 26308, 10, 16000, 100, 16000, 100, 962335, 18, 2780678, 6, + 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, 18, 52948122, + 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, 36, + 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, + 72, 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, + 43623, 251, 0, 1, 100181, 726, 719, 0, 1, 100181, 726, 719, 0, 1, 100181, + 726, 719, 0, 1, 107878, 680, 0, 1, 95336, 1, 281145, 18848, 0, 1, 180194, + 159, 1, 1, 158519, 8942, 0, 1, 159378, 8813, 0, 1, 107490, 3298, 1, + 106057, 655, 1, 1964219, 24520, 3 ] }, "dRepActivity": 31, @@ -683,7 +93,7 @@ "maxValueSize": 5000, "minFeeRefScriptCostPerByte": 15, "minPoolCost": 170000000, - "monetaryExpansion": 0.0030, + "monetaryExpansion": 0.003, "poolPledgeInfluence": 0.3, "poolRetireMaxEpoch": 18, "poolVotingThresholds": { From 732a1d3ab45a85e2289834f877469ba3eb0d3dbe Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 19:36:21 +0100 Subject: [PATCH 08/23] chore: remove unnecessary comments --- nix/internal.nix | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index caa40a3..f06ac67 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -41,8 +41,7 @@ assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwi name = "cardano-ledger--${dep-tag}"; owner = "IntersectMBO"; repo = "cardano-ledger"; - #rev = "a9e78ae63cf8870f0ce6ce76bd7029b82ddb47e1"; # the one for cardano-node 10.4.1, tag: cardano-ledger-core-1.17.0.0 - rev = dep-tag; # the one for cardano-node 10.4.1 + rev = dep-tag; hash = "sha256-pD22f9VzNApynPhVYv0T7fsOZdbvYr1vlOxhKRhMSYk="; }; patched-cardano-ledger-src = pkgs.runCommandNoCC "cardano-ledger-src-patched" {} '' From 84c051dc867854693496950e2776dd2f6fe6bdfd Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Tue, 24 Feb 2026 20:17:58 +0100 Subject: [PATCH 09/23] chore: add `inputs.devshell` --- flake.lock | 21 +++++++++++++++++++++ flake.nix | 28 ++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index b85eaae..a641238 100644 --- a/flake.lock +++ b/flake.lock @@ -17,6 +17,26 @@ "type": "github" } }, + "devshell": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768818222, + "narHash": "sha256-460jc0+CZfyaO8+w8JNtlClB2n4ui1RbHfPTLkpwhU8=", + "owner": "numtide", + "repo": "devshell", + "rev": "255a2b1725a20d060f566e4755dbf571bbbb5f76", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, "flake-compat": { "flake": false, "locked": { @@ -98,6 +118,7 @@ "root": { "inputs": { "cardano-node": "cardano-node", + "devshell": "devshell", "flake-compat": "flake-compat", "flake-parts": "flake-parts", "nix-bundle-exe": "nix-bundle-exe", diff --git a/flake.nix b/flake.nix index 8df3e9a..6a41bd0 100644 --- a/flake.nix +++ b/flake.nix @@ -2,14 +2,26 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; - treefmt-nix.url = "github:numtide/treefmt-nix"; - treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; - flake-compat.url = "github:input-output-hk/flake-compat"; - flake-compat.flake = false; - cardano-node.url = "github:IntersectMBO/cardano-node/10.4.1"; - cardano-node.flake = false; # otherwise, +2k dependencies we don’t really use - nix-bundle-exe.url = "github:3noch/nix-bundle-exe"; - nix-bundle-exe.flake = false; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + flake-compat = { + url = "github:input-output-hk/flake-compat"; + flake = false; + }; + devshell = { + url = "github:numtide/devshell"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + cardano-node = { + url = "github:IntersectMBO/cardano-node/10.4.1"; + flake = false; # otherwise, +2k dependencies we don’t really use + }; + nix-bundle-exe = { + url = "github:3noch/nix-bundle-exe"; + flake = false; + }; }; outputs = inputs: let From 7a3de21df068956420dd92c4110bb60d2df55701 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 10:37:53 +0100 Subject: [PATCH 10/23] feat: add a working `numtide/devshell` --- .gitignore | 1 + flake.nix | 6 ++--- nix/internal.nix | 68 ++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index e4925d1..32402eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Nix result +result-* # Haskell / Cabal dist-newstyle/ diff --git a/flake.nix b/flake.nix index 6a41bd0..e9854c5 100644 --- a/flake.nix +++ b/flake.nix @@ -30,6 +30,7 @@ inputs.flake-parts.lib.mkFlake {inherit inputs;} ({config, ...}: { imports = [ inputs.treefmt-nix.flakeModule + inputs.devshell.flakeModule ]; flake.internal = @@ -52,9 +53,8 @@ default-x86_64-windows = inputs.self.internal.x86_64-windows.defaultPackage; }); - devShells = { - default = internal.devShell; - }; + devShells.old = internal.devShell.old; + devshells.new = internal.devShell.new; treefmt = {pkgs, ...}: { projectRootFile = "flake.nix"; diff --git a/nix/internal.nix b/nix/internal.nix index f06ac67..c423ca5 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -119,6 +119,35 @@ in rec { devShell = let cardano-node-devshell = cardano-node-flake.devShells.${buildSystem}.default; + cardano-node-inputs = lib.filter lib.isDerivation ( + (cardano-node-devshell.buildInputs or []) + ++ (cardano-node-devshell.nativeBuildInputs or []) + ++ (cardano-node-devshell.propagatedBuildInputs or []) + ++ (cardano-node-devshell.propagatedNativeBuildInputs or []) + ); + cardano-node-env = pkgs.buildEnv { + name = "cardano-node-devshell-env"; + paths = cardano-node-inputs; + ignoreCollisions = true; + }; + cardano-node-ghc-libdir = cardano-node-devshell.NIX_GHC_LIBDIR or ""; + + # numtide/devshell does not run stdenv setup hooks, so env vars that + # mkShell would set (e.g. PKG_CONFIG_PATH) are missing. + # PKG_CONFIG_PATH is critical: without it the cabal solver cannot + # verify pkgconfig-depends and dependency resolution fails. + # We extract it by running the same stdenv setup with the same inputs. + devshell-pkg-config-path = + pkgs.runCommand "devshell-pkg-config-path" { + inherit (cardano-node-devshell) buildInputs nativeBuildInputs; + propagatedBuildInputs = cardano-node-devshell.propagatedBuildInputs or []; + propagatedNativeBuildInputs = cardano-node-devshell.propagatedNativeBuildInputs or []; + } '' + source $stdenv/setup 2>/dev/null || true + mkdir -p $out + echo -n "''${PKG_CONFIG_PATH:-}" > $out/PKG_CONFIG_PATH + ''; + cabal-project-base = cardano-node-flake.project.${buildSystem}.args.cabalProject; cabal-project-template = pkgs.writeText "cabal.project" cabal-project-base; cabal-project-extra-packages = [ @@ -135,13 +164,48 @@ in rec { patched_node_src = toString (patched-cardano-node-src {withOurCode = false;}); extra_packages_json = cabal-project-extra-packages-json; }; - in - pkgs.mkShell { + in { + old = pkgs.mkShell { inputsFrom = [cardano-node-devshell]; shellHook = '' ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} ''; }; + new = { + pkgs, + config, + ... + }: { + name = "testgen-hs-devshell"; + env = + lib.optional (cardano-node-ghc-libdir != "") { + name = "NIX_GHC_LIBDIR"; + value = cardano-node-ghc-libdir; + } + ++ [ + { + name = "PKG_CONFIG_PATH"; + eval = "$(cat ${devshell-pkg-config-path}/PKG_CONFIG_PATH)\${PKG_CONFIG_PATH:+:\$PKG_CONFIG_PATH}"; + } + ]; + devshell = { + packages = [cardano-node-env]; + startup.rewrite-cabal-project.text = '' + ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} + ''; + motd = '' + + {202}🔨 Welcome to ${config.name}{reset} + $(menu) + + You can now run: + · {bold}cabal update{reset} + · {bold}cabal build testgen-hs{reset} + · or even {bold}haskell-language-server{reset} for LSP + ''; + }; + }; + }; testgen-hs = let patched-flake = patched-cardano-node-flake'; From 46f520d35dc2e00ccbc580a8666b10efd42ccc8c Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 12:21:14 +0100 Subject: [PATCH 11/23] feat: expose Haskell tools in the new devshell menu --- nix/internal.nix | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/nix/internal.nix b/nix/internal.nix index c423ca5..014ab82 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -132,6 +132,11 @@ in rec { }; cardano-node-ghc-libdir = cardano-node-devshell.NIX_GHC_LIBDIR or ""; + # Extract an individual package from the cardano-node devshell inputs + # so we can expose it as a devshell command with its own menu entry. + findInput = pred: label: + lib.findFirst pred (throw "devshell input '${label}' not found") cardano-node-inputs; + # numtide/devshell does not run stdenv setup hooks, so env vars that # mkShell would set (e.g. PKG_CONFIG_PATH) are missing. # PKG_CONFIG_PATH is critical: without it the cabal solver cannot @@ -188,6 +193,23 @@ in rec { eval = "$(cat ${devshell-pkg-config-path}/PKG_CONFIG_PATH)\${PKG_CONFIG_PATH:+:\$PKG_CONFIG_PATH}"; } ]; + commands = [ + { + name = "ghc"; + package = findInput (p: lib.hasPrefix "ghc-shell-for-packages" (p.name or "")) "ghc"; + category = "haskell"; + } + { + name = "cabal"; + package = findInput (p: (p.pname or "") == "cabal-install-exe-cabal") "cabal"; + category = "haskell"; + } + { + name = "haskell-language-server"; + package = findInput (p: (p.pname or "") == "haskell-language-server-exe-haskell-language-server") "hls"; + category = "haskell"; + } + ]; devshell = { packages = [cardano-node-env]; startup.rewrite-cabal-project.text = '' @@ -201,7 +223,7 @@ in rec { You can now run: · {bold}cabal update{reset} · {bold}cabal build testgen-hs{reset} - · or even {bold}haskell-language-server{reset} for LSP + · {bold}cabal run testgen-hs -- --help{reset} ''; }; }; From a36a99fcc446dcab94b84b2463a8554c0b32097c Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 12:25:14 +0100 Subject: [PATCH 12/23] feat: add `direnv` --- .envrc | 3 +++ .gitignore | 4 ++++ flake.nix | 3 +-- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..af3b9a0 --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +use flake + +source_env_if_exists .envrc.local diff --git a/.gitignore b/.gitignore index 32402eb..d224461 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ dist-newstyle/ /cabal.project /cabal.project.* .ghc.environment.* + +# direnv +.direnv/ +.envrc.local diff --git a/flake.nix b/flake.nix index e9854c5..d4872ff 100644 --- a/flake.nix +++ b/flake.nix @@ -53,8 +53,7 @@ default-x86_64-windows = inputs.self.internal.x86_64-windows.defaultPackage; }); - devShells.old = internal.devShell.old; - devshells.new = internal.devShell.new; + devshells.default = internal.devShell.new; treefmt = {pkgs, ...}: { projectRootFile = "flake.nix"; From 9c939a91506344cf717489ce473cf917ba328b53 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 21:55:11 +0100 Subject: [PATCH 13/23] fix: the devshell build for 10.6.2 --- .gitignore | 1 + nix/internal.nix | 87 ++++++++++++++++++++++++++++++++---- nix/rewrite_cabal_project.py | 31 ++++++++++++- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index d224461..d071cd6 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ result-* dist-newstyle/ /cabal.project /cabal.project.* +/.cabal/ .ghc.environment.* # direnv diff --git a/nix/internal.nix b/nix/internal.nix index 5073ff7..8eea986 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -53,10 +53,31 @@ assert builtins.elem targetSystem ["x86_64-linux" "aarch64-linux" "aarch64-darwi patch -p1 -d $out/libs/cardano-ledger-test -i ${./cardano-ledger-test--windows-fix.diff} ''} ''; + constrained-generators-src = patched-cardano-node-flake'.project.${buildSystem}.hsPkgs.constrained-generators.src; + patched-constrained-generators-src = constrained-generators-src; cardano-api-src = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-api.src; + cardano-api-id = cardano-node-flake'.project.${buildSystem}.hsPkgs.cardano-api.identifier; + # Extract the latest revised .cabal from the local CHaP index. + # CHaP revisions relax version bounds to match the package set; + # without this, the on-disk .cabal carries the original tight bounds + # and cabal-install's solver rejects valid dependency plans. + # + # GNU tar overwrites earlier entries with later ones, so a plain + # extract of a duplicate member yields the last revision — exactly + # the latest CHaP revision we need. + cardano-api-revised-cabal = let + member = "${cardano-api-id.name}/${cardano-api-id.version}/${cardano-api-id.name}.cabal"; + in + pkgs.runCommandNoCC "cardano-api-revised-cabal" {} '' + tar xf ${cardano-node-flake'.inputs.CHaP}/01-index.tar.gz '${member}' + cp '${member}' $out + ''; patched-cardano-api-src = pkgs.applyPatches { name = "cardano-api-src-patched"; src = cardano-api-src; + prePatch = '' + cp ${cardano-api-revised-cabal} cardano-api.cabal + ''; patches = [./cardano-api--expose-internal.diff]; }; patched-cardano-node-src = {withOurCode ? true}: @@ -159,23 +180,50 @@ in rec { "${patched-cardano-api-src}" "${patched-cardano-ledger-src}/libs/cardano-ledger-core" "${patched-cardano-ledger-src}/libs/cardano-ledger-test" - "${patched-cardano-ledger-src}/libs/constrained-generators" + "${patched-constrained-generators-src}" "@REPO_ROOT@/testgen-hs" ]; cabal-project-extra-packages-json = builtins.toJSON cabal-project-extra-packages; + # cardano-ledger-core uses the deprecated PV3.fromGHC (should be + # fromHaskellRatio). The upstream cabal.project sets -Werror globally, + # which promotes this deprecation warning into a build error. The nix + # build is unaffected because haskell.nix doesn't pass -Werror from + # the project file, but the devshell's cabal does. + cabal-project-extra-suffix = '' + package cardano-ledger-core + ghc-options: -Wwarn=deprecations + ''; cabal-project-rewrite-script = pkgs.substituteAll { src = ./rewrite_cabal_project.py; cabal_project_template = toString cabal-project-template; patched_node_src = toString (patched-cardano-node-src {withOurCode = false;}); extra_packages_json = cabal-project-extra-packages-json; + extra_project_suffix = cabal-project-extra-suffix; + local_chap_path = toString cardano-node-flake'.inputs.CHaP; }; in { - old = pkgs.mkShell { - inputsFrom = [cardano-node-devshell]; - shellHook = '' - ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} - ''; - }; + old = let + chap-store-path = toString cardano-node-flake'.inputs.CHaP; + in + pkgs.mkShell { + inputsFrom = [cardano-node-devshell]; + shellHook = '' + export CABAL_DIR="$PWD/.cabal" + ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} + + _chap_marker="$CABAL_DIR/.chap-store-path" + if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then + mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" + cabal update cardano-haskell-packages 2>/dev/null + printf '%s' '${chap-store-path}' > "$_chap_marker" + fi + + if [ ! -e "$CABAL_DIR/packages/hackage.haskell.org/01-index.tar" ]; then + echo "First-time setup: downloading Hackage package index…" + cabal update hackage.haskell.org + fi + ''; + }; new = { pkgs, config, @@ -188,6 +236,10 @@ in rec { value = cardano-node-ghc-libdir; } ++ [ + { + name = "CABAL_DIR"; + eval = "$PRJ_ROOT/.cabal"; + } { name = "PKG_CONFIG_PATH"; eval = "$(cat ${devshell-pkg-config-path}/PKG_CONFIG_PATH)\${PKG_CONFIG_PATH:+:\$PKG_CONFIG_PATH}"; @@ -212,8 +264,27 @@ in rec { ]; devshell = { packages = [cardano-node-env]; - startup.rewrite-cabal-project.text = '' + startup.rewrite-cabal-project.text = let + chap-store-path = toString cardano-node-flake'.inputs.CHaP; + in '' ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} + + # Re-index the local CHaP only when the underlying Nix store + # path changes (i.e. after a flake.lock update). A marker + # file records the store path that was last indexed. + _chap_marker="$CABAL_DIR/.chap-store-path" + if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then + mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" + cabal update cardano-haskell-packages 2>/dev/null + printf '%s' '${chap-store-path}' > "$_chap_marker" + fi + + # On first use of this project-local CABAL_DIR, also fetch the + # Hackage index so that 'cabal build' works out of the box. + if [ ! -e "$CABAL_DIR/packages/hackage.haskell.org/01-index.tar" ]; then + echo "First-time setup: downloading Hackage package index…" + cabal update hackage.haskell.org + fi ''; motd = '' diff --git a/nix/rewrite_cabal_project.py b/nix/rewrite_cabal_project.py index 4031223..16503ba 100644 --- a/nix/rewrite_cabal_project.py +++ b/nix/rewrite_cabal_project.py @@ -1,14 +1,19 @@ """Rewrite cabal.project packages for the devshell.""" import json +import re from pathlib import Path TEMPLATE_PATH = Path("@cabal_project_template@") PATCHED_NODE_SRC = "@patched_node_src@" EXTRA_PACKAGES = json.loads(r"""@extra_packages_json@""") +EXTRA_PROJECT_SUFFIX = r"""@extra_project_suffix@""" +LOCAL_CHAP_PATH = "@local_chap_path@" REPO_ROOT = str(Path.cwd()) +REPO_NAME = "cardano-haskell-packages" + def rewrite_packages(text: str) -> str: """Rewrite the packages section to use patched and extra packages.""" @@ -58,5 +63,29 @@ def rewrite_packages(text: str) -> str: return result +def use_local_chap(text: str) -> str: + """Point the CHaP repository at a local Nix-store copy. + + This uses the same pinned CHaP snapshot that cardano-node's haskell.nix + uses, giving us cabal-file revisions and full reproducibility. + """ + # Replace the "repository cardano-haskell-packages" block (url, secure, + # root-keys, and any key-threshold lines) with a local file: URL. + text = re.sub( + r"(?:--[^\n]*\n)*" # optional preceding comment lines + r"repository cardano-haskell-packages\n" + r"(?: [^\n]+\n)*", # indented continuation lines + f"repository {REPO_NAME}\n url: file:{LOCAL_CHAP_PATH}\n secure: True\n", + text, + ) + + return text + + base_text = TEMPLATE_PATH.read_text(encoding="utf-8") -Path("cabal.project").write_text(rewrite_packages(base_text), encoding="utf-8") +result = rewrite_packages(base_text) +if LOCAL_CHAP_PATH.strip(): + result = use_local_chap(result) +if EXTRA_PROJECT_SUFFIX.strip(): + result = result.rstrip("\n") + "\n\n" + EXTRA_PROJECT_SUFFIX.strip() + "\n" +Path("cabal.project").write_text(result, encoding="utf-8") From 2c28fe3f290fe49d08f249c632b87deeaae78df4 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 22:26:43 +0100 Subject: [PATCH 14/23] chore: remove the suggestion to manually run `cabal update` on entry --- nix/internal.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/nix/internal.nix b/nix/internal.nix index 8eea986..a07a6fa 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -292,7 +292,6 @@ in rec { $(menu) You can now run: - · {bold}cabal update{reset} · {bold}cabal build testgen-hs{reset} · {bold}cabal run testgen-hs -- --help{reset} ''; From 88832645d4ab3f7d01df5a5d35aab00a21590869 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 23:34:21 +0100 Subject: [PATCH 15/23] fix: make directory entry quicker by 260 ms --- nix/internal.nix | 19 ++++++++++++++++--- nix/rewrite_cabal_project.py | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index a07a6fa..f0558e5 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -200,7 +200,20 @@ in rec { extra_packages_json = cabal-project-extra-packages-json; extra_project_suffix = cabal-project-extra-suffix; local_chap_path = toString cardano-node-flake'.inputs.CHaP; + repo_root = "@REPO_ROOT@"; }; + + # Pre-generate cabal.project at Nix build time so the devshell + # startup doesn't need to launch Python (~260 ms saved). + # The only remaining placeholder is @REPO_ROOT@, which is + # sed-replaced with $PRJ_ROOT at shell entry. + cabal-project-generated = + pkgs.runCommand "cabal-project-generated" { + nativeBuildInputs = [pkgs.python3]; + } '' + python3 ${cabal-project-rewrite-script} + cp cabal.project $out + ''; in { old = let chap-store-path = toString cardano-node-flake'.inputs.CHaP; @@ -209,7 +222,7 @@ in rec { inputsFrom = [cardano-node-devshell]; shellHook = '' export CABAL_DIR="$PWD/.cabal" - ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} + sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated} > cabal.project _chap_marker="$CABAL_DIR/.chap-store-path" if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then @@ -267,8 +280,7 @@ in rec { startup.rewrite-cabal-project.text = let chap-store-path = toString cardano-node-flake'.inputs.CHaP; in '' - ${lib.getExe pkgs.python3} ${cabal-project-rewrite-script} - + sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated} > cabal.project # Re-index the local CHaP only when the underlying Nix store # path changes (i.e. after a flake.lock update). A marker # file records the store path that was last indexed. @@ -285,6 +297,7 @@ in rec { echo "First-time setup: downloading Hackage package index…" cabal update hackage.haskell.org fi + ''; motd = '' diff --git a/nix/rewrite_cabal_project.py b/nix/rewrite_cabal_project.py index 16503ba..e9e0cae 100644 --- a/nix/rewrite_cabal_project.py +++ b/nix/rewrite_cabal_project.py @@ -10,7 +10,7 @@ EXTRA_PACKAGES = json.loads(r"""@extra_packages_json@""") EXTRA_PROJECT_SUFFIX = r"""@extra_project_suffix@""" LOCAL_CHAP_PATH = "@local_chap_path@" -REPO_ROOT = str(Path.cwd()) +REPO_ROOT = "@repo_root@" REPO_NAME = "cardano-haskell-packages" From 2e315736abe85a77ebe76e0aa03f3f127f491693 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 23:39:24 +0100 Subject: [PATCH 16/23] chore: make intent explicit with file redirections in shell startup scripts --- nix/internal.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index f0558e5..c3bac1d 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -171,7 +171,7 @@ in rec { } '' source $stdenv/setup 2>/dev/null || true mkdir -p $out - echo -n "''${PKG_CONFIG_PATH:-}" > $out/PKG_CONFIG_PATH + echo -n "''${PKG_CONFIG_PATH:-}" >$out/PKG_CONFIG_PATH ''; cabal-project-base = cardano-node-flake.project.${buildSystem}.args.cabalProject; @@ -222,13 +222,13 @@ in rec { inputsFrom = [cardano-node-devshell]; shellHook = '' export CABAL_DIR="$PWD/.cabal" - sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated} > cabal.project + sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated} >"$PWD"/cabal.project _chap_marker="$CABAL_DIR/.chap-store-path" if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" cabal update cardano-haskell-packages 2>/dev/null - printf '%s' '${chap-store-path}' > "$_chap_marker" + printf '%s' '${chap-store-path}' >"$_chap_marker" fi if [ ! -e "$CABAL_DIR/packages/hackage.haskell.org/01-index.tar" ]; then @@ -280,7 +280,7 @@ in rec { startup.rewrite-cabal-project.text = let chap-store-path = toString cardano-node-flake'.inputs.CHaP; in '' - sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated} > cabal.project + sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated} >"$PRJ_ROOT"/cabal.project # Re-index the local CHaP only when the underlying Nix store # path changes (i.e. after a flake.lock update). A marker # file records the store path that was last indexed. @@ -288,7 +288,7 @@ in rec { if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then mkdir -p "$CABAL_DIR/packages/cardano-haskell-packages" cabal update cardano-haskell-packages 2>/dev/null - printf '%s' '${chap-store-path}' > "$_chap_marker" + printf '%s' '${chap-store-path}' >"$_chap_marker" fi # On first use of this project-local CABAL_DIR, also fetch the From ad8c0eced4524dd4f783e55695be80580b49e917 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 23:46:44 +0100 Subject: [PATCH 17/23] fix: only modify `cabal.project` when it's needed (keep mtime more stable for Cabal) --- nix/internal.nix | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index c3bac1d..87ac440 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -222,7 +222,10 @@ in rec { inputsFrom = [cardano-node-devshell]; shellHook = '' export CABAL_DIR="$PWD/.cabal" - sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated} >"$PWD"/cabal.project + _new=$(sed "s|@REPO_ROOT@|$PWD|g" ${cabal-project-generated}) + if [ ! -e "$PWD/cabal.project" ] || [ "$(cat "$PWD/cabal.project")" != "$_new" ]; then + printf '%s\n' "$_new" > "$PWD/cabal.project" + fi _chap_marker="$CABAL_DIR/.chap-store-path" if [ ! -e "$_chap_marker" ] || [ "$(cat "$_chap_marker")" != "${chap-store-path}" ]; then @@ -280,7 +283,10 @@ in rec { startup.rewrite-cabal-project.text = let chap-store-path = toString cardano-node-flake'.inputs.CHaP; in '' - sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated} >"$PRJ_ROOT"/cabal.project + _new=$(sed "s|@REPO_ROOT@|$PRJ_ROOT|g" ${cabal-project-generated}) + if [ ! -e "$PRJ_ROOT/cabal.project" ] || [ "$(cat "$PRJ_ROOT/cabal.project")" != "$_new" ]; then + printf '%s\n' "$_new" > "$PRJ_ROOT/cabal.project" + fi # Re-index the local CHaP only when the underlying Nix store # path changes (i.e. after a flake.lock update). A marker # file records the store path that was last indexed. From e0d192861ad83641d45cbcb6e18d416d62d9b0cf Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 25 Feb 2026 23:48:59 +0100 Subject: [PATCH 18/23] chore: make `nixlint` pass --- nix/internal.nix | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/nix/internal.nix b/nix/internal.nix index 87ac440..27b1da6 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -240,11 +240,7 @@ in rec { fi ''; }; - new = { - pkgs, - config, - ... - }: { + new = {config, ...}: { name = "testgen-hs-devshell"; env = lib.optional (cardano-node-ghc-libdir != "") { From 8049e57d1be4408b6175dd548437c45f66a981a0 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 18 Mar 2026 12:55:22 +0100 Subject: [PATCH 19/23] fix(lsp): use `makeRelativeToProject` with `embedFile` to fix a HLS issue See --- testgen-hs/SynthEvalTx.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testgen-hs/SynthEvalTx.hs b/testgen-hs/SynthEvalTx.hs index 0e6145c..1411cf0 100644 --- a/testgen-hs/SynthEvalTx.hs +++ b/testgen-hs/SynthEvalTx.hs @@ -42,7 +42,7 @@ import Data.Aeson import qualified Data.Aeson as J import qualified Data.Aeson.Encoding as AesonEncoding import qualified Data.ByteString as BS -import Data.FileEmbed (embedFile) +import Data.FileEmbed (embedFile, makeRelativeToProject) import qualified Data.Map.Strict as Map import qualified Data.Set as Set import Data.Text (Text) @@ -135,7 +135,7 @@ dummySystemStart = SystemStart $ UTCTime (fromGregorian 2020 7 29) (secondsToDiffTime 0) protocolParamsJSON :: BS.ByteString -protocolParamsJSON = $(embedFile "protocol-params-preview.json") +protocolParamsJSON = $(embedFile =<< makeRelativeToProject "protocol-params-preview.json") protocolParams :: PParams (Cardano.Ledger.Api.Era.ConwayEra) protocolParams = From 9fd64cb5c0a078a16ace3a091c5f0ec0ab88b2a2 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 18 Mar 2026 13:06:39 +0100 Subject: [PATCH 20/23] chore: add a `README.md` section about LSP and `direnv` --- README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/README.md b/README.md index 008c779..bed9a87 100644 --- a/README.md +++ b/README.md @@ -275,3 +275,39 @@ And then easily reproduce it by providing the same seed that was found: {"cbor":"8182068182028200a0","haskellRepr":"ApplyTxError (ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList [])) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayCertsFailure (WithdrawalsNotInRewardsCERTS (fromList []))"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} {"cbor":"81820681820481581cc0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456","haskellRepr":"ApplyTxError (ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| []) :| [])","json":{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraConway","error":["ConwayWdrlNotDelegatedToDRep (KeyHash {unKeyHash = \"c0231342a5c66b25d652a7116559d02cbe9515ef890fd698de38d456\"} :| [])"],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"},"typeTag":"ApplyTxError (ConwayEra StandardCrypto)"} ``` + +## Development – HLS setup + +This project provides a Nix devshell with GHC and HLS. To get full IDE +support you need [`direnv`](https://direnv.net/) with +[`nix-direnv`](https://github.com/nix-community/nix-direnv) so that the +`.envrc` is picked up automatically. See their official documentation for +installation instructions. + +The instructions below use VS Code as an example, but any editor with LSP and +`direnv` support (Neovim, Emacs, etc.) works -- just configure it to use the +`haskell-language-server` binary from `PATH` rather than a bundled one. + +### VS Code example + +1. Install the extensions **Haskell** (`haskell.haskell`) and **direnv** + (`mkhl.direnv`). + +2. Tell the Haskell extension to use the HLS provided by the devshell + instead of managing its own. Add this to your VS Code settings: + + ```json + { "haskell.manageHLS": "PATH" } + ``` + +3. Allow direnv for this project and restart HLS: + + ``` + direnv allow + ``` + + Then in VS Code: `Ctrl+Shift+P` -> _Haskell: Restart Haskell LSP Server_. + +> **Note:** The first HLS session takes roughly 10 minutes while `hie-bios` +> builds all dependencies via `cabal v2-repl`. Subsequent loads use the +> cache at `~/.cache/hie-bios/`. From 67e0bb475ccb5e16a551cbbc0e8e2fb2f7797f62 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 18 Mar 2026 13:09:29 +0100 Subject: [PATCH 21/23] fix(punctation): use Unicode --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bed9a87..fba2992 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,7 @@ support you need [`direnv`](https://direnv.net/) with installation instructions. The instructions below use VS Code as an example, but any editor with LSP and -`direnv` support (Neovim, Emacs, etc.) works -- just configure it to use the +`direnv` support (Neovim, Emacs, etc.) works – just configure it to use the `haskell-language-server` binary from `PATH` rather than a bundled one. ### VS Code example @@ -306,7 +306,7 @@ The instructions below use VS Code as an example, but any editor with LSP and direnv allow ``` - Then in VS Code: `Ctrl+Shift+P` -> _Haskell: Restart Haskell LSP Server_. + Then in VS Code: `Ctrl+Shift+P` → _Haskell: Restart Haskell LSP Server_. > **Note:** The first HLS session takes roughly 10 minutes while `hie-bios` > builds all dependencies via `cabal v2-repl`. Subsequent loads use the From 3ebc43c7e3a8ef9a817d9da7d082b24b642b0136 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 18 Mar 2026 15:24:43 +0100 Subject: [PATCH 22/23] =?UTF-8?q?fix(vscode):=20don=E2=80=99t=20try=20to?= =?UTF-8?q?=20use=20`haskell-language-server-wrapper`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fba2992..8e98735 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,11 @@ The instructions below use VS Code as an example, but any editor with LSP and instead of managing its own. Add this to your VS Code settings: ```json - { "haskell.manageHLS": "PATH" } + { + "haskell.manageHLS": "PATH", + "haskell.serverExecutablePath": "haskell-language-server", + "haskell.plugin.semanticTokens.globalOn": true + } ``` 3. Allow direnv for this project and restart HLS: From 29966da2aafbf93eb751126f5992404e3103a104 Mon Sep 17 00:00:00 2001 From: Michal Rus Date: Wed, 18 Mar 2026 15:39:15 +0100 Subject: [PATCH 23/23] fix(lsp): expose a no-op `haskell-language-server-wrapper` --- README.md | 18 +++++++++++------- nix/internal.nix | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 8e98735..dd58c9e 100644 --- a/README.md +++ b/README.md @@ -285,8 +285,9 @@ support you need [`direnv`](https://direnv.net/) with installation instructions. The instructions below use VS Code as an example, but any editor with LSP and -`direnv` support (Neovim, Emacs, etc.) works – just configure it to use the -`haskell-language-server` binary from `PATH` rather than a bundled one. +`direnv` support (Neovim, Emacs, etc.) works the same way. The devshell puts +both `haskell-language-server` and `haskell-language-server-wrapper` on +`PATH`, so most editors should pick up HLS automatically. ### VS Code example @@ -297,11 +298,14 @@ The instructions below use VS Code as an example, but any editor with LSP and instead of managing its own. Add this to your VS Code settings: ```json - { - "haskell.manageHLS": "PATH", - "haskell.serverExecutablePath": "haskell-language-server", - "haskell.plugin.semanticTokens.globalOn": true - } + { "haskell.manageHLS": "PATH" } + ``` + + Optionally, enable semantic highlighting (richer colours for types, + type variables, etc.): + + ```json + { "haskell.plugin.semanticTokens.globalOn": true } ``` 3. Allow direnv for this project and restart HLS: diff --git a/nix/internal.nix b/nix/internal.nix index 27b1da6..7ca4b97 100644 --- a/nix/internal.nix +++ b/nix/internal.nix @@ -158,6 +158,17 @@ in rec { findInput = pred: label: lib.findFirst pred (throw "devshell input '${label}' not found") cardano-node-inputs; + haskell-language-server = findInput (p: (p.pname or "") == "haskell-language-server-exe-haskell-language-server") "haskell-language-server"; + + # Shim so that editors looking for haskell-language-server-wrapper find it + # on PATH; the devshell already sets the correct GHC. + haskell-language-server-wrapper = pkgs.writeShellApplication { + name = "haskell-language-server-wrapper"; + text = '' + exec ${lib.getExe haskell-language-server} "$@" + ''; + }; + # numtide/devshell does not run stdenv setup hooks, so env vars that # mkShell would set (e.g. PKG_CONFIG_PATH) are missing. # PKG_CONFIG_PATH is critical: without it the cabal solver cannot @@ -270,12 +281,15 @@ in rec { } { name = "haskell-language-server"; - package = findInput (p: (p.pname or "") == "haskell-language-server-exe-haskell-language-server") "hls"; + package = haskell-language-server; category = "haskell"; } ]; devshell = { - packages = [cardano-node-env]; + packages = [ + cardano-node-env + haskell-language-server-wrapper + ]; startup.rewrite-cabal-project.text = let chap-store-path = toString cardano-node-flake'.inputs.CHaP; in ''