Skip to content

stack: prefer a committed .cabal over re-running hpack (#626, #767) - #2547

Open
hamishmack wants to merge 9 commits into
masterfrom
hkm/issue-626-767-stack-prefer-cabal
Open

hamishmack wants to merge 9 commits into
masterfrom
hkm/issue-626-767-stack-prefer-cabal

Conversation

@hamishmack

@hamishmack hamishmack commented Jul 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

findCabalFiles UsePackageYamlFirst re-rendered a package.yaml through hpack whenever it existed, even when the package directory also had a committed .cabal file. That discarded the committed file and failed outright with ".cabal was generated with a newer version of hpack, please upgrade" (#626) or ".cabal was modified manually, please use --force" (#767).

It now uses the committed .cabal when one exists and falls back to hpack only when there is no .cabal. --ignore-package-yaml (IgnorePackageYaml) still forces cabal-only discovery.

This applies to:

  • stack-to-nix (lib/Stack2nix/Project.hs).
  • plan-to-nix (cabal projects). It had its own copy of findCabalFiles that still rendered package.yaml. On the Nix side, call-cabal-project-to-nix.nix only skips the shell hpack run when a .cabal exists, so cabal-install planned against the committed .cabal while plan-to-nix generated .plan.nix from hpack output and marked it cabal-generator = "hpack". comp-builder.nix then re-ran hpack at build time and could hit the same .cabal was modified manually, please use --force to overwrite. #767 error. Plan2Nix.Project is removed and plan-to-nix uses Stack2nix.Project.findCabalFiles; with an on-disk .cabal no cabal-generator is emitted, so the build-time hpack run is skipped.
  • cabal-name (used to name stack git dependencies), which already shared the stack function.
  • The same change in the nix-tools-sh copy, whose files were identical.

Behaviour changes to be aware of

  • A stale committed .cabal wins. If package.yaml was edited without regenerating the .cabal, haskell.nix uses the committed .cabal. Stack itself would regenerate it (unless the .cabal was hand-modified or made by a newer hpack). This is deliberate: it matches what cabal-install sees, avoids running hpack, and the fix is to commit the regenerated .cabal. No staleness warning: hpack's -- hash: header hashes the .cabal body (to detect hand edits), not package.yaml, so detecting staleness would mean rendering every package.yaml through hpack, which is the cost and failure mode this change removes.
  • Several .cabal files plus a package.yaml: all the .cabal files are returned (as with no package.yaml); cabal-name takes the first.

Tests

Self-contained test suite stack-project-tests (temp dir only, no external tools or network) asserting that findCabalFiles:

plan-to-nix now calls the same function, so these cover it too.

Verification

  • Compiled and ran stack-project-tests with nixpkgs GHC 9.10 + Cabal 3.16: all pass.
  • Typechecked plan-to-nix (plan2nix/Main.hs) for nix-tools, and for nix-tools-sh against nix-tools' cabal2nix library (the -sh one needs the stable-haskell Cabal fork). No new warnings.
  • Full nix-tools build and test run are left to CI (check-nix-tools).

Notes

  • Release-gated: haskell.nix uses nix-tools as a prebuilt static release binary, so this reaches haskell.nix builds only after the next nix-tools release (cf. error in flake. #2506).
  • A --use-package-yaml opt-out (stack's regenerate-always behaviour) was considered and skipped; easy to add later if wanted.

Closes #626.
Closes #767.

In the stack path, findCabalFiles UsePackageYamlFirst always re-rendered a package.yaml through hpack whenever it existed, even when the package directory also contained a committed .cabal file. This discarded the committed file and historically failed outright when the .cabal had been hand-modified or was produced by a newer hpack.

The cabalProject path already prefers a committed .cabal (call-cabal-project-to-nix.nix skips hpack when a .cabal exists). Mirror that in the stack path: use the on-disk .cabal when present, falling back to hpack only when there is no .cabal. --ignore-package-yaml continues to force cabal-only discovery.

Adds a self-contained (no external tools/network) test-suite stack-project-tests exercising findCabalFiles.
`StackProjectTests` imports `Cabal2Nix` for the `CabalFile` constructors, which
lives in the `cabal2nix` internal library rather than the main `nix-tools`
library, so the test suite failed to compile with

    Could not load module 'Cabal2Nix'
    It is a member of the hidden package 'nix-tools-0.1.0.0:cabal2nix'.

Add `nix-tools:cabal2nix` to its build-depends.
…tack-prefer-cabal

# Conflicts:
#	nix-tools/nix-tools/nix-tools.cabal

Copilot AI left a comment

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.

🟢 Approval recommended

Static review found no blocking issues in the focused discovery change or test integration; compilation and test execution remain unverified.

No open issues 🎉

What changed in this PR

Updates Stack package discovery to prefer an existing .cabal file over regenerating it with hpack, aligning with the Cabal path and addressing #626 and #767.

Changes:

  • Uses hpack only when no .cabal file exists, preserving IgnorePackageYaml behavior.
  • Adds and registers four self-contained tests covering file preference and fallback behavior.
  • Delivery to haskell.nix builds remains gated on a nix-tools release.
File Description
nix-tools/​nix-tools/​tests/​StackProjectTests.hs Adds regression tests for discovery and hpack fallback.
nix-tools/​nix-tools/​nix-tools.cabal Registers the stack-project-tests suite.
nix-tools/​nix-tools/​lib/​Stack2nix/​Project.hs Prefers on-disk .cabal files before invoking hpack.

Review effort: Balanced

…tack-prefer-cabal

# Conflicts:
#	nix-tools/nix-tools/nix-tools.cabal
plan-to-nix kept its own copy of findCabalFiles, which still rendered
package.yaml through hpack whenever one existed.  For a cabalProject with
both files cabal-install planned against the committed .cabal, but the
generated .plan.nix came from hpack and was marked cabal-generator =
"hpack", so comp-builder re-ran hpack at build time and could hit the
same "modified manually" failure as #767.

Drop Plan2Nix.Project and use Stack2nix.Project.findCabalFiles, so both
tools take the committed .cabal (even a stale one) and only fall back to
hpack when there is no .cabal.  The same change is applied to the
nix-tools-sh copy, whose files were identical.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

.cabal was modified manually, please use --force to overwrite. Do not call hpack if .cabal exists

2 participants