feat: add Nix flake for reproducible dev environment (#238)

* feat: add Nix flake for reproducible dev environment

- flake.nix with devShell (Node.js 24, pnpm, oxlint, Playwright, gh)
- nix/checks.nix for hermetic lint/typecheck/test via nix flake check
- shell.nix for backward compat, .envrc for direnv integration

* fix: use corepack for exact pnpm version pinning
- corepack reads packageManager field from package.json
- --install-directory to writable .corepack/bin (Nix store is read-only)
- COREPACK_ENABLE_DOWNLOAD_PROMPT=0 for non-interactive downloads
- Remove checks.nix (sandboxed pnpm install infeasible)
- Fix stale check to use .modules.yaml
- Remove Playwright from Nix (let pnpm manage)
- Pin shell.nix to flake-compat rev from flake.lock

* chore: address review feedback on Nix dev shell

- Remove unused lib and stdenv function arguments
- Replace no-op PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD env var with comments
  documenting why we don't use Nix-provided browsers and the NixOS
  system library limitation
- Make pnpm install non-fatal in shellHook so the shell still opens
  cleanly if the lockfile is stale
- Improve staleness check readability (use -f guard instead of
  2>/dev/null redirect)

---------

Co-authored-by: Steve Faulkner <sfaulkner@cloudflare.com>
This commit is contained in:
Kidus Abebe
2026-03-05 02:42:42 +03:00
committed by GitHub
parent 4ed5c89346
commit d3b80ca247
6 changed files with 193 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
use flake
+6
View File
@@ -27,3 +27,9 @@ coverage/
.DS_Store
.idea/
.vscode/
# Nix
result
result-*
.direnv/
.corepack/
Generated
+78
View File
@@ -0,0 +1,78 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1772479524,
"narHash": "sha256-u7nCaNiMjqvKpE+uZz9hE7pgXXTmm5yvdtFaqzSzUQI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "4215e62dc2cd3bc705b0a423b9719ff6be378a43",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+32
View File
@@ -0,0 +1,32 @@
{
description = "vinext Vite plugin reimplementing the Next.js API surface";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
# Backward compatibility for non-flake users (shell.nix)
flake-compat = {
url = "github:edolstra/flake-compat";
flake = false;
};
};
outputs = {
self,
nixpkgs,
flake-utils,
...
}:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = nixpkgs.legacyPackages.${system};
in {
devShells.default = pkgs.callPackage ./nix/devShell.nix {};
formatter = pkgs.nixfmt;
}
);
# To update pinned dependencies: nix flake update
}
+68
View File
@@ -0,0 +1,68 @@
{
mkShell,
nodejs_24,
oxlint,
gh,
jq,
nixfmt,
}:
mkShell {
name = "vinext";
packages =
[
# Runtime — Node.js 24 ships with corepack, which reads the
# packageManager field from package.json to install the exact
# pnpm version the project declares (e.g. pnpm@10.30.0).
nodejs_24
# Linting (matches pnpm run lint)
oxlint
# Nix formatting
nixfmt
# Utilities
gh # GitHub CLI — used in AGENTS.md workflow (gh search code)
jq
];
env = {
# Allow corepack to download the pnpm version specified in packageManager
# without an interactive confirmation prompt (which hangs in non-TTY shells).
COREPACK_ENABLE_DOWNLOAD_PROMPT = "0";
# Playwright browser downloads are left at their default (enabled).
# We intentionally do NOT use Nix-provided playwright-driver.browsers
# because its version would couple to nixpkgs and likely mismatch the
# @playwright/test version in package.json, causing runtime failures.
#
# Note: on NixOS or in --pure Nix shells, Playwright also needs system
# libraries (GTK3, ALSA, libdrm, etc.) that are not provided here.
# E2E tests work on standard Linux/macOS where these libraries are
# available from the host. For NixOS, you may need to wrap the Playwright
# binary with the required library paths or use a FHS environment.
};
shellHook = ''
# Corepack is bundled with Node.js but needs a writable directory for
# its shims since the Nix store is read-only. We create a local bin
# directory and prepend it to PATH.
COREPACK_INSTALL_DIR="$PWD/.corepack/bin"
mkdir -p "$COREPACK_INSTALL_DIR"
export PATH="$COREPACK_INSTALL_DIR:$PATH"
corepack enable --install-directory "$COREPACK_INSTALL_DIR" 2>/dev/null
echo "🚀 vinext dev shell"
echo " Node.js $(node --version)"
echo " pnpm $(pnpm --version 2>/dev/null || echo '(downloading...)')"
echo ""
# Install dependencies if node_modules is missing or lockfile has changed.
# pnpm uses .modules.yaml (not npm's .package-lock.json).
if [ ! -d node_modules ] || { [ -f pnpm-lock.yaml ] && [ pnpm-lock.yaml -nt node_modules/.modules.yaml ]; }; then
echo "📦 Running pnpm install..."
pnpm install --frozen-lockfile || echo " pnpm install failed. Run it manually to see the full error."
fi
'';
}
+8
View File
@@ -0,0 +1,8 @@
# Backward compatibility for users without flake support.
# Prefer `nix develop` if your Nix installation supports flakes.
#
# Pinned to the same flake-compat rev as in flake.lock for reproducibility.
(import (fetchTarball "https://github.com/edolstra/flake-compat/archive/5edf11c44bc78a0d334f6334cdaf7d60d732daab.tar.gz") {
src = ./.;
})
.shellNix