You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
(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?
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.
Motivation
Legacy
spago0.21.x is the last release from the Haskell codebase, and it is the only release that still understandsspago.dhall/packages.dhall. All ongoing Spago development happens in the PureScript rewrite (≥ 0.93), which dropped Dhall entirely in favour ofspago.yaml, the PureScript Registry, and aspago.locklock 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
easy-ps.spago= 0.21.0) andpurs-0_15_16-0, both from easy-purescript-nix (flake.nix).test/ps) is configured withspago.dhall+packages.dhall, wherepackages.dhallisupstream-ps // upstream-lua. The right operand (Unisay/purescript-lua-package-sets) overrides core packages with Lua FFI forks. That package set ships onlypackages.dhall(no JSON variant).test/Language/PureScript/Backend/Lua/Golden/Spec.hs) shells out tospago build -u '-g corefn'and then readstest/ps/output/**/corefn.json.Target state
purescript-overlay(see the prerequisite issue), so this is a one-attribute swap of the spago pin;pursstays at 0.15.16.test/psusesspago.yaml: aregistrypackage-set baseline plusextraPackagesgit overrides for the FFI-bearing Lua forks.spago.lockis 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.
spago-bin.spago-0_21_0pin with the overlay's stablespago(new release, ≥ 0.93).pursat 0.15.16 (the overlay's stablepurs), so only the package-set and build changes drive any golden churn.spago --version≥ 0.93 insidenix develop.2. Test-project config (
test/ps)spago.yamlwithspago-legacy migrate(the only release carrying themigratecommand), then deletespago.dhallandpackages.dhall.workspace.packageSet.registry: <version>as the baseline.extraPackagesgit overrides for the FFI-bearing forks only. Packages that ship.luaFFI and therefore require a Lua fork:arrays,console,control,effect,enums,foldable-traversable,functions,integers,numbers,partial,prelude,refs,st,strings,unfoldable,unsafe-coerce.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.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 buildwould not generatecorefn.jsonat all. The suite only appears to pass there because committedGolden*/corefn.jsonfiles are read as stale inputs, which silently defeats the point of recompiling from source.spago build --purs-args "--codegen corefn,js". Usecorefn,jsrather thancorefnalone: 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).corefn.json(touch a source file, rebuild, diff) so the harness validates freshly compiled output rather than committed copies.4.
.gitignoreand the lock filespago.lockrather 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.).js/.js.mapartifacts while keepingGolden*/corefn.jsontracked.5. Goldens and docs
./scripts/golden_resetagainst current package versions.CLAUDE.md(spago version, the new build command, and how the override-based package set works).6. The Lua package-set forks ("all packages")
extraPackagesgit entries need each package's dependency list. There are two ways to supply it:(a) Inline
dependencies:per git entry intest/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.yamlto 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
spago.lock): pins the exact fork commits behind every git override, giving reproducible golden inputs that complement the Nix pin. Commit it.workspace.backend.cmd): longer term, registerpsluaas a Spago backend so downstream users get first-classspago build/spago bundle/spago runtargeting 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.--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
purs0.15.16 and the fork versions? (We plan to keeppursat 0.15.16; the registry baseline must be compatible with it.)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:
flake.nix, so the pinned legacy spago in the dev shell (and therefore CI) cannot read the newspago.yaml.spago builddoes not request CoreFn codegen, so it would stop generating freshcorefn.json(masked by committed goldens).main(the package set has since moved), and it drops aluacheck --no-redefinedflag whose inline comment explains why it is needed.References