bloop: fix completions and jre-less wrapper, add update script (#552782)

This commit is contained in:
Jakub Kozłowski
2026-08-17 19:12:34 +00:00
committed by GitHub
3 changed files with 273 additions and 53 deletions

View File

@@ -7,49 +7,40 @@
jre,
lib,
zlib,
callPackage,
runCommand,
zsh,
fish,
}:
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.isx86_64 then
"x86_64-apple-darwin"
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-eFESR6iPHRDViGv+Fk3sCvPgVAhk2L1gCG4LnfXO/v4=";
};
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.isx86_64 then
"sha256-wQXAldzU6Typ6pZB8k3dfX7g+aaVF7jXvd0pnuk5gZU="
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 (finalAttrs: {
inherit pname version;
dontUnpack = true;
nativeBuildInputs = [
@@ -61,37 +52,82 @@ stdenv.mkDerivation rec {
(lib.getLib stdenv.cc.cc)
zlib
];
propagatedBuildInputs = [ jre ];
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
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}
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 = {
command = lib.getExe (callPackage ./update.nix { });
supportedFeatures = [ "commit" ];
};
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
'';
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 = {
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
tomahna
];
};
}
})

View File

@@ -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="
}
}
}

View File

@@ -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"
}
# <section> -> { "<key>": { 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 { <key>: { 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)"
} ]'
'';
}