From 007dcd6b29108cde40e36d07755e2b7c707a4b15 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 14:48:44 -0300 Subject: [PATCH 1/8] bloop: fix stale fish completions Upstream replaced the fish-completions asset of every release at some point, but the hash here has not changed since 1.5.11. A fetchurl whose hash still matches resolves out of the binary cache no matter what the URL now serves, so nothing ever failed and users kept getting the 2023 completions. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index 0fb6d58c68c8..a0ee19a19348 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -30,7 +30,7 @@ stdenv.mkDerivation rec { bloop-fish = fetchurl { url = "https://github.com/scalacenter/bloop/releases/download/v${version}/fish-completions"; - sha256 = "sha256-eFESR6iPHRDViGv+Fk3sCvPgVAhk2L1gCG4LnfXO/v4="; + sha256 = "sha256-RF6nZxbw4sLwCP4irKJLYCZnlkaRdEkmpYkOHBoSF/8="; }; bloop-zsh = fetchurl { From 08651af96e2468c76b790ea284f97f33a17bf2b2 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 14:48:54 -0300 Subject: [PATCH 2/8] bloop: drop dead x86_64-darwin asset meta.platforms has never listed x86_64-darwin, so the branch selecting this asset was unreachable and its hash was never checked against anything. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index a0ee19a19348..d8ba56381fbd 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -16,8 +16,6 @@ stdenv.mkDerivation rec { platform = if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64 then "x86_64-pc-linux" - else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64 then - "x86_64-apple-darwin" else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64 then "aarch64-apple-darwin" else @@ -43,8 +41,6 @@ stdenv.mkDerivation rec { sha256 = if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64 then "sha256-F5wRihAwf/TNBSYortTCoK9qKqTI+1N5InJ+rqLFp8A=" - else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64 then - "sha256-wQXAldzU6Typ6pZB8k3dfX7g+aaVF7jXvd0pnuk5gZU=" else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64 then "sha256-OrONKbC2l0jjfmguDmoiyEaJWdTrKBiP0ZEa5rhizDM=" else From 4524d6c79d525c05305d89971bd15d4c0f32e3c3 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 14:49:25 -0300 Subject: [PATCH 3/8] bloop: move sources into sources.json The version and the per-platform asset names and hashes become data, replacing the two if/else chains that selected them. A following commit adds an update script, which can then rewrite JSON instead of Nix. meta.platforms is derived from the asset list, so the two cannot drift, and meta.changelog can be built from the same data. The fetchurl calls move from derivation attributes into let bindings; they were only ever used through string interpolation in installPhase, so being attributes just put their store paths in the build environment. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 63 ++++++++++++------------------ pkgs/by-name/bl/bloop/sources.json | 28 +++++++++++++ 2 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 pkgs/by-name/bl/bloop/sources.json diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index d8ba56381fbd..17d401330b11 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -9,43 +9,34 @@ zlib, }: -stdenv.mkDerivation rec { +let pname = "bloop"; - version = "2.1.1"; + sources = lib.importJSON ./sources.json; + inherit (sources) + repo + version + assets + completions + ; - platform = - if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64 then - "x86_64-pc-linux" - else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64 then - "aarch64-apple-darwin" - else - throw "unsupported platform"; + platforms = builtins.attrNames assets; - bloop-bash = fetchurl { - url = "https://github.com/scalacenter/bloop/releases/download/v${version}/bash-completions"; - sha256 = "sha256-2mt+zUEJvQ/5ixxFLZ3Z0m7uDSj/YE9sg/uNMjamvdE="; - }; + fetchAsset = + { asset, hash }: + fetchurl { + url = "https://github.com/${repo}/releases/download/v${version}/${asset}"; + inherit hash; + }; - bloop-fish = fetchurl { - url = "https://github.com/scalacenter/bloop/releases/download/v${version}/fish-completions"; - sha256 = "sha256-RF6nZxbw4sLwCP4irKJLYCZnlkaRdEkmpYkOHBoSF/8="; - }; - - bloop-zsh = fetchurl { - url = "https://github.com/scalacenter/bloop/releases/download/v${version}/zsh-completions"; - sha256 = "sha256-WNMsPwBfd5EjeRbRtc06lCEVI2FVoLfrqL82OR0G7/c="; - }; - - bloop-binary = fetchurl { - url = "https://github.com/scalacenter/bloop/releases/download/v${version}/bloop-${platform}"; - sha256 = - if stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86_64 then - "sha256-F5wRihAwf/TNBSYortTCoK9qKqTI+1N5InJ+rqLFp8A=" - else if stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64 then - "sha256-OrONKbC2l0jjfmguDmoiyEaJWdTrKBiP0ZEa5rhizDM=" - else - throw "unsupported platform"; - }; + bloop-binary = fetchAsset ( + assets.${stdenv.hostPlatform.system} or (throw "Unsupported platform ${stdenv.hostPlatform.system}") + ); + bloop-bash = fetchAsset completions.bash; + bloop-fish = fetchAsset completions.fish; + bloop-zsh = fetchAsset completions.zsh; +in +stdenv.mkDerivation { + inherit pname version; dontUnpack = true; nativeBuildInputs = [ @@ -76,14 +67,12 @@ stdenv.mkDerivation rec { meta = { homepage = "https://scalacenter.github.io/bloop/"; + changelog = "https://github.com/${repo}/releases/tag/v${version}"; sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; license = lib.licenses.asl20; description = "Scala build server and command-line tool to make the compile and test developer workflows fast and productive in a build-tool-agnostic way"; mainProgram = "bloop"; - platforms = [ - "x86_64-linux" - "aarch64-darwin" - ]; + inherit platforms; maintainers = with lib.maintainers; [ agilesteel kubukoz diff --git a/pkgs/by-name/bl/bloop/sources.json b/pkgs/by-name/bl/bloop/sources.json new file mode 100644 index 000000000000..b7184133bcdd --- /dev/null +++ b/pkgs/by-name/bl/bloop/sources.json @@ -0,0 +1,28 @@ +{ + "repo": "scalacenter/bloop", + "version": "2.1.1", + "completions": { + "bash": { + "asset": "bash-completions", + "hash": "sha256-2mt+zUEJvQ/5ixxFLZ3Z0m7uDSj/YE9sg/uNMjamvdE=" + }, + "fish": { + "asset": "fish-completions", + "hash": "sha256-RF6nZxbw4sLwCP4irKJLYCZnlkaRdEkmpYkOHBoSF/8=" + }, + "zsh": { + "asset": "zsh-completions", + "hash": "sha256-WNMsPwBfd5EjeRbRtc06lCEVI2FVoLfrqL82OR0G7/c=" + } + }, + "assets": { + "aarch64-darwin": { + "asset": "bloop-aarch64-apple-darwin", + "hash": "sha256-OrONKbC2l0jjfmguDmoiyEaJWdTrKBiP0ZEa5rhizDM=" + }, + "x86_64-linux": { + "asset": "bloop-x86_64-pc-linux", + "hash": "sha256-F5wRihAwf/TNBSYortTCoK9qKqTI+1N5InJ+rqLFp8A=" + } + } +} From 3d05d7dd470fbba43973e3dd080ff161c615b41a Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 14:49:39 -0300 Subject: [PATCH 4/8] bloop: add passthru test Checks the wrapper and the autopatchelfed binary answer --help, which is the only thing that works without network access: everything that reaches the build server downloads it from Maven Central first. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index 17d401330b11..f09b5b44dcdd 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -7,6 +7,7 @@ jre, lib, zlib, + runCommand, }: let @@ -35,7 +36,7 @@ let bloop-fish = fetchAsset completions.fish; bloop-zsh = fetchAsset completions.zsh; in -stdenv.mkDerivation { +stdenv.mkDerivation (finalAttrs: { inherit pname version; dontUnpack = true; @@ -65,6 +66,22 @@ stdenv.mkDerivation { runHook postInstall ''; + passthru = { + tests.help = runCommand "${pname}-help" { nativeBuildInputs = [ finalAttrs.finalPackage ]; } '' + export HOME="$TMPDIR" + + # Anything that reaches the build server, `bloop --version` and + # `bloop about` included, downloads it from Maven Central first and so + # cannot run in the sandbox. --help is answered by the native client + # alone, and still exercises the patched binary and its wrapper. + bloop --help > help.txt + grep -q 'Interact with Bloop' help.txt + grep -q -- '--java-home' help.txt + + touch $out + ''; + }; + meta = { homepage = "https://scalacenter.github.io/bloop/"; changelog = "https://github.com/${repo}/releases/tag/v${version}"; @@ -79,4 +96,4 @@ stdenv.mkDerivation { tomahna ]; }; -} +}) From 8e3e5247a43d182773ed4179f953204910502023 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 14:49:52 -0300 Subject: [PATCH 5/8] bloop: add update script Asks GitHub for the latest release, prefetches every asset sources.json lists and rewrites the file, so that r-ryantm can bump this package the way it already bumps scala-cli. The script takes no arguments and knows nothing about bloop: the repository, the current version and even which sections of assets exist are read out of sources.json, which is found through meta.position of $UPDATE_NIX_ATTR_PATH. writeShellApplication runs shellcheck over it at build time. It supports the `commit` updateScript feature: nix-shell maintainers/scripts/update.nix --argstr package bloop --arg commit true Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 6 ++ pkgs/by-name/bl/bloop/update.nix | 156 ++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 pkgs/by-name/bl/bloop/update.nix diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index f09b5b44dcdd..f00d39ec31da 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -7,6 +7,7 @@ jre, lib, zlib, + callPackage, runCommand, }: @@ -67,6 +68,11 @@ stdenv.mkDerivation (finalAttrs: { ''; passthru = { + updateScript = { + command = lib.getExe (callPackage ./update.nix { }); + supportedFeatures = [ "commit" ]; + }; + tests.help = runCommand "${pname}-help" { nativeBuildInputs = [ finalAttrs.finalPackage ]; } '' export HOME="$TMPDIR" diff --git a/pkgs/by-name/bl/bloop/update.nix b/pkgs/by-name/bl/bloop/update.nix new file mode 100644 index 000000000000..d8464fcbb8f3 --- /dev/null +++ b/pkgs/by-name/bl/bloop/update.nix @@ -0,0 +1,156 @@ +{ + writeShellApplication, + coreutils, + curl, + git, + jq, + nix, +}: + +writeShellApplication { + name = "update-bloop"; + + runtimeInputs = [ + coreutils + curl + git + jq + nix + ]; + + text = '' + # stdout is reserved for the JSON expected by the `commit` updateScript + # feature, everything else has to go to stderr. + attr_path="''${UPDATE_NIX_ATTR_PATH:-bloop}" + + nixpkgs=$(git rev-parse --show-toplevel) + position=$(nix-instantiate --eval --json --attr "$attr_path.meta.position" "$nixpkgs" \ + | jq --raw-output .) + + # Everything else is read out of the file that is about to be rewritten. + sources_json="$(dirname "''${position%:*}")/sources.json" + repo=$(jq --raw-output .repo "$sources_json") + old_version=$(jq --raw-output .version "$sources_json") + + auth=() + if [[ -n "''${GITHUB_TOKEN:-}" ]]; then + auth=(--header "Authorization: Bearer $GITHUB_TOKEN") + fi + + release=$(curl --silent --show-error --fail "''${auth[@]}" \ + "https://api.github.com/repos/$repo/releases/latest") + new_version=$(jq --raw-output '.tag_name // "" | ltrimstr("v")' <<< "$release") + + # both versions end up inside Nix expressions below + version_pattern='^[0-9][0-9A-Za-z.+-]*$' + + if [[ ! "$new_version" =~ $version_pattern ]]; then + echo "$repo published an implausible version: '$new_version'" >&2 + exit 1 + fi + + if [[ ! "$old_version" =~ $version_pattern ]]; then + echo "$sources_json holds an implausible version: '$old_version'" >&2 + exit 1 + fi + + order=$(nix-instantiate --eval \ + --expr "builtins.compareVersions \"$new_version\" \"$old_version\"") + + case "$order" in + 0) + echo "$attr_path is already at the latest version $new_version." >&2 + echo '[]' + exit 0 + ;; + -1) + echo "refusing to downgrade $attr_path from $old_version to $new_version." >&2 + exit 1 + ;; + esac + + # An asset that disappeared would otherwise surface as a bare 404 from + # nix-prefetch-url further down. + known=$(jq '[ to_entries[] | .value | objects | .[].asset ]' "$sources_json") + missing=$(jq --raw-output --argjson published "$(jq '[ .assets[].name ]' <<< "$release")" \ + '[ .[] | select(IN($published[]) | not) ] | join(", ")' <<< "$known") + + if [[ -n "$missing" ]]; then + echo "v$new_version does not ship $missing, listed in $sources_json" >&2 + exit 1 + fi + + prefetch() { + local hash + hash=$(nix-prefetch-url --type sha256 \ + "https://github.com/$repo/releases/download/v$new_version/$1") + + nix-hash --to-sri --type sha256 "$hash" + } + + #
-> { "": { asset, hash }, ... } for each key in that section + collect() { + local section="$1" + local objects=() + local key asset hash + + while read -r key; do + asset=$(jq --raw-output --arg section "$section" --arg key "$key" \ + '.[$section][$key].asset' "$sources_json") + hash=$(prefetch "$asset") + + objects+=("$(jq --null-input --compact-output \ + --arg key "$key" \ + --arg asset "$asset" \ + --arg hash "$hash" \ + '{ ($key): { asset: $asset, hash: $hash } }')") + done < <(jq --raw-output --arg section "$section" '.[$section] | keys[]' "$sources_json") + + if [[ ''${#objects[@]} -eq 0 ]]; then + echo '{}' + return + fi + + printf '%s\n' "''${objects[@]}" | jq --slurp add + } + + # Every object-valued key is a section of { : { asset, hash } }, repo + # and version being strings. Which sections exist is therefore data too. + sections='{}' + + while read -r section; do + entries=$(collect "$section") + sections=$(jq --arg section "$section" --argjson entries "$entries" \ + '. + { ($section): $entries }' <<< "$sections") + done < <(jq --raw-output 'to_entries[] | select(.value | type == "object") | .key' "$sources_json") + + # Write beside the target and move into place, so that a failure cannot + # leave a truncated sources.json and an unbuildable package behind. + tmp=$(mktemp "$sources_json.XXXXXX") + trap 'rm -f "$tmp"' EXIT + + jq --null-input \ + --arg repo "$repo" \ + --arg version "$new_version" \ + --argjson sections "$sections" \ + '{ repo: $repo, version: $version } + $sections' \ + > "$tmp" + + chmod --reference="$sources_json" "$tmp" + mv "$tmp" "$sources_json" + + jq --null-input --compact-output \ + --arg attrPath "$attr_path" \ + --arg oldVersion "$old_version" \ + --arg newVersion "$new_version" \ + --arg file "$sources_json" \ + --arg repo "$repo" \ + '[ { + attrPath: $attrPath, + oldVersion: $oldVersion, + newVersion: $newVersion, + files: [ $file ], + commitBody: "https://github.com/\($repo)/releases/tag/v\($newVersion)" + } ]' + ''; +} From 64cc46ece815e56289ece2e82da2ad2fe3d6de67 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Wed, 12 Aug 2026 16:02:18 -0300 Subject: [PATCH 6/8] bloop: test installed completions Parses each of the three completion assets with the shell it targets. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index f00d39ec31da..4e7fb08d0a51 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -9,6 +9,8 @@ zlib, callPackage, runCommand, + zsh, + fish, }: let @@ -86,6 +88,24 @@ stdenv.mkDerivation (finalAttrs: { touch $out ''; + + tests.completions = + runCommand "${pname}-completions" + { + nativeBuildInputs = [ + zsh + fish + ]; + } + '' + share=${finalAttrs.finalPackage}/share + + bash -n "$share/bash-completion/completions/bloop" + zsh -n "$share/zsh/site-functions/_bloop" + fish -n "$share/fish/vendor_completions.d/bloop.fish" + + touch $out + ''; }; meta = { From de49e1fc4af985fe7f5b31ac97e1d06b5a81b401 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Thu, 13 Aug 2026 17:28:18 -0300 Subject: [PATCH 7/8] bloop: give the wrapper a jre The wrapper referenced no jre at all: propagatedBuildInputs put one in the closure but on nobody PATH, so the client found a java only if the user happened to have one, and overriding jre changed nothing at run time. Now that the wrapper carries it, the propagation is redundant and was pushing the jre into the build environment of every dependent. Dropping it does mean bloop no longer puts a java on the PATH of anything that takes it as an input, `nix-shell -p bloop` included; add a jre alongside it if you relied on that. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index 4e7fb08d0a51..856540513ade 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -52,14 +52,17 @@ stdenv.mkDerivation (finalAttrs: { (lib.getLib stdenv.cc.cc) zlib ]; - propagatedBuildInputs = [ jre ]; installPhase = '' runHook preInstall install -D -m 0755 ${bloop-binary} $out/.bloop-wrapped - makeWrapper $out/.bloop-wrapped $out/bin/bloop + # The client starts a jvm build server, so it needs a jre on PATH and in + # JAVA_HOME; propagating the jre put it in the closure but on nobody's PATH. + makeWrapper $out/.bloop-wrapped $out/bin/bloop \ + --prefix PATH : ${lib.makeBinPath [ jre ]} \ + --set JAVA_HOME ${jre.home} #Install completions installShellCompletion --name bloop --bash ${bloop-bash} From 71bf0153865f622b98612d4f9051a688f52bf803 Mon Sep 17 00:00:00 2001 From: Vladyslav Pekker Date: Thu, 13 Aug 2026 17:33:10 -0300 Subject: [PATCH 8/8] bloop: require Java 17 or newer Upstream documents JDK 17 or higher for the CLI, which is what this packages. Assisted-by: Claude Code (Claude Opus 5) --- pkgs/by-name/bl/bloop/package.nix | 33 ++++++++++++++++++------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/pkgs/by-name/bl/bloop/package.nix b/pkgs/by-name/bl/bloop/package.nix index 856540513ade..1690f454f309 100644 --- a/pkgs/by-name/bl/bloop/package.nix +++ b/pkgs/by-name/bl/bloop/package.nix @@ -53,24 +53,29 @@ stdenv.mkDerivation (finalAttrs: { zlib ]; - installPhase = '' - runHook preInstall + # upstream documents JDK 17 or higher for the CLI, which is what this packages + installPhase = + assert lib.assertMsg (lib.versionAtLeast jre.version "17.0.0") '' + bloop requires Java 17 or newer, but ${jre.name} is ${jre.version} + ''; + '' + runHook preInstall - install -D -m 0755 ${bloop-binary} $out/.bloop-wrapped + install -D -m 0755 ${bloop-binary} $out/.bloop-wrapped - # The client starts a jvm build server, so it needs a jre on PATH and in - # JAVA_HOME; propagating the jre put it in the closure but on nobody's PATH. - makeWrapper $out/.bloop-wrapped $out/bin/bloop \ - --prefix PATH : ${lib.makeBinPath [ jre ]} \ - --set JAVA_HOME ${jre.home} + # The client starts a jvm build server, so it needs a jre on PATH and in + # JAVA_HOME; propagating the jre put it in the closure but on nobody's PATH. + makeWrapper $out/.bloop-wrapped $out/bin/bloop \ + --prefix PATH : ${lib.makeBinPath [ jre ]} \ + --set JAVA_HOME ${jre.home} - #Install completions - installShellCompletion --name bloop --bash ${bloop-bash} - installShellCompletion --name _bloop --zsh ${bloop-zsh} - installShellCompletion --name bloop.fish --fish ${bloop-fish} + #Install completions + installShellCompletion --name bloop --bash ${bloop-bash} + installShellCompletion --name _bloop --zsh ${bloop-zsh} + installShellCompletion --name bloop.fish --fish ${bloop-fish} - runHook postInstall - ''; + runHook postInstall + ''; passthru = { updateScript = {