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