Skip to content

Add Nix flake support with source build, prebuilt, and desktop outputs - #5

Open
levonk wants to merge 16 commits into
acryldev:mainfrom
levonk:feature/nix-flake-support
Open

Add Nix flake support with source build, prebuilt, and desktop outputs#5
levonk wants to merge 16 commits into
acryldev:mainfrom
levonk:feature/nix-flake-support

Conversation

@levonk

@levonk levonk commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add flake.nix with #acryl (TUI from source, also #default), #prebuilt (prebuilt CLI release tarball), #acryl-desktop (Electron from source), and devShells.default
  • Add devbox.json + devbox.lock for reproducible development environments
  • Add .github/workflows/nix.yml with SHA-pinned actions, path filtering, and PR-guarded nix run steps
  • Add Nix and Devbox install sections to README.md, README.en.md, and README.zh.md
  • Support all 4 Nix target systems (x86_64-linux, aarch64-linux, aarch64-darwin, x86_64-darwin)

Closes #4.

Flake outputs

Output Type Description
#default / #acryl source TUI built from source using pnpmConfigHook + fetchPnpmDeps
#prebuilt prebuilt CLI release tarball (v0.1.19) with bundled Node runtime + native addons
#acryl-desktop source Electron desktop app built from source using nixpkgs electron
devShells.default dev Node 22, PNPM 11, esbuild

Platform support

  • x86_64-linux, aarch64-linux, aarch64-darwin: nixpkgs-unstable
  • x86_64-darwin: nixpkgs-26.05-darwin (legacy pin, since nixpkgs 26.11 dropped Intel macOS)

Devbox x86_64-darwin workaround

devbox 0.18 ignores nixpkgs.commit for regular package-name resolution and hardcodes nixpkgs 26.11 (which dropped x86_64-darwin). The workaround uses per-package platform scoping: clean package names for normal platforms (Linux, aarch64-darwin), flake URL references to nixpkgs-26.05-darwin only for x86_64-darwin. Normal platforms are unaffected.

