3771de8ae0
In flakes, packages are not exposed directly but instead they are declared inside “packages” or “legacyPackages” output under their host platform. flake-compat reflects this. Let’s look for an attribute also in these outputs if the direct lookup fails.
260 lines
9.0 KiB
Bash
Executable File
260 lines
9.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -e
|
||
|
||
scriptName=update-source-version # do not use the .wrapped name
|
||
|
||
die() {
|
||
echo "$scriptName: error: $1" >&2
|
||
exit 1
|
||
}
|
||
|
||
usage() {
|
||
echo "Usage: $scriptName <attr> <version> [<new-source-hash>] [<new-source-url>]"
|
||
echo " [--version-key=<version-key>] [--system=<system>] [--file=<file-to-update>] [--rev=<revision>]"
|
||
echo " [--ignore-same-hash] [--print-changes]"
|
||
}
|
||
|
||
args=()
|
||
|
||
for arg in "$@"; do
|
||
case $arg in
|
||
--system=*)
|
||
system="${arg#*=}"
|
||
systemArg="--system ${arg#*=}"
|
||
;;
|
||
--version-key=*)
|
||
versionKey="${arg#*=}"
|
||
;;
|
||
--file=*)
|
||
nixFile="${arg#*=}"
|
||
if [[ ! -f "$nixFile" ]]; then
|
||
die "Could not find provided file $nixFile"
|
||
fi
|
||
;;
|
||
--rev=*)
|
||
newRevision="${arg#*=}"
|
||
;;
|
||
--ignore-same-hash)
|
||
ignoreSameHash="true"
|
||
;;
|
||
--print-changes)
|
||
printChanges="true"
|
||
;;
|
||
--help)
|
||
usage
|
||
exit 0
|
||
;;
|
||
--*)
|
||
echo "$scriptName: Unknown argument: $arg"
|
||
usage
|
||
exit 1
|
||
;;
|
||
*)
|
||
args["${#args[*]}"]=$arg
|
||
;;
|
||
esac
|
||
done
|
||
|
||
attr=${args[0]}
|
||
newVersion=${args[1]}
|
||
newHash=${args[2]}
|
||
newUrl=${args[3]}
|
||
|
||
# Third-party repositories might not accept arguments in their default.nix.
|
||
importTree="(let tree = import ./.; in if builtins.isFunction tree then tree {} else tree)"
|
||
|
||
if (( "${#args[*]}" < 2 )); then
|
||
echo "$scriptName: Too few arguments"
|
||
usage
|
||
exit 1
|
||
fi
|
||
|
||
if (( "${#args[*]}" > 4 )); then
|
||
echo "$scriptName: Too many arguments"
|
||
usage
|
||
exit 1
|
||
fi
|
||
|
||
if [[ -z "$versionKey" ]]; then
|
||
versionKey=version
|
||
fi
|
||
|
||
# Allow finding packages among flake outputs in repos using flake-compat.
|
||
pname=$(nix-instantiate $systemArg --eval --strict -A "$attr.name" || echo)
|
||
if [[ -z "$pname" ]]; then
|
||
if [[ -z "$system" ]]; then
|
||
system=$(nix-instantiate --eval -E 'builtins.currentSystem' | tr -d '"')
|
||
fi
|
||
|
||
pname=$(nix-instantiate $systemArg --eval --strict -A "packages.$system.$attr.name" || echo)
|
||
if [[ -n "$pname" ]]; then
|
||
attr="packages.$system.$attr"
|
||
else
|
||
pname=$(nix-instantiate $systemArg --eval --strict -A "legacyPackages.$system.$attr.name" || echo)
|
||
if [[ -n "$pname" ]]; then
|
||
attr="legacyPackages.$system.$attr"
|
||
else
|
||
die "Could not find attribute '$attr'!"
|
||
fi
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$nixFile" ]]; then
|
||
nixFile=$(nix-instantiate $systemArg --eval --strict -A "$attr.meta.position" | sed -re 's/^"(.*):[0-9]+"$/\1/')
|
||
if [[ ! -f "$nixFile" ]]; then
|
||
die "Couldn't evaluate '$attr.meta.position' to locate the .nix file!"
|
||
fi
|
||
|
||
# flake-compat will return paths in the Nix store, we need to correct for that.
|
||
possiblyOutPath=$(nix-instantiate $systemArg --eval -E "with $importTree; outPath" | tr -d '"')
|
||
if [[ -n "$possiblyOutPath" ]]; then
|
||
outPathEscaped=$(echo "$possiblyOutPath" | sed 's#[$^*\\.[|]#\\&#g')
|
||
pwdEscaped=$(echo "$PWD" | sed 's#[$^*\\.[|]#\\&#g')
|
||
nixFile=$(echo "$nixFile" | sed "s|^$outPathEscaped|$pwdEscaped|")
|
||
fi
|
||
fi
|
||
|
||
oldHashAlgo=$(nix-instantiate $systemArg --eval --strict -A "$attr.src.drvAttrs.outputHashAlgo" | tr -d '"')
|
||
oldHash=$(nix-instantiate $systemArg --eval --strict -A "$attr.src.drvAttrs.outputHash" | tr -d '"')
|
||
|
||
if [[ -z "$oldHashAlgo" || -z "$oldHash" ]]; then
|
||
die "Couldn't evaluate old source hash from '$attr.src'!"
|
||
fi
|
||
|
||
if [[ $(grep --count "$oldHash" "$nixFile") != 1 ]]; then
|
||
die "Couldn't locate old source hash '$oldHash' (or it appeared more than once) in '$nixFile'!"
|
||
fi
|
||
|
||
oldUrl=$(nix-instantiate $systemArg --eval -E "with $importTree; builtins.elemAt ($attr.src.drvAttrs.urls or [ $attr.src.url ]) 0" | tr -d '"')
|
||
|
||
if [[ -z "$oldUrl" ]]; then
|
||
die "Couldn't evaluate source url from '$attr.src'!"
|
||
fi
|
||
|
||
oldVersion=$(nix-instantiate $systemArg --eval -E "with $importTree; $attr.${versionKey} or (builtins.parseDrvName $attr.name).version" | tr -d '"')
|
||
|
||
if [[ -z "$oldVersion" ]]; then
|
||
die "Couldn't find out the old version of '$attr'!"
|
||
fi
|
||
|
||
if [[ "$oldVersion" = "$newVersion" ]]; then
|
||
echo "$scriptName: New version same as old version, nothing to do." >&2
|
||
if [ -n "$printChanges" ]; then
|
||
printf '[]\n'
|
||
fi
|
||
exit 0
|
||
fi
|
||
|
||
if [[ -n "$newRevision" ]]; then
|
||
oldRevision=$(nix-instantiate $systemArg --eval -E "with $importTree; $attr.src.rev" | tr -d '"')
|
||
if [[ -z "$oldRevision" ]]; then
|
||
die "Couldn't evaluate source revision from '$attr.src'!"
|
||
fi
|
||
fi
|
||
|
||
# Escape regex metacharacter that are allowed in store path names
|
||
oldVersionEscaped=$(echo "$oldVersion" | sed -re 's|[.+]|\\&|g')
|
||
oldUrlEscaped=$(echo "$oldUrl" | sed -re 's|[${}.+]|\\&|g')
|
||
|
||
if [[ $(grep --count --extended-regexp "^\s*(let\b)?\s*$versionKey\s*=\s*\"$oldVersionEscaped\"" "$nixFile") = 1 ]]; then
|
||
pattern="/\b$versionKey\b\s*=/ s|\"$oldVersionEscaped\"|\"$newVersion\"|"
|
||
elif [[ $(grep --count --extended-regexp "^\s*(let\b)?\s*name\s*=\s*\"[^\"]+-$oldVersionEscaped\"" "$nixFile") = 1 ]]; then
|
||
pattern="/\bname\b\s*=/ s|-$oldVersionEscaped\"|-$newVersion\"|"
|
||
else
|
||
die "Couldn't figure out where out where to patch in new version in '$attr'!"
|
||
fi
|
||
|
||
if [[ "$oldHash" =~ ^(sha256|sha512)[:-] ]]; then
|
||
# Handle the possible SRI-style hash attribute (in the form ${type}${separator}${hash})
|
||
# True SRI uses dash as a separator and only supports base64, whereas Nix’s SRI-style format uses a colon and supports all the same encodings like regular hashes (16/32/64).
|
||
# To keep this program reasonably simple, we will upgrade Nix’s format to SRI.
|
||
oldHashAlgo="${BASH_REMATCH[1]}"
|
||
sri=true
|
||
elif [[ "$oldHashAlgo" = "null" ]]; then
|
||
# Some fetcher functions support SRI-style `hash` attribute in addition to legacy type-specific attributes. When `hash` is used `outputHashAlgo` is null so let’s complain when SRI-style hash value was not detected.
|
||
die "Unable to figure out hashing scheme from '$oldHash' in '$attr'!"
|
||
fi
|
||
|
||
case "$oldHashAlgo" in
|
||
# Lengths of hex-encoded hashes
|
||
sha256) hashLength=64 ;;
|
||
sha512) hashLength=128 ;;
|
||
*) die "Unhandled hash algorithm '$oldHashAlgo' in '$attr'!" ;;
|
||
esac
|
||
|
||
# Make a temporary all-zeroes hash of $hashLength characters
|
||
tempHash=$(printf '%0*d' "$hashLength" 0)
|
||
|
||
if [[ -n "$sri" ]]; then
|
||
# SRI hashes only support base64
|
||
# SRI hashes need to declare the hash type as part of the hash
|
||
tempHash="$(nix to-sri --type "$oldHashAlgo" "$tempHash")"
|
||
fi
|
||
|
||
# Escape regex metacharacter that are allowed in hashes (+)
|
||
oldHashEscaped=$(echo "$oldHash" | sed -re 's|[+]|\\&|g')
|
||
tempHashEscaped=$(echo "$tempHash" | sed -re 's|[+]|\\&|g')
|
||
|
||
# Replace new version
|
||
sed -i.bak "$nixFile" -re "$pattern"
|
||
if cmp -s "$nixFile" "$nixFile.bak"; then
|
||
die "Failed to replace version '$oldVersion' to '$newVersion' in '$attr'!"
|
||
fi
|
||
|
||
# Replace new URL
|
||
if [[ -n "$newUrl" ]]; then
|
||
sed -i "$nixFile" -re "s|\"$oldUrlEscaped\"|\"$newUrl\"|"
|
||
|
||
if cmp -s "$nixFile" "$nixFile.bak"; then
|
||
die "Failed to replace source URL '$oldUrl' to '$newUrl' in '$attr'!"
|
||
fi
|
||
fi
|
||
|
||
sed -i "$nixFile" -re "s|\"$oldHashEscaped\"|\"$tempHash\"|"
|
||
if cmp -s "$nixFile" "$nixFile.bak"; then
|
||
die "Failed to replace source hash of '$attr' to a temporary hash!"
|
||
fi
|
||
|
||
# Replace new revision, if given
|
||
if [[ -n "$newRevision" ]]; then
|
||
sed -i "$nixFile" -re "s|\"$oldRevision\"|\"$newRevision\"|"
|
||
|
||
if cmp -s "$nixFile" "$nixFile.bak"; then
|
||
die "Failed to replace source revision '$oldRevision' to '$newRevision' in '$attr'!"
|
||
fi
|
||
fi
|
||
|
||
# If new hash not given on the command line, recalculate it ourselves.
|
||
if [[ -z "$newHash" ]]; then
|
||
nix-build $systemArg --no-out-link -A "$attr.src" 2>"$attr.fetchlog" >/dev/null || true
|
||
# FIXME: use nix-build --hash here once https://github.com/NixOS/nix/issues/1172 is fixed
|
||
newHash=$(sed '1,/hash mismatch in fixed-output derivation/d' "$attr.fetchlog" | grep --perl-regexp --only-matching 'got: +.+[:-]\K.+')
|
||
|
||
if [[ -n "$sri" ]]; then
|
||
# nix-build preserves the hashing scheme so we can just convert the result to SRI using the old type
|
||
newHash="$(nix to-sri --type "$oldHashAlgo" "$newHash")"
|
||
fi
|
||
fi
|
||
|
||
if [[ -z "$newHash" ]]; then
|
||
cat "$attr.fetchlog" >&2
|
||
die "Couldn't figure out new hash of '$attr.src'!"
|
||
fi
|
||
|
||
if [[ -z "${ignoreSameHash}" && "$oldVersion" != "$newVersion" && "$oldHash" = "$newHash" ]]; then
|
||
mv "$nixFile.bak" "$nixFile"
|
||
die "Both the old and new source hashes of '$attr.src' were equivalent. Please fix the package's source URL to be dependent on '\${version}'!"
|
||
fi
|
||
|
||
sed -i "$nixFile" -re "s|\"$tempHashEscaped\"|\"$newHash\"|"
|
||
if cmp -s "$nixFile" "$nixFile.bak"; then
|
||
die "Failed to replace temporary source hash of '$attr' to the final source hash!"
|
||
fi
|
||
|
||
rm -f "$nixFile.bak"
|
||
rm -f "$attr.fetchlog"
|
||
|
||
if [ -n "$printChanges" ]; then
|
||
printf '[{"attrPath":"%s","oldVersion":"%s","newVersion":"%s","files":["%s"]}]\n' "$attr" "$oldVersion" "$newVersion" "$nixFile"
|
||
fi
|