|
| 1 | +## Context |
| 2 | + |
| 3 | +OpenSpec is a TypeScript CLI tool using pnpm for dependency management. The project requires Node.js ≥20.19.0. Nix uses its own build system that needs to understand how to fetch dependencies and build the project reproducibly. |
| 4 | + |
| 5 | +The Nix ecosystem has specific patterns for packaging Node.js/pnpm projects that differ from the traditional npm ecosystem. |
| 6 | + |
| 7 | +## Goals |
| 8 | + |
| 9 | +- Enable OpenSpec to be run directly via `nix run github:Fission-AI/OpenSpec` |
| 10 | +- Support all major platforms (Linux x86/ARM, macOS x86/ARM) |
| 11 | +- Use existing pnpm-lock.yaml for reproducible builds |
| 12 | +- Provide development environment for Nix users |
| 13 | + |
| 14 | +## Non-Goals |
| 15 | + |
| 16 | +- Replace existing npm/pnpm publishing workflow |
| 17 | +- Publish to nixpkgs (can be done later as separate effort) |
| 18 | +- Support Windows (Nix doesn't run natively on Windows) |
| 19 | + |
| 20 | +## Decisions |
| 21 | + |
| 22 | +### Use stdenv.mkDerivation instead of buildNpmPackage |
| 23 | + |
| 24 | +**Decision**: Package OpenSpec using `stdenv.mkDerivation` with pnpm hooks. |
| 25 | + |
| 26 | +**Rationale**: The zigbee2mqtt package in nixpkgs demonstrates the current best practice for pnpm projects. Using `buildNpmPackage` with pnpm requires complex configuration, while `mkDerivation` with the right hooks is more straightforward and better supported. |
| 27 | + |
| 28 | +**Alternative considered**: Using `buildNpmPackage` with `npmConfigHook = pkgs.pnpmConfigHook` - this is the older pattern and causes issues with dependency fetching. |
| 29 | + |
| 30 | +### Use fetchPnpmDeps with explicit pnpm version |
| 31 | + |
| 32 | +**Decision**: Use `pkgs.fetchPnpmDeps` with `pnpm = pkgs.pnpm_9` and `fetcherVersion = 3`. |
| 33 | + |
| 34 | +**Rationale**: |
| 35 | +- pnpm lockfile version 9.0 requires fetcherVersion 3 |
| 36 | +- Explicit pnpm_9 ensures consistency between fetch and build |
| 37 | +- This is the documented way to handle pnpm projects in nixpkgs |
| 38 | + |
| 39 | +### Multi-platform support without flake-utils |
| 40 | + |
| 41 | +**Decision**: Implement multi-platform support using plain Nix with `nixpkgs.lib.genAttrs`. |
| 42 | + |
| 43 | +**Rationale**: Per user request, avoid extra dependencies. The `genAttrs` pattern is simple and well-understood in the Nix community. |
| 44 | + |
| 45 | +### Node.js 20 instead of latest |
| 46 | + |
| 47 | +**Decision**: Pin to nodejs_20 to match package.json engines requirement. |
| 48 | + |
| 49 | +**Rationale**: Ensures consistency with development environment and npm package requirements. Avoids potential compatibility issues with newer Node versions. |
| 50 | + |
| 51 | +## Key Implementation Details |
| 52 | + |
| 53 | +### Dependency Hash Management |
| 54 | + |
| 55 | +The `pnpmDeps.hash` field must be updated whenever dependencies change. The workflow: |
| 56 | +1. Set hash to fake value (all zeros) |
| 57 | +2. Run `nix build` |
| 58 | +3. Nix fails with actual hash |
| 59 | +4. Update flake.nix with correct hash |
| 60 | + |
| 61 | +This is standard Nix workflow for fixed-output derivations. |
| 62 | + |
| 63 | +### Build Inputs |
| 64 | + |
| 65 | +Required nativeBuildInputs: |
| 66 | +- `nodejs_20` - runtime |
| 67 | +- `npmHooks.npmInstallHook` - handles installation phase |
| 68 | +- `pnpmConfigHook` - configures pnpm environment |
| 69 | +- `pnpm_9` - pnpm executable |
| 70 | + |
| 71 | +The `dontNpmPrune = true` is important to keep all dependencies after build. |
| 72 | + |
| 73 | +## Risks / Trade-offs |
| 74 | + |
| 75 | +**[Risk]** Hash needs updating when dependencies change → **Mitigation**: Document this clearly; error message from Nix provides correct hash |
| 76 | + |
| 77 | +**[Risk]** Nix builds might lag behind npm releases → **Mitigation**: This is fine; Nix users can still use npm if they need bleeding edge |
| 78 | + |
| 79 | +**[Trade-off]** Additional maintenance burden for hash updates → **Benefit**: Better experience for Nix ecosystem users |
| 80 | + |
| 81 | +## Migration Plan |
| 82 | + |
| 83 | +1. Add flake.nix to repository |
| 84 | +2. Test builds on multiple platforms (can use GitHub Actions with Nix) |
| 85 | +3. Update README with Nix installation instructions |
| 86 | +4. Optionally add to CI pipeline to catch hash mismatches early |
| 87 | + |
| 88 | +No breaking changes - this is purely additive. |
| 89 | + |
| 90 | +## Open Questions |
| 91 | + |
| 92 | +- Should we add automatic hash updating to CI? (Could use nix-update-script) |
| 93 | +- Should we submit to nixpkgs after validation? (Separate decision) |
| 94 | +- Do we want to support older Node versions in flake? (Probably no - stick to package.json requirement) |
0 commit comments