Skip to content

Migrate toolchain to the new Spago (spago.yaml + registry), including the Lua package-set forks #55

@Unisay

Description

@Unisay

Motivation

Legacy spago 0.21.x is the last release from the Haskell codebase, and it is the only release that still understands spago.dhall / packages.dhall. All ongoing Spago development happens in the PureScript rewrite (≥ 0.93), which dropped Dhall entirely in favour of spago.yaml, the PureScript Registry, and a spago.lock lock file. Staying on the legacy driver means we are pinned to a frozen tool while the ecosystem (package sets, registry, tooling) moves to the new format.

PR #41 already attempted the test-project half of this migration. It is a useful starting point but incomplete in ways that would break our build, so this issue tracks the full migration so it lands correctly rather than merging that PR as-is. See the "Relationship to PR #41" section below.

Current state

  • Dev shell pins the legacy Haskell spago (easy-ps.spago = 0.21.0) and purs-0_15_16-0, both from easy-purescript-nix (flake.nix).
  • Test project (test/ps) is configured with spago.dhall + packages.dhall, where packages.dhall is upstream-ps // upstream-lua. The right operand (Unisay/purescript-lua-package-sets) overrides core packages with Lua FFI forks. That package set ships only packages.dhall (no JSON variant).
  • Golden harness (test/Language/PureScript/Backend/Lua/Golden/Spec.hs) shells out to spago build -u '-g corefn' and then reads test/ps/output/**/corefn.json.

Target state

  • Dev shell provides the new spago (≥ 0.93). The toolchain itself already comes from purescript-overlay (see the prerequisite issue), so this is a one-attribute swap of the spago pin; purs stays at 0.15.16.
  • test/ps uses spago.yaml: a registry package-set baseline plus extraPackages git overrides for the FFI-bearing Lua forks.
  • The build reliably emits CoreFn, goldens are regenerated against current package versions, and spago.lock is committed for reproducibility.

Migration plan

1. Bump spago to the new release (flake.nix)

Prerequisite: the separate "source the toolchain from purescript-overlay" issue (#54), which moves the dev shell to the overlay while keeping legacy spago 0.21. With the overlay already providing the toolchain, this step is just a pin swap.

  • Replace the legacy spago-bin.spago-0_21_0 pin with the overlay's stable spago (new release, ≥ 0.93).
  • Keep purs at 0.15.16 (the overlay's stable purs), so only the package-set and build changes drive any golden churn.
  • Confirm spago --version ≥ 0.93 inside nix develop.

2. Test-project config (test/ps)

  • Scaffold spago.yaml with spago-legacy migrate (the only release carrying the migrate command), then delete spago.dhall and packages.dhall.
  • Set workspace.packageSet.registry: <version> as the baseline.
  • Add extraPackages git overrides for the FFI-bearing forks only. Packages that ship .lua FFI and therefore require a Lua fork: arrays, console, control, effect, enums, foldable-traversable, functions, integers, numbers, partial, prelude, refs, st, strings, unfoldable, unsafe-coerce.
  • Leave pure (no-FFI) packages to come straight from the registry: bifunctors, const, contravariant, distributive, either, exists, functors, gen, identity, invariant, maybe, newtype, nonempty, orders, profunctor, safe-coerce, tailrec, tuples, type-equality. Verify each resolves against the chosen registry version; promote any that turn out to be incompatible.
  • Pin every git override to a tag, not a branch. The new spago strongly discourages branch refs, and the lock file records the resolved commit anyway.

3. CoreFn generation (Golden/Spec.hs)

The new spago defaults to JS codegen; CoreFn is not produced unless asked for. PR #41's plain spago build would not generate corefn.json at all. The suite only appears to pass there because committed Golden*/corefn.json files are read as stale inputs, which silently defeats the point of recompiling from source.

  • Change the build command to pass CoreFn through purs, e.g. spago build --purs-args "--codegen corefn,js". Use corefn,js rather than corefn alone: incremental-build caching can leave CoreFn stale when only one codegen target is requested (spago build didn't update corefn in output directory purescript/spago#643).
  • Confirm the build actually refreshes corefn.json (touch a source file, rebuild, diff) so the harness validates freshly compiled output rather than committed copies.

4. .gitignore and the lock file

  • Commit spago.lock rather than ignoring it. For a golden-test project, pinning the exact resolved fork commits is what we want. (PR simple PR to get this project building with the new Spago #41 ignored it; reverse that decision here.)
  • Ignore the dependency output directories and .js / .js.map artifacts while keeping Golden*/corefn.json tracked.

5. Goldens and docs

  • Regenerate goldens with ./scripts/golden_reset against current package versions.
  • Update the toolchain section of CLAUDE.md (spago version, the new build command, and how the override-based package set works).

6. The Lua package-set forks ("all packages")

extraPackages git entries need each package's dependency list. There are two ways to supply it:

  • (a) Inline dependencies: per git entry in test/ps/spago.yaml (fast; this is what PR simple PR to get this project building with the new Spago #41 did; duplicates each fork's dep graph in our config and drifts over time).

  • (b) Add a spago.yaml to each Lua fork repo so consumers don't inline deps (reusable, single source of truth; ~16 FFI fork repos to touch, all under accounts we control).

  • Choose (a) or (b). Recommendation: (b) long-term since we own the forks, with (a) acceptable as an interim step to unblock the test project.

New spago features worth adopting

  • Lock file (spago.lock): pins the exact fork commits behind every git override, giving reproducible golden inputs that complement the Nix pin. Commit it.
  • Custom backend (workspace.backend.cmd): longer term, register pslua as a Spago backend so downstream users get first-class spago build / spago bundle / spago run targeting Lua instead of wiring the compiler by hand. Good for adoption, but worth a separate follow-up issue rather than folding it into this migration.
  • Registry solver (--use-solver): deliberately not recommended here. Golden stability wants exact pins, not range solving, so the package set with pinned overrides is the better fit.

Open questions

  • Which registry package-set version aligns with purs 0.15.16 and the fork versions? (We plan to keep purs at 0.15.16; the registry baseline must be compatible with it.)
  • Strategy (a) vs (b) for fork dependency lists.
  • Do the FFI forks themselves need a one-time bump to be registry-resolvable (versions/bounds), or do the existing tags work as-is via git overrides?

Relationship to PR #41

PR #41 ("migrate test project from spago.dhall to spago.yaml") is the prior art for steps 2, 4, and the inline-deps variant of step 6. What it is missing, and what this issue exists to get right:

  • It does not touch flake.nix, so the pinned legacy spago in the dev shell (and therefore CI) cannot read the new spago.yaml.
  • Its spago build does not request CoreFn codegen, so it would stop generating fresh corefn.json (masked by committed goldens).
  • Its regenerated goldens are now stale relative to main (the package set has since moved), and it drops a luacheck --no-redefined flag whose inline comment explains why it is needed.

References

Metadata

Metadata

Assignees

Labels

area: toolchainnix / spago / purs / buildchoreMaintenance / infrastructure

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions