diff --git a/.gitignore b/.gitignore index 264a4c9..cddda57 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,11 @@ !docs/branding/** !crates !crates/** +# Static assets are built by Nix (frontendStatic derivation in flake.nix). +# Do not commit them; `nix build` produces them from frontend/src/. +crates/archivr-server/static/ +crates/archivr-server/static/** + !vendor !vendor/** diff --git a/AGENTS.md b/AGENTS.md index 9caa30a..6566445 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,11 +74,13 @@ cargo build --release -p archivr-server # Frontend (Bun, from frontend/) bun install bun run dev # Vite dev server -bun run build # → ../crates/archivr-server/static (served by the server) +bun run build # → ../crates/archivr-server/static (gitignored; only needed for bare cargo run) # Nix nix develop # devshell: yt-dlp, nushell, uv, twitter-api-client -nix build .#archivr-server # also .#archivr-cli, .#archivr-all +nix build .#archivr-server # also .#archivr-cli, .#archivr-all; builds frontend automatically + # If frontendDeps hash is stale (after bun.lock/package.json change): + # nix build 2>&1 | grep "got:" → paste hash into flake.nix frontendDepsHash # Docker docker compose up -d # port 8080; config in ./config/archivr-server.toml (see docker/config.example.toml) @@ -131,7 +133,7 @@ No CI is configured; no rustfmt.toml/clippy.toml — default `cargo fmt`/`clippy - **Bun** is the frontend package manager (`frontend/bun.lock`); use `bun`, not npm/yarn. - Runtime binaries the app expects on PATH or via env vars: `yt-dlp`, Chromium, `single-file` (Node), Python 3 with `twitter-api-client`, `ffmpeg`. `nix develop` provides the dev subset. - `.gitignore` is **default-deny with an allowlist** — new top-level files/dirs are invisible to git until explicitly allowed there. -- Frontend build output (`crates/archivr-server/static/`) is generated; never hand-edit it. +- Frontend build output (`crates/archivr-server/static/`) is generated by the `frontendStatic` Nix derivation; never hand-edit it, and do not commit it — it is excluded from git tracking. `nix build` is the standard workflow everywhere (local and NixOS) and builds the frontend automatically. `bun run build` only needed for bare `cargo run` one-off testing. When `bun.lock` or `package.json` changes, update `frontendDepsHash` in `flake.nix` for each system by running `nix build 2>&1 | grep "got:"` and pasting the reported hash. - **yt-dlp is pinned to a specific GitHub release** in the `ytDlp` derivation in `flake.nix` (zipapp fetched from `github.com/yt-dlp/yt-dlp/releases`, wrapped with `python312` + `ffmpeg`) — not taken from nixpkgs. Both the `archivr` and `archivr-server` wrappers set `ARCHIVR_YT_DLP` from it. Three diff --git a/docs/README.md b/docs/README.md index ec22919..b66ef51 100644 --- a/docs/README.md +++ b/docs/README.md @@ -430,7 +430,7 @@ cargo run -p archivr-server -- ./archivr-server.toml # Frontend (from frontend/) bun install bun run dev # Vite dev server -bun run build # → crates/archivr-server/static/ +bun run build # → crates/archivr-server/static/ (gitignored; nix build does this automatically) # Nix nix develop # dev shell diff --git a/flake.nix b/flake.nix index 16384e5..2883664 100644 --- a/flake.nix +++ b/flake.nix @@ -121,6 +121,60 @@ --prefix PATH : ${lib.makeBinPath [ pkgs.python312 pkgs.ffmpeg ]} ''; }; + # Frontend: per-system hash for the node_modules FOD. + # bun installs platform-specific native binaries (esbuild, rollup), + # so the hash differs between systems. + # To compute the hash for a new system, set its entry to + # "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" and run: + # nix build .#archivr-server 2>&1 | grep "got:" + # then paste the reported hash here. + frontendDepsHash = + { + "aarch64-darwin" = "sha256-QYmiCaORbrWPVaM9xXViCZChSxwObRCjlrM03zukjQ0="; + "x86_64-linux" = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + "aarch64-linux" = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + } + .${system} or "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; + + # FOD: fetch npm deps via bun. Network is allowed; output is hashed. + frontendDeps = pkgs.stdenv.mkDerivation { + pname = "archivr-frontend-deps"; + version = "0.1.0"; + src = ./frontend; + nativeBuildInputs = [ pkgs.bun ]; + buildPhase = '' + export HOME=$TMPDIR + bun install --frozen-lockfile + ''; + installPhase = '' + cp -r node_modules $out + ''; + outputHash = frontendDepsHash; + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; + }; + + # Build the Vite bundle using the pre-fetched node_modules. + # Source files in frontend/src/ are jj-tracked and flow through automatically; + # only the deps hash (above) needs updating when bun.lock/package.json changes. + frontendStatic = pkgs.stdenv.mkDerivation { + pname = "archivr-frontend-static"; + version = "0.1.0"; + src = ./frontend; + nativeBuildInputs = [ pkgs.nodejs ]; + buildPhase = '' + export HOME=$TMPDIR + export BABEL_CACHE_PATH=$TMPDIR/babel-cache + cp -r ${frontendDeps} node_modules + chmod -R u+w node_modules + node node_modules/vite/bin/vite.js build --outDir dist + ''; + installPhase = '' + cp -r dist $out + ''; + dontFixup = true; + }; + version = "0.1.0"; src = pkgs.lib.cleanSource ./.; cargoLock = { @@ -206,7 +260,7 @@ cp ${archivr_server_unwrapped}/bin/archivr-server $out/libexec/archivr-server/archivr-server cp ${./vendor/twitter/scrape_user_tweet_contents.py} $out/libexec/archivr-server/scrape_user_tweet_contents.py chmod +x $out/libexec/archivr-server/scrape_user_tweet_contents.py - cp -r ${./crates/archivr-server/static}/* $out/share/archivr-server/static/ + cp -r ${frontendStatic}/* $out/share/archivr-server/static/ makeWrapper $out/libexec/archivr-server/archivr-server $out/bin/archivr-server \ --set ARCHIVR_STATIC_DIR $out/share/archivr-server/static \ --set ARCHIVR_YT_DLP ${ytDlp}/bin/yt-dlp \