Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
Comment on lines +1 to +6

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add a trailing newline to satisfy linters.

YAMLlint flags β€œno new line character at the end of file.” Add a newline to keep CI green and editors happy.

Apply:

-      interval: weekly
+      interval: weekly
+
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
🧰 Tools
πŸͺ› YAMLlint (1.37.1)

[error] 6-6: no new line character at the end of file

(new-line-at-end-of-file)

πŸ€– Prompt for AI Agents
In .github/dependabot.yml around lines 1 to 6 the file is missing a trailing
newline which causes yamllint to fail; edit the file and add a single newline
character at the end of the file (ensure the file ends with a line break) then
save/commit so linters and CI see the newline.

🧹 Nitpick (assertive)

Tame update noise with groups and metadata (still one ecosystem: github-actions).

Given the PR’s goal to standardize infra, group GH Actions updates to reduce PR churn and add metadata for routing.

Example:

 version: 2
 updates:
   - package-ecosystem: github-actions
     directory: /
     schedule:
       interval: weekly
+    groups:
+      gha-all:
+        patterns:
+          - "*"
+    reviewers:
+      - "neo-haskell/maintainers"
+    labels:
+      - "dependencies"
+      - "gha"

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
πŸͺ› YAMLlint (1.37.1)

[error] 6-6: no new line character at the end of file

(new-line-at-end-of-file)

πŸ€– Prompt for AI Agents
In .github/dependabot.yml lines 1-6, the config currently creates separate
weekly PRs for GitHub Actions updates; add a dependabot "groups" section to
bundle all github-actions updates into a single grouped PR and add metadata
fields (labels, reviewers or assignees, and an optional
open-pull-requests-limit) to the update entry so PRs are routed and
rate-limited; update the file by adding a top-level groups mapping that
names/combines the github-actions updates into one group and extend the existing
update entry with metadata keys like labels and reviewers/assignees and set
open-pull-requests-limit to control churn.

28 changes: 11 additions & 17 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,27 @@ jobs:
run: brew install bash

- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27

- name: Build the project with Nix
run: nix-build

