nandi/frqpublic Fork 0
b3b3cf6
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Four builds, four hashes, and the one file that was doing it

cljd-deps was not reproducible, and the reason turned out to be pub's own
bookkeeping. It records which project directories use the cache under
active_roots/, sharded by a hash of the path — and $NIX_BUILD_TOP is a new
path on every build, so every run left a differently-named note to itself.
The package sources under hosted/ were byte-identical the whole time.

The cleanup below already sweeps everything a tool writes about a run
rather than about a dependency. active_roots was simply missing from the
list, presumably added by a newer pub than the one this was written for.

Two runs in one container now agree, so the recorded hash is a value that
can hold rather than one machine's souvenir. The old one predated the
2026-09-12 ClojureDart bump, which is why gitlibs and m2 looked wrong too
— they were not: they were stable across every build and only this
machine's store copy was old.

The stub gets pubspec.lock now. That fixed nothing that was broken —
resolution was already agreeing — but a lock is the difference between
agreeing today and agreeing after pub.dev moves.

And the diagnostic prints the path rather than the basename, which is the
change that made any of this findable. `cljd-deps pub 09` names nothing;
`cljd-deps pub pub-cache/active_roots/09` names the bug.

`#flutter-appimage` rides along: the Flutter desktop GUI squashed into one
file the way `#appimage` does libcosmic's, since a store path cannot come
home without its closure. It wraps flutter-desktop rather than the
unwrapped binary, so the bundle carries the Mesa nixGL puts the host
driver in front of.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-09-13T22:20:55-07:00 Browse files
b3b3cf6 parent: 972ab37
modified flake.nix +31 -4
@@ -465,6 +465,18 @@
465465 appimage =
466466 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
467467
468+ # The other desktop GUI, squashed the same way. `flutter-desktop` is
469+ # already the nixGL-wrapped launcher rather than the raw Flutter
470+ # bundle, so this carries the same store Mesa for the same reason —
471+ # and it is the whole point here, since a host with Flutter's
472+ # runtime deps but no Nix is exactly who wants one file.
473+ #
474+ # Named by its backend, the way the outputs it wraps are: `appimage`
475+ # is libcosmic's and this is Flutter's, and neither is the default.
476+ flutter-appimage =
477+ nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default
478+ self.packages.${pkgs.stdenv.hostPlatform.system}.flutter-desktop;
479+
468480 # Everything `clojure -M:cljd compile` would otherwise reach the
469481 # network for, fetched once and hashed.
470482 #
@@ -530,7 +542,12 @@
530542 cp ${./common/deps.edn} "$NIX_BUILD_TOP/common/deps.edn"
531543 cp ${./flutter/deps.edn} "$proj/deps.edn"
532544 cp ${./flutter/pubspec.yaml} "$proj/pubspec.yaml"
533- chmod u+w "$proj/deps.edn" "$proj/pubspec.yaml"
545+ # The lock, or `pub get` resolves against pub.dev and takes
546+ # whatever satisfies the ranges today. Every build then fetches
547+ # a slightly different set and the fixed-output hash is a
548+ # promise nothing can keep.
549+ cp ${./flutter/pubspec.lock} "$proj/pubspec.lock"
550+ chmod u+w "$proj/deps.edn" "$proj/pubspec.yaml" "$proj/pubspec.lock"
534551 cat > "$proj/src/stub/main.cljd" <<'EOF'
535552 (ns stub.main)
536553 (defn main [] nil)
@@ -573,9 +590,16 @@
573590 # rewrites its resolution metadata on every resolve, and
574591 # tools.gitlibs keeps bare clones it only needs in order to
575592 # make a checkout. None of it is read offline.
593+ #
594+ # active_roots is the one that was actually breaking this. Pub
595+ # records the project directories using the cache, sharded by
596+ # a hash of the path, and $NIX_BUILD_TOP is different on every
597+ # run -- so two builds whose hosted/ trees were byte-identical
598+ # still disagreed, purely over which directory had asked. Four
599+ # builds gave four hashes until this went.
576600 rm -rf "$out/pub-cache/log" "$out/pub-cache/_temp" \
577601 "$out/pub-cache/git" "$out/pub-cache/global_packages" \
578- "$out/pub-cache/bin"
602+ "$out/pub-cache/bin" "$out/pub-cache/active_roots"
579603
580604 # tools.gitlibs keeps a bare clone per URL under _repos/, and a
581605 # bare clone is packfiles — which two runs of the same fetch do
@@ -630,7 +654,10 @@
630654 done
631655 for d in "$out"/pub-cache/*/*; do
632656 [ -d "$d" ] || continue
633- echo "cljd-deps pub $(basename "$d") $( (cd "$d" && find . -type f \
657+ # The path relative to $out, not the basename: two runs that
658+ # disagree here disagree about *which* directory exists, and
659+ # a bare `09` names nothing you can go and look at.
660+ echo "cljd-deps pub ''${d#$out/} $( (cd "$d" && find . -type f \
634661 -exec sha256sum {} + | sort -k2 | sha256sum) )" >&2
635662 done
636663 '';
@@ -639,7 +666,7 @@
639666 outputHashAlgo = "sha256";
640667 # Moves when flutter/deps.edn or flutter/pubspec.yaml move, and
641668 # not when frq's own source does see the stub above.
642- outputHash = "sha256-gfJGlKCPaJsKcXfCWOJY1089XEfzTndEx0LVf3JOXfs=";
669+ outputHash = "sha256-qSGx7WFdVyV7yu4R+EjiQHZcLqwDjYSlohiZcXB43DY=";
643670 };
644671
645672 # The Flutter desktop GUI, built rather than run out of the tree.
@@ -465,6 +465,18 @@
465 appimage =465 appimage =
466 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;466 nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default frq;
467 467
468+ # The other desktop GUI, squashed the same way. `flutter-desktop` is
469+ # already the nixGL-wrapped launcher rather than the raw Flutter
470+ # bundle, so this carries the same store Mesa for the same reason —
471+ # and it is the whole point here, since a host with Flutter's
472+ # runtime deps but no Nix is exactly who wants one file.
473+ #
474+ # Named by its backend, the way the outputs it wraps are: `appimage`
475+ # is libcosmic's and this is Flutter's, and neither is the default.
476+ flutter-appimage =
477+ nix-appimage.bundlers.${pkgs.stdenv.hostPlatform.system}.default
478+ self.packages.${pkgs.stdenv.hostPlatform.system}.flutter-desktop;
479+
468 # Everything `clojure -M:cljd compile` would otherwise reach the480 # Everything `clojure -M:cljd compile` would otherwise reach the
469 # network for, fetched once and hashed.481 # network for, fetched once and hashed.
470 #482 #
@@ -530,7 +542,12 @@
530 cp ${./common/deps.edn} "$NIX_BUILD_TOP/common/deps.edn"542 cp ${./common/deps.edn} "$NIX_BUILD_TOP/common/deps.edn"
531 cp ${./flutter/deps.edn} "$proj/deps.edn"543 cp ${./flutter/deps.edn} "$proj/deps.edn"
532 cp ${./flutter/pubspec.yaml} "$proj/pubspec.yaml"544 cp ${./flutter/pubspec.yaml} "$proj/pubspec.yaml"
533- chmod u+w "$proj/deps.edn" "$proj/pubspec.yaml"545+ # The lock, or `pub get` resolves against pub.dev and takes
546+ # whatever satisfies the ranges today. Every build then fetches
547+ # a slightly different set and the fixed-output hash is a
548+ # promise nothing can keep.
549+ cp ${./flutter/pubspec.lock} "$proj/pubspec.lock"
550+ chmod u+w "$proj/deps.edn" "$proj/pubspec.yaml" "$proj/pubspec.lock"
534 cat > "$proj/src/stub/main.cljd" <<'EOF'551 cat > "$proj/src/stub/main.cljd" <<'EOF'
535 (ns stub.main)552 (ns stub.main)
536 (defn main [] nil)553 (defn main [] nil)
@@ -573,9 +590,16 @@
573 # rewrites its resolution metadata on every resolve, and590 # rewrites its resolution metadata on every resolve, and
574 # tools.gitlibs keeps bare clones it only needs in order to591 # tools.gitlibs keeps bare clones it only needs in order to
575 # make a checkout. None of it is read offline.592 # make a checkout. None of it is read offline.
593+ #
594+ # active_roots is the one that was actually breaking this. Pub
595+ # records the project directories using the cache, sharded by
596+ # a hash of the path, and $NIX_BUILD_TOP is different on every
597+ # run -- so two builds whose hosted/ trees were byte-identical
598+ # still disagreed, purely over which directory had asked. Four
599+ # builds gave four hashes until this went.
576 rm -rf "$out/pub-cache/log" "$out/pub-cache/_temp" \600 rm -rf "$out/pub-cache/log" "$out/pub-cache/_temp" \
577 "$out/pub-cache/git" "$out/pub-cache/global_packages" \601 "$out/pub-cache/git" "$out/pub-cache/global_packages" \
578- "$out/pub-cache/bin"602+ "$out/pub-cache/bin" "$out/pub-cache/active_roots"
579 603
580 # tools.gitlibs keeps a bare clone per URL under _repos/, and a604 # tools.gitlibs keeps a bare clone per URL under _repos/, and a
581 # bare clone is packfiles — which two runs of the same fetch do605 # bare clone is packfiles — which two runs of the same fetch do
@@ -630,7 +654,10 @@
630 done654 done
631 for d in "$out"/pub-cache/*/*; do655 for d in "$out"/pub-cache/*/*; do
632 [ -d "$d" ] || continue656 [ -d "$d" ] || continue
633- echo "cljd-deps pub $(basename "$d") $( (cd "$d" && find . -type f \657+ # The path relative to $out, not the basename: two runs that
658+ # disagree here disagree about *which* directory exists, and
659+ # a bare `09` names nothing you can go and look at.
660+ echo "cljd-deps pub ''${d#$out/} $( (cd "$d" && find . -type f \
634 -exec sha256sum {} + | sort -k2 | sha256sum) )" >&2661 -exec sha256sum {} + | sort -k2 | sha256sum) )" >&2
635 done662 done
636 '';663 '';
@@ -639,7 +666,7 @@
639 outputHashAlgo = "sha256";666 outputHashAlgo = "sha256";
640 # Moves when flutter/deps.edn or flutter/pubspec.yaml move, and667 # Moves when flutter/deps.edn or flutter/pubspec.yaml move, and
641 # not when frq's own source does see the stub above.668 # not when frq's own source does see the stub above.
642- outputHash = "sha256-gfJGlKCPaJsKcXfCWOJY1089XEfzTndEx0LVf3JOXfs=";669+ outputHash = "sha256-qSGx7WFdVyV7yu4R+EjiQHZcLqwDjYSlohiZcXB43DY=";
643 };670 };
644 671
645 # The Flutter desktop GUI, built rather than run out of the tree.672 # The Flutter desktop GUI, built rather than run out of the tree.