at master 14 kB view raw
1#!/usr/bin/env nix-shell 2#!nix-shell -I nixpkgs=./. -i bash -p curl jq nix gnused nixfmt 3# shellcheck shell=bash 4 5set -Eeuo pipefail 6shopt -s inherit_errexit 7 8trap 'exit 1' ERR 9 10rids=({linux-{,musl-}{arm,arm64,x64},osx-{arm64,x64},win-{arm64,x64,x86}}) 11 12release () { 13 local content="$1" 14 local version="$2" 15 16 jq -er '.releases[] | select(."release-version" == "'"$version"'")' <<< "$content" 17} 18 19release_files () { 20 local release="$1" 21 local expr="$2" 22 23 jq -er '[('"$expr"').files[] | select(.name | test("^.*.tar.gz$"))]' <<< "$release" 24} 25 26release_platform_attr () { 27 local release_files="$1" 28 local platform="$2" 29 local attr="$3" 30 31 jq -r '.[] | select((.rid == "'"$platform"'") and (.name | contains("-composite-") or contains("-pack-") | not)) | ."'"$attr"'"' <<< "$release_files" 32} 33 34platform_sources () { 35 local release_files="$1" 36 37 echo "srcs = {" 38 for rid in "${rids[@]}"; do 39 local url hash 40 41 url=$(release_platform_attr "$release_files" "$rid" url) 42 hash=$(release_platform_attr "$release_files" "$rid" hash) 43 44 [[ -z "$url" || -z "$hash" ]] && continue 45 46 hash=$(nix --extra-experimental-features nix-command hash convert --to sri --hash-algo sha512 "$hash") 47 48 echo " $rid = { 49 url = \"$url\"; 50 hash = \"$hash\"; 51 };" 52 done 53 echo " };" 54} 55 56nuget_index="$(curl -fsSL "https://api.nuget.org/v3/index.json")" 57 58get_nuget_resource() { 59 jq -er '.resources[] | select(."@type" == "'"$1"'")."@id"' <<<"$nuget_index" 60} 61 62nuget_package_base_url="$(get_nuget_resource "PackageBaseAddress/3.0.0")" 63nuget_registration_base_url="$(get_nuget_resource "RegistrationsBaseUrl/3.6.0")" 64 65generate_package_list() { 66 local version="$1" indent="$2" 67 shift 2 68 local pkgs=( "$@" ) pkg url hash catalog_url catalog hash_algorithm 69 70 for pkg in "${pkgs[@]}"; do 71 url=${nuget_package_base_url}${pkg,,}/${version,,}/${pkg,,}.${version,,}.nupkg 72 73 if hash=$(curl -s --head "$url" -o /dev/null -w '%header{x-ms-meta-sha512}') && [[ -n "$hash" ]]; then 74 # Undocumented fast path for nuget.org 75 # https://github.com/NuGet/NuGetGallery/issues/9433#issuecomment-1472286080 76 hash=$(nix --extra-experimental-features nix-command hash convert --to sri --hash-algo sha512 "$hash") 77 elif { 78 catalog_url=$(curl -sL --compressed "${nuget_registration_base_url}${pkg,,}/${version,,}.json" | jq -r ".catalogEntry") && [[ -n "$catalog_url" ]] && 79 catalog=$(curl -sL "$catalog_url") && [[ -n "$catalog" ]] && 80 hash_algorithm="$(jq -er '.packageHashAlgorithm' <<<"$catalog")"&& [[ -n "$hash_algorithm" ]] && 81 hash=$(jq -er '.packageHash' <<<"$catalog") && [[ -n "$hash" ]] 82 }; then 83 # Documented but slower path (requires 2 requests) 84 hash=$(nix --extra-experimental-features nix-command hash convert --to sri --hash-algo "${hash_algorithm,,}" "$hash") 85 elif hash=$(nix-prefetch-url "$url" --type sha512); then 86 # Fallback to downloading and hashing locally 87 echo "Failed to fetch hash from nuget for $url, falling back to downloading locally" >&2 88 hash=$(nix --extra-experimental-features nix-command hash convert --to sri --hash-algo sha512 "$hash") 89 else 90 echo "Failed to fetch hash for $url" >&2 91 exit 1 92 fi 93 94 echo "$indent(fetchNupkg { pname = \"${pkg}\"; version = \"${version}\"; hash = \"${hash}\"; })" 95 done 96} 97 98versionAtLeast () { 99 local cur_version=$1 min_version=$2 100 printf "%s\0%s" "$min_version" "$cur_version" | sort -zVC 101} 102 103# These packages are implicitly references by the build process, 104# based on the specific project configurations (RIDs, used features, etc.) 105# They are always referenced with the same version as the SDK used for building. 106# Since we lock nuget dependencies, when these packages are included in the generated 107# lock files (deps.nix), every update of SDK required those lock files to be 108# updated to reflect the new versions of these packages - otherwise, the build 109# would fail due to missing dependencies. 110# 111# Moving them to a separate list stored alongside the SDK package definitions, 112# and implicitly including them along in buildDotnetModule allows us 113# to make updating .NET SDK packages a lot easier - we now just update 114# the versions of these packages in one place, and all packages that 115# use buildDotnetModule continue building with the new .NET version without changes. 116# 117# Keep in mind that there is no canonical list of these implicitly 118# referenced packages - this list was created based on looking into 119# the deps.nix files of existing packages, and which dependencies required 120# updating after a SDK version bump. 121# 122# Due to this, make sure to check if new SDK versions introduce any new packages. 123# This should not happend in minor or bugfix updates, but probably happens 124# with every new major .NET release. 125aspnetcore_packages () { 126 local version=$1 127 local pkgs=( 128 Microsoft.AspNetCore.App.Ref 129 ) 130 131 generate_package_list "$version" ' ' "${pkgs[@]}" 132} 133 134aspnetcore_target_packages () { 135 local version=$1 136 local rid=$2 137 local pkgs=( 138 "Microsoft.AspNetCore.App.Runtime.$rid" 139 ) 140 141 generate_package_list "$version" ' ' "${pkgs[@]}" 142} 143 144netcore_packages () { 145 local version=$1 146 local pkgs=( 147 Microsoft.NETCore.DotNetAppHost 148 Microsoft.NETCore.App.Ref 149 ) 150 151 if ! versionAtLeast "$version" 9; then 152 pkgs+=( 153 Microsoft.NETCore.DotNetHost 154 Microsoft.NETCore.DotNetHostPolicy 155 Microsoft.NETCore.DotNetHostResolver 156 ) 157 fi 158 159 if versionAtLeast "$version" 7; then 160 pkgs+=( 161 Microsoft.DotNet.ILCompiler 162 ) 163 fi 164 165 if versionAtLeast "$version" 8; then 166 pkgs+=( 167 Microsoft.NET.ILLink.Tasks 168 ) 169 fi 170 171 generate_package_list "$version" ' ' "${pkgs[@]}" 172} 173 174netcore_host_packages () { 175 local version=$1 176 local rid=$2 177 local pkgs=( 178 "Microsoft.NETCore.App.Crossgen2.$rid" 179 ) 180 181 local min_ilcompiler= 182 case "$rid" in 183 linux-musl-arm) ;; 184 linux-arm) ;; 185 win-x86) ;; 186 osx-arm64) min_ilcompiler=8 ;; 187 *) min_ilcompiler=7 ;; 188 esac 189 190 if [[ -n "$min_ilcompiler" ]] && versionAtLeast "$version" "$min_ilcompiler"; then 191 pkgs+=( 192 "runtime.$rid.Microsoft.DotNet.ILCompiler" 193 ) 194 fi 195 196 generate_package_list "$version" ' ' "${pkgs[@]}" 197} 198 199netcore_target_packages () { 200 local version=$1 201 local rid=$2 202 local pkgs=( 203 "Microsoft.NETCore.App.Host.$rid" 204 "Microsoft.NETCore.App.Runtime.$rid" 205 "runtime.$rid.Microsoft.NETCore.DotNetAppHost" 206 ) 207 208 if ! versionAtLeast "$version" 9; then 209 pkgs+=( 210 "runtime.$rid.Microsoft.NETCore.DotNetHost" 211 "runtime.$rid.Microsoft.NETCore.DotNetHostPolicy" 212 "runtime.$rid.Microsoft.NETCore.DotNetHostResolver" 213 ) 214 case "$rid" in 215 linux-musl-arm*) ;; 216 win-arm64) ;; 217 *) pkgs+=( 218 "Microsoft.NETCore.App.Runtime.Mono.$rid" 219 ) ;; 220 esac 221 fi 222 223 if versionAtLeast "$version" 10; then 224 pkgs+=( 225 "Microsoft.NETCore.App.Runtime.NativeAOT.$rid" 226 ) 227 fi 228 229 generate_package_list "$version" ' ' "${pkgs[@]}" 230} 231 232usage () { 233 echo "Usage: $pname [[--sdk] [-o output] sem-version] ... 234Get updated dotnet src (platform - url & sha512) expressions for specified versions 235 236Exit codes: 237 0 Success 238 1 Failure 239 2 Release not found 240 241Examples: 242 $pname 6.0.14 7.0.201 - specific x.y.z versions 243 $pname 6.0 7.0 - latest x.y versions 244" >&2 245} 246 247update() { 248 local -r sem_version=$1 sdk=$2 249 local output=$3 250 251 local patch_specified=false 252 # Check if a patch was specified as an argument. 253 # If so, generate file for the specific version. 254 # If only x.y version was provided, get the latest patch 255 # version of the given x.y version. 256 if [[ "$sem_version" =~ ^[0-9]{1,}\.[0-9]{1,}\.[0-9]{1,} ]]; then 257 patch_specified=true 258 elif [[ ! "$sem_version" =~ ^[0-9]{1,}\.[0-9]{1,}$ ]]; then 259 usage 260 return 1 261 fi 262 263 : ${output:="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"/versions/$sem_version.nix} 264 echo "Generating $output" 265 266 # Make sure the x.y version is properly passed to .NET release metadata url. 267 # Then get the json file and parse it to find the latest patch release. 268 local major_minor content major_minor_patch 269 major_minor=$(sed 's/^\([0-9]*\.[0-9]*\).*$/\1/' <<< "$sem_version") 270 content=$(curl -fsSL https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/"$major_minor"/releases.json) 271 if [[ -n $sdk ]]; then 272 trap '' ERR 273 major_minor_patch=$( 274 jq -er --arg version "$sem_version" ' 275 .releases[] | 276 select(.sdks[].version == $version) | 277 ."release-version"' <<< "$content" || if [[ $? == 4 ]]; then exit 2; else exit 1; fi) 278 trap 'exit 1' ERR 279 else 280 major_minor_patch=$([ "$patch_specified" == true ] && echo "$sem_version" || jq -er '."latest-release"' <<< "$content") 281 fi 282 local major_minor_underscore=${major_minor/./_} 283 284 local release_content aspnetcore_version runtime_version 285 local -a sdk_versions 286 287 release_content=$(release "$content" "$major_minor_patch") 288 aspnetcore_version=$(jq -er '."aspnetcore-runtime".version' <<< "$release_content") 289 runtime_version=$(jq -er '.runtime.version' <<< "$release_content") 290 291 if [[ -n $sdk ]]; then 292 sdk_versions=("$sem_version") 293 else 294 mapfile -t sdk_versions < <(jq -er '.sdks[] | .version' <<< "$release_content" | sort -rn) 295 fi 296 297 # If patch was not specified, check if the package is already the latest version 298 # If it is, exit early 299 if [ "$patch_specified" == false ] && [ -f "$output" ]; then 300 local -a versions 301 IFS= readarray -d '' versions < <( 302 nix-instantiate --eval --json -E "{ output }: with (import output { 303 buildAspNetCore = { ... }: {}; 304 buildNetSdk = { version, ... }: { inherit version; }; 305 buildNetRuntime = { version, ... }: { inherit version; }; 306 fetchNupkg = { ... }: {}; 307 }); (x: builtins.deepSeq x x) [ 308 runtime_${major_minor_underscore}.version 309 sdk_${major_minor_underscore}.version 310 ]" --argstr output "$output" | jq -e --raw-output0 .[]) 311 if [[ "${versions[0]}" == "$major_minor_patch" && "${versions[1]}" == "${sdk_versions[0]}" ]]; then 312 echo "Nothing to update." 313 return 314 fi 315 fi 316 317 local aspnetcore_files runtime_files 318 aspnetcore_files="$(release_files "$release_content" .\"aspnetcore-runtime\")" 319 runtime_files="$(release_files "$release_content" .runtime)" 320 321 local channel_version support_phase 322 channel_version=$(jq -er '."channel-version"' <<< "$content") 323 support_phase=$(jq -er '."support-phase"' <<< "$content") 324 325 local aspnetcore_sources runtime_sources 326 aspnetcore_sources="$(platform_sources "$aspnetcore_files")" 327 runtime_sources="$(platform_sources "$runtime_files")" 328 329 result=$(mktemp -t dotnet-XXXXXX.nix) 330 trap "rm -f $result" TERM INT EXIT 331 332 ( 333 echo "{ buildAspNetCore, buildNetRuntime, buildNetSdk, fetchNupkg }: 334 335# v$channel_version ($support_phase) 336 337let 338 commonPackages = [" 339 aspnetcore_packages "${aspnetcore_version}" 340 netcore_packages "${runtime_version}" 341 echo " ]; 342 343 hostPackages = {" 344 for rid in "${rids[@]}"; do 345 echo " $rid = [" 346 netcore_host_packages "${runtime_version}" "$rid" 347 echo " ];" 348 done 349 echo " }; 350 351 targetPackages = {" 352 for rid in "${rids[@]}"; do 353 echo " $rid = [" 354 aspnetcore_target_packages "${aspnetcore_version}" "$rid" 355 netcore_target_packages "${runtime_version}" "$rid" 356 echo " ];" 357 done 358 echo " }; 359 360in rec { 361 release_$major_minor_underscore = \"$major_minor_patch\"; 362 363 aspnetcore_$major_minor_underscore = buildAspNetCore { 364 version = \"${aspnetcore_version}\"; 365 $aspnetcore_sources 366 }; 367 368 runtime_$major_minor_underscore = buildNetRuntime { 369 version = \"${runtime_version}\"; 370 $runtime_sources 371 };" 372 373 local -A feature_bands 374 unset latest_sdk 375 376 for sdk_version in "${sdk_versions[@]}"; do 377 local sdk_base_version=${sdk_version%-*} 378 local feature_band=${sdk_base_version:0:-2}xx 379 # sometimes one release has e.g. both 8.0.202 and 8.0.203 380 [[ ! ${feature_bands[$feature_band]+true} ]] || continue 381 feature_bands[$feature_band]=$sdk_version 382 local sdk_files sdk_sources 383 sdk_files="$(release_files "$release_content" ".sdks[] | select(.version == \"$sdk_version\")")" 384 sdk_sources="$(platform_sources "$sdk_files")" 385 local sdk_attrname=sdk_${feature_band//./_} 386 [[ -v latest_sdk ]] || local latest_sdk=$sdk_attrname 387 388 echo " 389 $sdk_attrname = buildNetSdk { 390 version = \"${sdk_version}\"; 391 $sdk_sources 392 inherit commonPackages hostPackages targetPackages; 393 runtime = runtime_$major_minor_underscore; 394 aspnetcore = aspnetcore_$major_minor_underscore; 395 };" 396 done 397 398 if [[ -n $sdk ]]; then 399 echo " 400 sdk = sdk_$major_minor_underscore; 401" 402 fi 403 404 echo " 405 sdk_$major_minor_underscore = $latest_sdk; 406}" 407 )> "$result" 408 409 nixfmt "$result" 410 cp "$result" "$output" 411 echo "Generated $output" 412} 413 414main () { 415 local pname sdk output 416 pname=$(basename "$0") 417 418 sdk= 419 output= 420 421 while [ $# -gt 0 ]; do 422 case $1 in 423 --sdk) 424 shift 425 sdk=1 426 ;; 427 -o) 428 shift 429 output=$1 430 shift 431 ;; 432 *) 433 update "$1" "$sdk" "$output" 434 shift 435 ;; 436 esac 437 done 438} 439 440main "$@"