- name: Cache Cabal
id: cache
uses: actions/cache@v4
- uses: DeterminateSystems/determinate-nix-action@v3.8.6
with:
path: |
~/.cabal
dist-newstyle
.devenv/profile
key: ${{ runner.os }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/*.lock') }}-1 # modify the key if the cache is not working as expected
extra-conf: |
extra-substituters = https://cache.iog.io
extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
Comment on lines +34 to +36

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Confirm extra-conf semantics; add a guard to prove caches are active.

Using extra-conf with extra-substituters and extra-trusted-public-keys is supported and appends to existing lists, but it’s order-dependent in nix.conf. Add a quick verification step to fail fast if the cache isn’t actually being used. (github.com, nix.dev)

Apply this minimal check right after the Determinate Nix step:

       - uses: DeterminateSystems/determinate-nix-action@v3.8.6
         with:
           extra-conf: |
             extra-substituters = https://cache.iog.io
             extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
+      - name: Verify Nix cache wiring
+        run: |
+          nix --version
+          nix show-config | sed -n 's/^substituters = //p; s/^trusted-public-keys = //p'
+          nix show-config | grep -q 'https://cache.iog.io' || { echo "cache.iog.io not in substituters"; exit 1; }
+          nix show-config | grep -q 'hydra.iohk.io:' || { echo "IOG key not in trusted-public-keys"; exit 1; }
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
extra-conf: |
extra-substituters = https://cache.iog.io
extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
extra-conf: |
extra-substituters = https://cache.iog.io
extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=
- name: Verify Nix cache wiring
run: |
nix --version
nix show-config | sed -n 's/^substituters = //p; s/^trusted-public-keys = //p'
nix show-config | grep -q 'https://cache.iog.io' || { echo "cache.iog.io not in substituters"; exit 1; }
nix show-config | grep -q 'hydra.iohk.io:' || { echo "IOG key not in trusted-public-keys"; exit 1; }
πŸ€– Prompt for AI Agents
In .github/workflows/test.yml around lines 34-36, the workflow appends
extra-substituters and extra-trusted-public-keys via extra-conf but doesn't
verify they took effect; add a new job step immediately after the Determinate
Nix step that programmatically checks nix configuration/substituters and fails
fast if the configured cache URL (https://cache.iog.io) is not present or not
active β€” e.g., invoke nix to output current substituters/config (JSON or text),
assert the cache URL appears in the substituters list, and exit nonzero if it
does not; keep the check minimal and CI-friendly so the workflow fails early
when the cache is not actually used.


- name: Update Cabal Hackage list
# if: steps.cache.outputs.cache-hit != 'true'
run: nix-shell --run "cabal update"
- uses: DeterminateSystems/magic-nix-cache-action@v13

Comment on lines +38 to +39

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ› οΈ Refactor suggestion

Also pin Magic Nix Cache to a commit SHA.

Mitigate action supply‑chain risk by pinning to an exact commit, not a mutable tag.

-      - uses: DeterminateSystems/magic-nix-cache-action@v13
+      - uses: DeterminateSystems/magic-nix-cache-action@<commit-sha-of-v13>
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: DeterminateSystems/magic-nix-cache-action@v13
- uses: DeterminateSystems/magic-nix-cache-action@<commit-sha-of-v13>
πŸ€– Prompt for AI Agents
.github/workflows/test.yml around lines 38-39: the workflow currently references
DeterminateSystems/magic-nix-cache-action@v13 which is a mutable tag; replace
this with an exact commit SHA to pin the action. Locate the desired commit on
the DeterminateSystems/magic-nix-cache-action GitHub repo, copy the full commit
SHA (40 chars) and update the uses line to use @<commit-sha>; commit the change
and run the workflow to verify it still works.

- name: Build the project with Nix
run: nix-build
Comment on lines +40 to +41

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Increase build log visibility for flakier investigations.

Add -L to surface derivation logs in CI output.

-      - name: Build the project with Nix
-        run: nix-build
+      - name: Build the project with Nix
+        run: nix-build -L
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Build the project with Nix
run: nix-build
- name: Build the project with Nix
run: nix-build -L
πŸ€– Prompt for AI Agents
In .github/workflows/test.yml around lines 40 to 41, the CI build step runs
"nix-build" without the -L flag which hides derivation logs; update the step to
run "nix-build -L" so derivation logs are printed to the workflow output,
improving visibility for flaky or failing builds.


- name: Build the project with cabal
run: nix-shell --run "cabal build all"

- name: Run core tests
run: nix-shell --run "cabal test core"

- name: Run core doctests
run: nix-shell --run "run-doctests-verbose"
# Doctests are failing - See issue #204
# - name: Run core doctests
# run: nix-shell --run "cabal repl --with-compiler=doctest --repl-options=--verbose core"
Comment on lines +49 to +51

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Keep doctests running but non-blocking to avoid drift.

Instead of commenting out, run them with continue-on-error so failures are visible but don’t gate merges while #204 is open.

-      # Doctests are failing - See issue #204
-      # - name: Run core doctests
-      #   run: nix-shell --run "cabal repl --with-compiler=doctest --repl-options=--verbose core"
+      - name: Run core doctests (non-blocking) # See issue #204
+        continue-on-error: true
+        run: nix-shell --run "cabal repl --with-compiler=doctest --repl-options=--verbose core"
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Doctests are failing - See issue #204
# - name: Run core doctests
# run: nix-shell --run "cabal repl --with-compiler=doctest --repl-options=--verbose core"
- name: Run core doctests (non-blocking) # See issue #204
continue-on-error: true
run: nix-shell --run "cabal repl --with-compiler=doctest --repl-options=--verbose core"
πŸ€– Prompt for AI Agents
In .github/workflows/test.yml around lines 49 to 51, the doctest step has been
commented out; restore the step instead of removing it and make it non-blocking
by adding continue-on-error: true to the step. Re-enable the "Run core doctests"
step with the same run command (nix-shell --run "cabal repl
--with-compiler=doctest --repl-options=--verbose core") and add
continue-on-error: true (and keep the step name) so failures are reported in the
workflow but do not fail the job while issue #204 is open.


- name: Run CLI tests
run: nix-shell --run "cabal test cli"
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"Monoid",
"Nanotime",
"NEOHASKELL",
"nhcli",
"nhcore",
"nixfmt",
"nixpkgs",
Expand Down
4 changes: 2 additions & 2 deletions cli/nhcli.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.4
name: nhcli
version: 0.9.0
version: 0.9.1
synopsis: Command Line Tool for NeoHaskell
description: NeoHaskell is a dialect of Haskell that is focused on newcomer-friendliness and productivity.
homepage: https://neohaskell.org
Expand All @@ -11,7 +11,7 @@ maintainer: nhlib@nickseagull.dev
copyright:
category: Development
build-type: Simple
extra-source-files: ../nix/haskellnix.nix, ../nix/haskell-project.nix
extra-source-files: ./nix/haskell-project.nix

common common_cfg
ghc-options: -Wall
Expand Down
41 changes: 29 additions & 12 deletions cli/nix/haskell-project.nix
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
{ pkgs }:

# Generic function to create a Haskell project template
{ projectName, src }:
{ packages, mainPackageName, executableName }:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Verification agent

🧩 Analysis chain

Parameterize the compiler to avoid silent breakage when haskell.nix moves; verify ghc910 availability

Hard-coding compiler-nix-name = "ghc910" couples you to a specific compiler and may break as the pinned haskell.nix evolves. Make it configurable and default sensibly; also confirm that ghc910 is supported by your pinned commit.

Apply this diff to parameterize:

-{ packages, mainPackageName, executableName }:
+{ packages, mainPackageName, executableName, compilerNixName ? "ghc910" }:

And this one to use it:

-            compiler-nix-name = "ghc910";
+            compiler-nix-name = compilerNixName;

To confirm the compiler alias is valid for your pin, run a quick web check:

Also applies to: 19-20


🌐 Web query:

For haskell.nix commit 4d493449406ec91db804511a6d15b6f076ba40e7, what values are accepted for `compiler-nix-name`, and is `ghc910` supported?

πŸ’‘ Result:

For haskell.nix commit 4d493449406ec91db804511a6d15b6f076ba40e7, the accepted values for compiler-nix-name are compiler names like ghc96, ghc967, ghc98, ghc984, ghc910, ghc9102, ghc912, and ghc9122, each corresponding to specific GHC versions[2][8]. ghc910 is explicitly supported as one of the accepted values for GHC 9.10.x[2].

Key details:

  • Values for compiler-nix-name:
    • Examples: ghc96, ghc967 (GHC 9.6.x), ghc98, ghc984 (GHC 9.8.x), ghc910, ghc9102 (GHC 9.10.x), ghc912, ghc9122 (GHC 9.12.x)[2][8].
    • You should consult the project's source or overlays/bootstrap.nix for the complete, up-to-date list, as additional minor patch versions may also be accepted (e.g., ghc9102)[2].
  • Supported status:
    • The commit in question supports ghc910 as a valid value, and it is listed as tested in CI for nixpkgs-unstable[2].
  • How to use:
    • In configuration functions such as callCabalProjectToNix, set compiler-nix-name = "ghc910" to use GHC 9.10.x[8].
  • If a name does not match a defined compiler, you will see a Nix errorβ€”use the exact value from the documentation or table[2][6].
GHC Version compiler-nix-name example Supported/Tested?
9.6.x ghc96, ghc967 Yes
9.8.x ghc98, ghc984 Yes
9.10.x ghc910, ghc9102 Yes
9.12.x ghc912, ghc9122 Yes

Additional notes:

  • While ghc910 is supported and cached, experimental/minor patch versions may work if present in overlays/bootstrap.nix, but build times may be longer if not cached[2].
  • If you require an older or newer compiler not listed, advanced users can add new GHC versions following haskell.nix contributing instructions[2].

In summary, for haskell.nix at 4d493449406ec91db804511a6d15b6f076ba40e7, use ghc910 to select GHC 9.10.xβ€”the value is supported and actively tested[2][8].

Citations:


Parameterize compiler-nix-name with default "ghc910"

We’ve confirmed that ghc910 is natively supported in haskell.nix at commit 4d493449406ec91db804511a6d15b6f076ba40e7 (GHC 9.10.x). To prevent silent breakage when upstream evolves, refactor as follows:

β€’ In cli/nix/haskell-project.nix, change the function signature (around line 3):

-{ packages, mainPackageName, executableName }:
+{ packages, mainPackageName, executableName, compilerNixName ? "ghc910" }:

β€’ Replace the hard-coded compiler name (around lines 19–20):

-            compiler-nix-name = "ghc910";
+            compiler-nix-name = compilerNixName;

This makes the compiler choice configurable (defaulting to a verified, supported value) and guards against future compatibility issues.

πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around line 3, add a new optional parameter to
the function signature: compiler-nix-name ? "ghc910", and in the body around
lines 19–20 replace the hard-coded "ghc910" literal with the compiler-nix-name
parameter so the compiler can be configured while defaulting to the verified
ghc910 value.

let
haskellNix = import (pkgs.fetchFromGitHub {
owner = "input-output-hk";
repo = "haskell.nix";
rev = "4d493449406ec91db804511a6d15b6f076ba40e7";
sha256 = "sha256-CHNMDgFfpTV5WkVhmMLf5d5qaLUjgeoziVgmgnhPGrI=";
}) { inherit pkgs; };
# Apply haskell.nix overlay
}) { };
pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
overlays = [
haskellNix.overlay
(final: _prev: {
"${projectName}Project" = final.haskell-nix.cabalProject {
inherit src;
compiler-nix-name = "ghc910";
};
})
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final.haskell-nix.cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
Comment on lines +14 to +21

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Quote hyphenated attribute 'haskell-nix' to avoid a parse error

Attribute names with hyphens must be quoted in Nix. As written, final.haskell-nix.cabalProject will not parse correctly.

Apply this diff:

-          value = final.haskell-nix.cabalProject {
+          value = final."haskell-nix".cabalProject {
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final.haskell-nix.cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final."haskell-nix".cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around lines 14 to 21, attribute names containing
hyphens (like haskell-nix) must be quoted to parse correctly; replace usages
such as final.haskell-nix.cabalProject with final."haskell-nix".cabalProject
(and any other occurrences of haskell-nix in attribute access) so the attribute
name is quoted, leaving the rest of the expression unchanged.

];
Comment on lines 11 to 22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Name collisions in the overlay are possible; consider a namespaced project set.

Injecting each package at the top level (pkgsWithOverlay.nhcli, pkgsWithOverlay.nhcore) risks collisions with existing attributes. Consider nesting under a single attribute (e.g., final.neohaskellPackages) and selecting from there.

Apply:

-      (final: _prev:
-        builtins.listToAttrs (builtins.map (packageName: {
-          name = "${packageName}";
-          value = final."haskell-nix".cabalProject {
-            src = packages.${packageName};
-            compiler-nix-name = compilerNixName;
-          };
-        }) (builtins.attrNames packages)))
+      (final: _prev:
+        {
+          neohaskellPackages =
+            builtins.listToAttrs (builtins.map (packageName: {
+              name = packageName;
+              value = final."haskell-nix".cabalProject {
+                src = packages.${packageName};
+                compiler-nix-name = compilerNixName;
+              };
+            }) (builtins.attrNames packages));
+        })

And later:

-  pkg = builtins.getAttr mainPackageName pkgsWithOverlay;
+  pkg = builtins.getAttr mainPackageName pkgsWithOverlay.neohaskellPackages;
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
overlays = [
haskellNix.overlay
(final: _prev: {
"${projectName}Project" = final.haskell-nix.cabalProject {
inherit src;
compiler-nix-name = "ghc910";
};
})
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final.haskell-nix.cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
];
pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
overlays = [
haskellNix.overlay
# Namespace all generated projects under `neohaskellPackages`
(final: _prev:
{
neohaskellPackages =
builtins.listToAttrs (builtins.map (packageName: {
name = packageName;
value = final."haskell-nix".cabalProject {
src = packages.${packageName};
compiler-nix-name = compilerNixName;
};
}) (builtins.attrNames packages));
})
];
};
# Select from the namespaced set instead of the top level
pkg = builtins.getAttr mainPackageName pkgsWithOverlay.neohaskellPackages;
πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around lines 11–22, the current overlay injects
each package directly at the top level which can cause name collisions; instead
create a namespaced attribute (e.g., final.neohaskellPackages) that contains the
generated package set. Modify the overlay so it adds a single attribute like
neohaskellPackages = builtins.listToAttrs(...mapping...) rather than returning
the mapped attrs at the top level, keep the same mapping logic and names, and
update any code that referenced pkgsWithOverlay.<pkg> to select from
pkgsWithOverlay.neohaskellPackages.<pkg>.

inherit (haskellNix) config;
};
Comment on lines 11 to 24

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Lock imported nixpkgs system to match caller to prevent accidental cross-system evaluation.

Pass system = pkgs.system when importing nixpkgs-unstable to ensure consistency with the outer pkgs.

-  pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
+  pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
+    system = pkgs.system;
     overlays = [
       haskellNix.overlay
       (final: _prev:
         builtins.listToAttrs (builtins.map (packageName: {
           name = "${packageName}";
           value = final."haskell-nix".cabalProject {
             src = packages.${packageName};
             compiler-nix-name = compilerNixName;
           };
         }) (builtins.attrNames packages)))
     ];
     inherit (haskellNix) config;
   };
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
overlays = [
haskellNix.overlay
(final: _prev: {
"${projectName}Project" = final.haskell-nix.cabalProject {
inherit src;
compiler-nix-name = "ghc910";
};
})
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final.haskell-nix.cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
];
inherit (haskellNix) config;
};
pkgsWithOverlay = import haskellNix.sources.nixpkgs-unstable {
system = pkgs.system;
overlays = [
haskellNix.overlay
(final: _prev:
builtins.listToAttrs (builtins.map (packageName: {
name = "${packageName}";
value = final.haskell-nix.cabalProject {
src = packages.${packageName};
compiler-nix-name = "ghc910";
};
}) (builtins.attrNames packages)))
];
inherit (haskellNix) config;
};
πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around lines 11 to 24 the import of
haskellNix.sources.nixpkgs-unstable does not specify a system, which can cause
cross-system evaluation; update the import call to pass system = pkgs.system
(i.e. import haskellNix.sources.nixpkgs-unstable { system = pkgs.system;
overlays = [ ... ]; inherit (haskellNix) config; }) so the imported nixpkgs is
locked to the outer pkgs system.


flake = pkgsWithOverlay."${projectName}Project".flake { };
pkg = pkgsWithOverlay."${mainPackageName}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Prefer builtins.getAttr for dynamic attribute selection

Using a string-literal selector here works, but builtins.getAttr is idiomatic and avoids edge cases with interpolation in attr paths.

Apply this diff:

-  pkg = pkgsWithOverlay."${mainPackageName}";
+  pkg = builtins.getAttr mainPackageName pkgsWithOverlay;
πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around line 26, the attribute selection uses
string interpolation (pkg = pkgsWithOverlay."${mainPackageName}";) which is less
idiomatic and can have edge cases; replace it by using builtins.getAttr to
lookup the attribute dynamically: call builtins.getAttr with mainPackageName and
pkgsWithOverlay so pkg is assigned the attribute value safely and idiomatically.


in flake.packages."${projectName}:exe:${projectName}"
in {
shell = pkg.shellFor {
tools = {
cabal = { };
hlint = { };
haskell-language-server = { };
hspec-discover = { };
};
buildInputs = [
pkgs.nil
pkgs.nixfmt-classic
pkgs.nixpkgs-fmt
pkgs.pkg-config
pkgs.zlib
];
};
package = (pkg.flake { }).packages."${executableName}";
}
Comment on lines +44 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Fail early if executableName is wrong; add an assertion.

Guard against typos by asserting the package key exists in the flake outputs.

-  package = (pkg.flake { }).packages."${executableName}";
+  package =
+    let
+      flakeOut = pkg.flake { };
+      pkgsOut  = flakeOut.packages;
+    in
+      assert builtins.hasAttr executableName pkgsOut;
+      pkgsOut."${executableName}";
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
package = (pkg.flake { }).packages."${executableName}";
}
package =
let
flakeOut = pkg.flake { };
pkgsOut = flakeOut.packages;
in
assert builtins.hasAttr executableName pkgsOut;
pkgsOut."${executableName}";
}
πŸ€– Prompt for AI Agents
In cli/nix/haskell-project.nix around lines 44-45, the code directly indexes the
flake outputs with package = (pkg.flake { }).packages."${executableName}"; which
silently fails if executableName is misspelled; add a precondition using
builtins.hasAttr (or compute the packages attrset first) and assert that the key
exists before indexing, then use that validated attrset to select the package so
the build fails early with a clear assertion message when the package key is
missing.

2 changes: 1 addition & 1 deletion cli/src/Neo.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ run = do
Command.CommandOptions
{ name = "neo",
description = "NeoHaskell's console helper",
version = Just [Core.version|0.9.0|],
version = Just [Core.version|0.9.1|],
decoder = commandsParser
Comment on lines +52 to 53

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Verification agent

🧩 Analysis chain

Version banner updated to 0.9.1 β€” approved.

Consistent with cabal version bumps in this PR.

Run this quick sweep to ensure no stale version references remain:


🏁 Script executed:

#!/bin/bash
# Find old version references and version banners
rg -nP -C2 '\[Core\.version\|\d+\.\d+\.\d+\|]' --type=haskell
rg -nP '0\.9\.0' -g '!**/dist/**' -g '!**/result/**'

Length of output: 570


Stale version references detectedβ€”update to 0.9.1 before merging.

The CLI banner in cli/src/Neo.hs is correctly bumped to 0.9.1, but the following outdated 0.9.0 pins remain:

β€’ website/package-lock.json:2318,9404
– "synckit": "^0.9.0" β†’ "^0.9.1"
β€’ website/package-lock.json:7630
– "@deno/kv": ">=0.9.0" β†’ ">=0.9.1"

Command the update of these entries and regenerate the lock file.

πŸ€– Prompt for AI Agents
In cli/src/Neo.hs around lines 52 to 53 the CLI version was bumped to 0.9.1 but
website/package-lock.json still pins older 0.9.0 versions; update
website/package-lock.json entries: change "synckit": "^0.9.0" to "^0.9.1" at
offsets ~2318 and ~9404 and change "@deno/kv": ">=0.9.0" to ">=0.9.1" at ~7630,
then regenerate the lock file (npm install / npm ci or your repo's lock regen
step) to ensure checksums and transitive entries are consistent before merging.

}
cmd <- Command.parseHandler parser
Expand Down
8 changes: 5 additions & 3 deletions cli/src/Neo/Build.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ handle config = do
File.writeText targetAppPath appMainFile
|> Task.mapError (\_ -> CustomError "Could not write app main file")

-- FIXME: Create another thread that renders the output of the build via streaming.
-- As right now there's no output at all
let buildExpression :: Text =
[fmt|{ pkgs ? import <nixpkgs> {} }:
( (#{nixFile}) { inherit pkgs; } ).package|]

completion <-
Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", nixFile]) rootFolder Subprocess.InheritBOTH
Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", buildExpression]) rootFolder Subprocess.InheritBOTH
Comment on lines +78 to +83

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Verification agent

🧩 Analysis chain

nix-build -E must evaluate to a derivation; don’t pass a lambda.

Like nix-shell, nix-build -E expects a derivation or set of derivations, not a top-level lambda. The current expression begins with a lambda and will not build.

Minimal fix using let/in:

-  let buildExpression :: Text =
-        [fmt|{ pkgs ? import <nixpkgs> {} }:
-  ( (#{nixFile}) { inherit pkgs; } ).package|]
+  let buildExpression :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } ).package|]

Alternative (clearer target selection): evaluate the set and select -A package:

-  completion <-
-    Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", buildExpression]) rootFolder Subprocess.InheritBOTH
+  let projectSetExpr :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } )|]
+  completion <-
+    Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", projectSetExpr, "-A", "package"]) rootFolder Subprocess.InheritBOTH

Also see my notes on pinning nixpkgs to avoid reliance on NIX_PATH.

Run to double-check:


🏁 Script executed:

#!/bin/bash
rg -n -C2 'nix-build' cli/src/Neo/Build.hs

Length of output: 260


Enforce derivation evaluation for nix-build -E

By my omniscient decree, nix-build -E must receive a derivation, not a naked lambda. The current buildExpression begins with a top-level lambda and will fail.

Required changes in cli/src/Neo/Build.hs:

β€’ Replace the lambda form of buildExpression (around lines 78–83) with a let/in that returns a derivation:

-  let buildExpression :: Text =
-        [fmt|{ pkgs ? import <nixpkgs> {} }:
-  ( (#{nixFile}) { inherit pkgs; } ).package|]
+  let buildExpression :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } ).package|]

β€’ Alternatively, select the attribute from the set and pass it via -A package:

-  completion <-
-    Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", buildExpression]) rootFolder Subprocess.InheritBOTH
+  let projectSetExpr :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } )|]
+  completion <-
+    Subprocess.openInherit
+      "nix-build"
+      ( Array.fromLinkedList
+          [ "-E", projectSetExpr
+          , "-A", "package"
+          ]
+      )
+      rootFolder
+      Subprocess.InheritBOTH

β€’ Pin your <nixpkgs> import to a fixed commit or channel to avoid unpredictable builds via NIX_PATH.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let buildExpression :: Text =
[fmt|{ pkgs ? import <nixpkgs> {} }:
( (#{nixFile}) { inherit pkgs; } ).package|]
completion <-
Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", nixFile]) rootFolder Subprocess.InheritBOTH
Subprocess.openInherit "nix-build" (Array.fromLinkedList ["-E", buildExpression]) rootFolder Subprocess.InheritBOTH
let buildExpression :: Text =
[fmt|let pkgs = import <nixpkgs> {}; in
( (#{nixFile}) { inherit pkgs; } ).package|]
let projectSetExpr :: Text =
[fmt|let pkgs = import <nixpkgs> {}; in
( (#{nixFile}) { inherit pkgs; } )|]
completion <-
Subprocess.openInherit
"nix-build"
( Array.fromLinkedList
[ "-E", projectSetExpr
, "-A", "package"
]
)
rootFolder
Subprocess.InheritBOTH
πŸ€– Prompt for AI Agents
In cli/src/Neo/Build.hs around lines 78 to 83 the buildExpression is a top-level
lambda which yields a naked function and fails with nix-build -E because it must
receive a derivation; replace the lambda form with a let/in that imports the
pinned nixpkgs and evaluates the expression to a derivation (for example bind
pkgs = import (builtins.fetchTarball "<fixed-commit-or-channel>") {}; let drv =
(#{nixFile}) { inherit pkgs; } in drv), or alternatively change invocation to
pass the set attribute via -A package and select the attribute from the imported
set, and ensure you pin <nixpkgs> to a fixed commit or channel instead of using
an unpinned <nixpkgs>.

if completion.exitCode != 0
then errorOut completion.stderr
else print completion.stdout
Expand Down
7 changes: 5 additions & 2 deletions cli/src/Neo/Build/Templates/Nix.hs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ let
hp = (#{haskellProject});

in (hp { inherit pkgs; }) {
projectName = "#{name}";
src = ./.;
packages = {
"#{name}" = ./.;
};
mainPackageName = "#{name}";
executableName = "#{name}:exe:#{name}";
Comment on lines +21 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Verification agent

🧩 Analysis chain

Confirm executableName matches the cabal executable stanza.

You’re selecting the build target by a fully-qualified executableName. Ensure this value matches the actual executable defined in the cabal file; otherwise nix-build will fail to resolve the attribute.

If this template is used for arbitrary projects, consider deriving executableName from the generated Cabal template to avoid drift.

[suggest_nitpick]
Use this script to list cabal package and executable names:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Cabal packages and executables detected:"
fd -t f --extension cabal -a | while read -r f; do
  echo "--- $f"
  rg -nP '^\s*name\s*:\s*(\S+)' "$f" || true
  rg -nP '^\s*executable\s+(\S+)' -n "$f" || true
done

Length of output: 719


Fix Nix executableName to match Cabal executable

The nhcli.cabal file declares the executable stanza as β€œneo”, but your Nix template uses:

executableName = "#{name}:exe:#{name}";

which expands to nhcli:exe:nhcli and will fail to resolve.

Please update in cli/src/Neo/Build/Templates/Nix.hs (lines 21-25):

  • Replace
    executableName = "#{name}:exe:#{name}";
    with
    executableName = "#{name}:exe:neo";

To prevent this slipping out of sync in future, consider deriving executableName automatically from the .cabal manifest rather than hard-coding it.

πŸ€– Prompt for AI Agents
In cli/src/Neo/Build/Templates/Nix.hs around lines 21 to 25, the Nix template
sets executableName incorrectly to "#{name}:exe:#{name}" which expands to
nhcli:exe:nhcli and won't match the cabal executable named "neo"; change the
executableName value to "#{name}:exe:neo" so it resolves to nhcli:exe:neo and
matches the cabal stanza; optionally consider deriving the executable name from
the .cabal manifest in the future to avoid hard-coding.

}

|]
5 changes: 3 additions & 2 deletions cli/src/Neo/Shell.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ handle config = do
File.writeText targetAppPath appMainFile
|> Task.mapError (\_ -> CustomError "Could not write app main file")

let shellExpression =
let shellExpression :: Text =
[fmt|{ pkgs ? import <nixpkgs> {} }:
( (#{nixFile}) { inherit pkgs; } ).env|]
( (#{nixFile}) { inherit pkgs; } ).shell|]

Comment on lines +78 to +81

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Verification agent

🧩 Analysis chain

Stop returning a lambda to nix-shell; emit a derivation (use let/in).

nix-shell -E expects an expression that evaluates to a derivation or a shell env, not a top-level lambda. The current expression starts with a lambda and will not be evaluated as intended.

Apply this fix to bind pkgs via let/in and return the shell directly:

-  let shellExpression :: Text =
-        [fmt|{ pkgs ? import <nixpkgs> {} }:
-  ( (#{nixFile}) { inherit pkgs; } ).shell|]
+  let shellExpression :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } ).shell|]

Follow-up: the direct import of assumes NIX_PATH is set. Either pin nixpkgs (preferred) or ensure CI sets NIX_PATH/-I. See my comments on default.nix/common.nix for a robust pin.

Run this to confirm nix-shell evaluates to a derivation and not a lambda:


🏁 Script executed:

#!/bin/bash
# Show what the expression evaluates to (should not be a lambda)
rg -n -C2 'shellExpression' cli/src/Neo/Shell.hs

Length of output: 477


Stop returning a top‐level lambda to nix-shell; emit a derivation

nix-shell -E requires an expression that evaluates directly to a derivation or shell env, not a lambda. In cli/src/Neo/Shell.hs (lines 78–81), replace:

-  let shellExpression :: Text =
-        [fmt|{ pkgs ? import <nixpkgs> {} }:
-  ( (#{nixFile}) { inherit pkgs; } ).shell|]
+  let shellExpression :: Text =
+        [fmt|let pkgs = import <nixpkgs> {}; in
+  ( (#{nixFile}) { inherit pkgs; } ).shell|]

Additionally, avoid relying on an externally set NIX_PATH. Either pin nixpkgs to a fixed revision (preferred) or ensure your CI/README documents setting -I nixpkgs=<path> before invoking nix-shell.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let shellExpression :: Text =
[fmt|{ pkgs ? import <nixpkgs> {} }:
( (#{nixFile}) { inherit pkgs; } ).env|]
( (#{nixFile}) { inherit pkgs; } ).shell|]
let shellExpression :: Text =
[fmt|let pkgs = import <nixpkgs> {}; in
( (#{nixFile}) { inherit pkgs; } ).shell|]
πŸ€– Prompt for AI Agents
In cli/src/Neo/Shell.hs around lines 78–81, the code currently builds a
top-level lambda that returns a function instead of emitting a derivation;
change the expression so it evaluates directly to the derivation/shell (i.e.,
apply/import nixpkgs and call into the nix file to produce its .shell result
rather than returning a lambda). Replace the lambda-style "{ pkgs ? import
<nixpkgs> {} }:" wrapper with an expression that imports/pins a specific nixpkgs
revision (or uses a passed -I nixpkgs=<path>) and then immediately evaluates
(#{nixFile}).shell so nix-shell -E gets a derivation; also update CI/README to
document the chosen approach if you opt to require -I instead of pinning.

completion <-
Subprocess.openInherit "nix-shell" (Array.fromLinkedList ["-E", shellExpression]) rootFolder Subprocess.InheritBOTH
if completion.exitCode != 0
Expand Down
12 changes: 12 additions & 0 deletions common.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{ pkgs ? import ./nix/nixpkgs.nix { } }:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Thread the compiler choice; do not hard-code what the cosmos may change.

Expose a configurable compiler and pass it down to the project builder; default to a verified choice.

Apply:

-{ pkgs ? import ./nix/nixpkgs.nix { } }:
+{ pkgs ? import ./nix/nixpkgs.nix { }, compilerNixName ? "ghc910" }:
 ...
-in (hp {
+in (hp {
   packages = {
     "nhcore" = ./core;
     "nhcli" = ./cli;
   };
 
   mainPackageName = "nhcli";
   executableName = "nhcli:exe:neo";
-})
+  compilerNixName = compilerNixName;
+})

Also applies to: 10-12

πŸ€– Prompt for AI Agents
In common.nix around lines 1 and 10-12, the file currently hard-codes the
compiler via the imported pkgs; change the top-level attribute set to accept a
configurable compiler argument with a safe default from pkgs (e.g. default to
the verified compiler in pkgs like pkgs.stdenv.cc or the project's validated
compiler), then thread that compiler argument through wherever the project
builder is invoked (lines ~10-12) so the builder uses the passed-in compiler
instead of the hard-coded pkgs value; ensure the parameter name is documented in
the file header and used in all places that construct or call the project
builder.

🧹 Nitpick (assertive)

Eliminate the default pkgs import here to prevent accidental double-pinning.

Since default.nix and shell.nix already pass a pinned pkgs, making pkgs mandatory here avoids mixing nixpkgs instances if someone imports common.nix directly.

Apply:

-{ pkgs ? import ./nix/nixpkgs.nix { }, compilerNixName ? "ghc910" }:
+{ pkgs, compilerNixName ? "ghc910" }:

Committable suggestion skipped: line range outside the PR's diff.

πŸ€– Prompt for AI Agents
In common.nix around line 1, remove the default import so pkgs is a required
argument to avoid accidental double-pinning; change the function header to
accept only pkgs (no default import) and update any call sites to pass the
pinned pkgs from default.nix/shell.nix if they don’t already, ensuring no
implicit import of nixpkgs remains.


let hp = import ./cli/nix/haskell-project.nix { inherit pkgs; };
in (hp {
packages = {
"nhcore" = ./.;
"nhcli" = ./.;
};

mainPackageName = "nhcli";
executableName = "nhcli:exe:neo";
})
2 changes: 1 addition & 1 deletion core/nhcore.cabal
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cabal-version: 3.4
name: nhcore
version: 0.9.0
version: 0.9.1
synopsis: Core Library for NeoHaskell
description: NeoHaskell is a dialect of Haskell that is focused on newcomer-friendliness and productivity.
homepage: https://neohaskell.org
Expand Down
3 changes: 2 additions & 1 deletion default.nix
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
import ./cli/default.nix
{ pkgs ? import ./nix/nixpkgs.nix { } }:
((import ./common.nix { inherit pkgs; }).package)
12 changes: 8 additions & 4 deletions nix/nixpkgs.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Pinned nixpkgs for NeoHaskell project
# Last updated: 2025-08-25
Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Reduce duplication; bind the revision once and derive the name.

This avoids editing multiple spots when bumping nixpkgs.

Apply:

-import (builtins.fetchTarball {
-  name = "nixpkgs-20075955";
-  url = "https://github.com/nixos/nixpkgs/archive/20075955deac2583bb12f07151c2df830ef346b4.tar.gz";
+let
+  rev = "20075955deac2583bb12f07151c2df830ef346b4";
+in import (builtins.fetchTarball {
+  name = "nixpkgs-${rev}";
+  url = "https://github.com/nixos/nixpkgs/archive/${rev}.tar.gz";

Also applies to: 5-6

πŸ€– Prompt for AI Agents
In nix/nixpkgs.nix around lines 1-2 (and also apply the same change to lines
5-6), the nixpkgs revision and name are duplicated; bind the revision to a
single variable at the top and derive the package set name from that variable,
then replace all other hard-coded revision/name occurrences to reference those
variables so future bumps only change one value.

# Commit from nixos-unstable branch (2025-08-19)
import (builtins.fetchTarball {
name = "nixpkgs-054144e5";
url =
"https://github.com/nixos/nixpkgs/archive/054144e516ca1e3cf5706d5c3427d350eaa107af.tar.gz";
sha256 = "0ixd2mc9kkmr8mz20ah2j7akgwllxsjv94ah895zgi9b9q1h25ka";
name = "nixpkgs-20075955";
url = "https://github.com/nixos/nixpkgs/archive/20075955deac2583bb12f07151c2df830ef346b4.tar.gz";
# To get the sha256, run:
# nix-prefetch-url --unpack https://github.com/nixos/nixpkgs/archive/20075955deac2583bb12f07151c2df830ef346b4.tar.gz
sha256 = "1s3lxb33cwazlx72pygcbcc76bbgbhdil6q9bhqbzbjxj001zk0w";
})
63 changes: 38 additions & 25 deletions scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,51 @@ if [[ $REPLY =~ ^[Nn]$ ]]; then
else
echo "πŸ”§ Adding binary cache configuration..."

# Create /etc/nix/nix.conf if it doesn't exist
if [ ! -f /etc/nix/nix.conf ]; then
echo "Creating /etc/nix/nix.conf..."
sudo mkdir -p /etc/nix
sudo touch /etc/nix/nix.conf
# Add binary cache configuration to custom config file
sudo mkdir -p /etc/nix
if [ ! -f /etc/nix/nix.custom.conf ]; then
echo "Creating /etc/nix/nix.custom.conf..."
sudo touch /etc/nix/nix.custom.conf
fi

# Check if the substituters line exists
if sudo grep -q "^extra-substituters" /etc/nix/nix.conf; then
# Add to existing line if not already present
if ! sudo grep -q "$BINARY_CACHE_SUBSTITUTERS" /etc/nix/nix.conf; then
escaped_substituters=$(printf '%s\n' "$BINARY_CACHE_SUBSTITUTERS" | sed 's/[[\.*^$()+?{|]/\\&/g')
sudo sed -i "/^extra-substituters/s/$/ $escaped_substituters/" /etc/nix/nix.conf
fi
else
# Add new line
echo "extra-substituters = $BINARY_CACHE_SUBSTITUTERS" | sudo tee -a /etc/nix/nix.conf > /dev/null
# Append our config if not already present
if ! sudo grep -q "$BINARY_CACHE_SUBSTITUTERS" /etc/nix/nix.custom.conf; then
echo "extra-substituters = $BINARY_CACHE_SUBSTITUTERS" | sudo tee -a /etc/nix/nix.custom.conf > /dev/null
fi
if ! sudo grep -q "$BINARY_CACHE_PUBLIC_KEYS" /etc/nix/nix.custom.conf; then
echo "extra-trusted-public-keys = $BINARY_CACHE_PUBLIC_KEYS" | sudo tee -a /etc/nix/nix.custom.conf > /dev/null
fi

# Check if the trusted-public-keys line exists
if sudo grep -q "^extra-trusted-public-keys" /etc/nix/nix.conf; then
# Add to existing line if not already present
if ! sudo grep -q "$BINARY_CACHE_PUBLIC_KEYS" /etc/nix/nix.conf; then
escaped_keys=$(printf '%s\n' "$BINARY_CACHE_PUBLIC_KEYS" | sed 's/[[\.*^$()+?{|]/\\&/g')
sudo sed -i "/^extra-trusted-public-keys/s/$/ $escaped_keys/" /etc/nix/nix.conf
echo "βœ… Binary cache configuration added successfully!"

# Restart nix daemon to pick up new configuration
echo "πŸ”„ Restarting Nix daemon to apply configuration changes..."

# Detect platform and restart daemon accordingly
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
if sudo launchctl kickstart -k system/org.nixos.nix-daemon 2>/dev/null; then
echo "βœ… Nix daemon restarted successfully on macOS"
else
echo "⚠️ Failed to restart nix daemon on macOS - configuration will take effect on next restart"
fi
elif command -v systemctl &> /dev/null; then
# Linux with systemd (including WSL with systemd)
if sudo systemctl restart nix-daemon.service 2>/dev/null; then
echo "βœ… Nix daemon restarted successfully"
else
echo "⚠️ Failed to restart nix daemon - configuration will take effect on next restart"
fi
else
# Add new line
echo "extra-trusted-public-keys = $BINARY_CACHE_PUBLIC_KEYS" | sudo tee -a /etc/nix/nix.conf > /dev/null
# WSL or Linux without systemd - try pkill approach
echo "πŸ”„ Attempting to restart nix daemon..."
if sudo pkill nix-daemon 2>/dev/null; then
echo "βœ… Nix daemon stopped - it will restart automatically on next use"
else
echo "⚠️ Cannot automatically restart nix daemon on this system"
echo " Configuration will take effect when you restart your shell or reboot"
fi
fi
Comment on lines +76 to 92

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add a fallback when systemctl restart fails.

On some distros/WSL with systemd present but not fully functional, systemctl may exist yet fail. Fall back to pkill in that branch to avoid a no-op.

Apply this refinement:

@@
-    elif command -v systemctl &> /dev/null; then
+    elif command -v systemctl &> /dev/null; then
         # Linux with systemd (including WSL with systemd)
-        if sudo systemctl restart nix-daemon.service 2>/dev/null; then
+        if sudo systemctl restart nix-daemon.service 2>/dev/null; then
             echo "βœ… Nix daemon restarted successfully"
         else
-            echo "⚠️  Failed to restart nix daemon - configuration will take effect on next restart"
+            echo "⚠️  Failed to restart nix daemon via systemd - attempting manual stop so it auto-restarts"
+            if sudo pkill nix-daemon 2>/dev/null; then
+                echo "βœ… Nix daemon stopped - it will restart automatically on next use"
+            else
+                echo "⚠️  Cannot automatically restart nix daemon on this system"
+                echo "    Configuration will take effect when you restart your shell or reboot"
+            fi
         fi
πŸ€– Prompt for AI Agents
In scripts/install.sh around lines 76 to 92, the current branch tries systemctl
restart and on failure prints a warning but does not attempt the pkill fallback;
update the logic so that if sudo systemctl restart nix-daemon.service fails
(exit non-zero) you then attempt the pkill fallback (sudo pkill nix-daemon
2>/dev/null) and emit the appropriate success/failure messages based on pkill's
result, keeping existing messages and exit-code behavior and ensuring output
distinguishes "systemctl failed, falling back to pkill" versus final failure.


echo "βœ… Binary cache configuration added successfully!"
fi

echo ""
Expand Down
59 changes: 1 addition & 58 deletions shell.nix
Original file line number Diff line number Diff line change
@@ -1,58 +1 @@
{ pkgs ? import ./nix/nixpkgs.nix { }, }:
let
nix-pre-commit-hooks = import (builtins.fetchTarball
"https://github.com/cachix/git-hooks.nix/tarball/master");
in let
pre-commit = {
# Configured with the module options defined in `modules/pre-commit.nix`:
pre-commit-check = nix-pre-commit-hooks.run {
src = ./.;
# If your hooks are intrusive, avoid running on each commit with a default_states like this:
# default_stages = ["manual" "pre-push"];
hooks = {
fourmolu.enable = true;
nixfmt-classic.enable = true;
};
};
};
in pkgs.mkShell rec {
buildInputs = [
# Haskell dev tools
pkgs.ghc
pkgs.cabal-install
pkgs.haskell-language-server
pkgs.fourmolu
pkgs.hlint
pkgs.haskellPackages.zlib
pkgs.haskellPackages.hspec-discover
pkgs.haskellPackages.doctest

# Nix dev tools
pkgs.nil
pkgs.nixfmt-classic
pkgs.nixpkgs-fmt

# Website
pkgs.nodejs

# Required native libs
pkgs.pkg-config
pkgs.zlib
] ++ pre-commit.pre-commit-check.enabledPackages;

shellHook = ''
unset TEMP TMP TEMPDIR TMPDIR # Required for nix-shell to work
${pre-commit.pre-commit-check.shellHook}

run-doctests-verbose() {
cabal repl --with-compiler=doctest --repl-options=--verbose core
}

run-doctests() {
cabal repl --with-compiler=doctest core
}
'';

# Required for cabal to find the location of zlib and other native libraries
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
}
{ pkgs ? import ./nix/nixpkgs.nix { } }: ((import ./common.nix { inherit pkgs; }).shell)
Loading