CI

  • SHA-pinned actions: checkout@fbc6f399 (v5), nix-installer-action@ef8a1480 (v22), magic-nix-cache-action@908b263f (v14)
  • Path-filtered to flake.nix, flake.lock, **/*.nix, pnpm-lock.yaml, package.json
  • nix run steps guarded with if: github.event_name != 'pull_request'
  • Matrix: all 4 platforms
  • Tests: nix flake check --no-build, nix build for each output, nix run -- --help for each output

Test plan

  • nix flake check --no-build passes on all 4 systems
  • nix build .#acryl succeeds on all 4 systems
  • nix build .#prebuilt succeeds on all 4 systems
  • nix build .#acryl-desktop succeeds on all 4 systems
  • nix run .#default -- --help works
  • nix run .#prebuilt -- --help works
  • nix run .#acryl-desktop -- --help works
  • nix develop enters a shell with Node 22 + PNPM 11
  • devbox shell enters a shell on x86_64-darwin
  • Bilingual docs check passes (corepack pnpm check:bilingual-docs)

@musichen

musichen commented Sep 2, 2026

Copy link
Copy Markdown
Member

Hi @levonk thanks for contributing!
i'll review it once CI checks are green.

Add Nix flake support targeting the acryl-tui terminal client. The
flake uses nixpkgs' modern PNPM hooks (fetchPnpmDeps, pnpmConfigHook)
with pnpm_11 and fetcherVersion 4.

Key design decisions:
- Targets acryl-tui (not the Electron desktop app) as the default
  package, exposing the `acryl` binary via `nix run .#acryl`
- Uses nixpkgs-26.05-darwin legacy pin for x86_64-darwin (Intel
  macOS), since nixpkgs-unstable dropped support after 26.05
- Forces nodeLinker: hoisted in pnpm-workspace.yaml during the build
  to flatten node_modules (pnpm 11 moved this setting from .npmrc)
- Builds only the TUI dependency chain (acryl-control ->
  acryl-harness-runtime -> acryl-tui) instead of the full workspace
- Sets dontStrip and dontFixup to avoid slow strip/fixup phases on
  thousands of JS files in node_modules
- ESBUILD_BINARY_PATH points to nixpkgs esbuild to avoid the
  postinstall binary download (skipped by --ignore-scripts)

Also adds:
- devbox.json for reproducible development environment
- .github/workflows/nix.yml for CI across all 4 supported systems
- /result and /result-* to .gitignore
Document the implementation commit d6d2e46
which added Nix flake support for building acryl-tui.
Add `packages.${system}.acryl-desktop` to the flake, building the
Electron desktop app alongside the existing TUI output.

Key decisions:
- Uses nixpkgs electron (43.1.0) as the runtime instead of the npm
  electron package (which downloads a platform binary via postinstall,
  blocked by --ignore-scripts in the Nix sandbox)
- Creates a CJS shim at node_modules/electron/index.js that exports
  the nixpkgs electron path, replacing the real npm package
- Skips the generate-* build scripts (they use sharp for image
  processing) since build/ assets are already tracked in git
- Builds the full dependency chain: acryl-control ->
  acryl-harness-runtime -> dsh-community-market ->
  acryl-development-canvas -> acryl-desktop
- Refactors shared derivation attrs (pnpmDeps, preConfigure, etc.)
  into commonDerivationAttrs to avoid duplication between TUI and
  desktop derivations

Both outputs verified:
  nix run .#acryl -- --help
  nix run .#acryl-desktop -- --help
Document implementation commit 397f91034cb6a6444c6dccf6f33d06e8b10bf43b
which added the Electron desktop app as a separate Nix package output.
- SHA-pin all GitHub Actions to 40-char commit SHAs (checkout@v5,
  nix-installer-action@v22, magic-nix-cache-action@v14) instead of
  mutable @v4/@main refs — prevents supply-chain attacks
- Add if: github.event_name != 'pull_request' guard on nix run steps
  to prevent PR-controlled code from reaching GITHUB_TOKEN/OIDC
- Add path filtering to nix.yml (flake.nix, flake.lock, **/*.nix,
  pnpm-lock.yaml, etc.) so CI only fires when Nix files change
- Add nix run .#default -- --help test to CI
- Add act to devbox.json packages (required for local CI validation)
- Remove invalid nixpkgs.commit field from devbox.json (devbox 0.18
  ignores it; was set to channel name not 40-char hash)
- Add .devbox/ to .gitignore (devbox generated artifacts)
- Add Nix (Flake) and Devbox install sections to README.md,
  README.en.md, and README.zh.md
- Update bilingual-docs hash record in README.i18n.yaml

devbox.lock cannot be generated on x86_64-darwin due to devbox 0.18
hardcoding a nixpkgs commit that dropped x86_64-darwin support;
generate on aarch64-darwin or Linux.
devbox 0.18 ignores the nixpkgs.commit field for regular package-name
resolution and hardcodes nixpkgs 26.11, which dropped x86_64-darwin.
Work around this by referencing every package as a flake URL pointing
at the nixpkgs-26.05-darwin commit (f6107e5) — flake-based references
bypass devbox's package index, and nixpkgs.commit controls the shell
infrastructure (mkShell). This works on all platforms: x86_64-darwin,
aarch64-darwin, and Linux.

Generate and commit devbox.lock for reproducible environments.
Use per-package platform scoping: clean package names (nodejs_22,
pnpm_11, esbuild, act) for normal platforms (Linux, aarch64-darwin),
and flake URL references to nixpkgs-26.05-darwin only for x86_64-darwin.
The nixpkgs.commit field is set to the 26.05-darwin pin for the shell
infrastructure (mkShell), which devbox 0.18 honors when all active
packages on a platform are flake-based.

On normal platforms, packages resolve from nixpkgs-unstable via
devbox's index — the same behavior as before. On x86_64-darwin, the
flake URL references bypass devbox's hardcoded nixpkgs 26.11 (which
dropped x86_64-darwin) and pull from 26.05-darwin instead.

The lock file records both resolution paths. Linux/aarch64-darwin
entries will be populated when a user on that platform runs
devbox install.
Replace all github:levonk/acryl references with github:acryldev/acryl
in flake.nix (homepage meta), README.md, README.en.md, README.zh.md,
and DEVELOPMENT-LOG.md commit links. Update bilingual-docs hash record.
Add packages.<system>.prebuilt — fetches the prebuilt CLI tarball from
GitHub releases (v0.1.19). Each tarball bundles its own Node runtime
and native addons (node-pty, koffi, sharp), so no from-source build is
needed for the prebuilt path. Uses autoPatchelfHook on Linux for glibc
linking.

The default output remains #acryl (from-source build), following Nix
convention. #prebuilt is an optional fast path for users who want the
exact release binary.

Add CI steps to build and test #prebuilt on all 4 platforms.
Update READMEs to document the #prebuilt output.
…D hash

Four fixes for the CI failures on PR acryldev#5:

1. Rebase onto upstream/main (was 11 commits behind — caused the
   "Typecheck, test, and build" failure on a test already fixed on main)

2. Add use-flakehub: false to magic-nix-cache-action (the action defaults
   to use-flakehub: true, which attempts FlakeHub OIDC auth and breaks CI
   for orgs without a FlakeHub account — root cause of the "Unable to
   authenticate to FlakeHub" error)

3. Add timeout-minutes: 20 to the build job (was missing — GitHub's
   default max is 6h, caused the aarch64-darwin job to hang)

4. Update fetchPnpmDeps hash (stale after rebase picked up new upstream
   pnpm-lock.yaml changes)
@levonk
levonk force-pushed the feature/nix-flake-support branch from aa04fae to cfa8b92 Compare September 2, 2026 20:30
The preConfigure hook checked `if ! grep -q "nodeLinker"` and only
inserted `nodeLinker: hoisted` when the key was absent. But
pnpm-workspace.yaml already had `nodeLinker: isolated`, so the sed
never ran. The isolated linker creates a .pnpm/ virtual store with
symlinks that break when copied to the Nix store, leaving
node_modules/ with only 3 entries (the workspace packages) and no
registry dependencies like @deepseek-ai/dsh-llm.

Replace the value when the key exists, instead of only inserting when
missing. After this fix, node_modules/ has 648 packages and all four
outputs (default, acryl, prebuilt, acryl-desktop) pass smoke tests.
@levonk
levonk marked this pull request as draft September 2, 2026 23:08
The prebuilt release tarball bundles both glibc-linked and musl-linked
native koffi addons (musl_x64/koffi.node alongside linux_x64/koffi.node).
autoPatchelfHook was only finding glibc (stdenv.cc.cc.lib), so it failed
with "could not satisfy dependency libc.musl-x86_64.so.1" on Linux.

Add pkgs.musl to buildInputs so autoPatchelf can patch both variants.
The magic-nix-cache v14 static binary for arm64-darwin fails on the
macos-14 runner with:
  dyld: Symbol not found: __ZNSt13exception_ptr31__from_native_exception_pointerEPv
  Expected in: /usr/lib/libc++.1.dylib

This is a DeterminateSystems binary incompatibility — the binary was
built against a newer libc++ than the runner ships. The build itself
never starts; the job hangs for 20 minutes then gets cancelled.

Make the cache action Linux-only. Darwin builds work without it, just
slower (no cache acceleration). The flake and builds are unaffected.
@levonk
levonk marked this pull request as ready for review September 3, 2026 03:02
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.

Add Nix flake support for reproducible installs and development

2 participants