diff --git a/src/dotnet/devcontainer-feature.json b/src/dotnet/devcontainer-feature.json
index f6e0c39ab..600997dde 100644
--- a/src/dotnet/devcontainer-feature.json
+++ b/src/dotnet/devcontainer-feature.json
@@ -1,6 +1,6 @@
{
"id": "dotnet",
- "version": "2.1.3",
+ "version": "2.2.0",
"name": "Dotnet CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/dotnet",
"description": "This Feature installs the latest .NET SDK, which includes the .NET CLI and the shared runtime. Options are provided to choose a different version or additional versions.",
diff --git a/src/dotnet/scripts/dotnet-helpers.sh b/src/dotnet/scripts/dotnet-helpers.sh
index 26e29fa36..84a674917 100644
--- a/src/dotnet/scripts/dotnet-helpers.sh
+++ b/src/dotnet/scripts/dotnet-helpers.sh
@@ -18,11 +18,11 @@ fetch_latest_version_in_channel() {
local channel="$1"
local runtime="$2"
if [ "$runtime" = "dotnet" ]; then
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/Runtime/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/Runtime/$channel/latest.version"
elif [ "$runtime" = "aspnetcore" ]; then
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$channel/latest.version"
else
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/Sdk/$channel/latest.version"
fi
}
diff --git a/src/dotnet/scripts/vendor/dotnet-install.sh b/src/dotnet/scripts/vendor/dotnet-install.sh
index 38a160cf1..122ee68ed 100755
--- a/src/dotnet/scripts/vendor/dotnet-install.sh
+++ b/src/dotnet/scripts/vendor/dotnet-install.sh
@@ -423,11 +423,17 @@ get_normalized_architecture_for_specific_sdk_version() {
# args:
# version or channel - $1
is_arm64_supported() {
- #any channel or version that starts with the specified versions
- case "$1" in
- ( "1"* | "2"* | "3"* | "4"* | "5"*)
- echo false
- return 0
+ # Extract the major version by splitting on the dot
+ major_version="${1%%.*}"
+
+ # Check if the major version is a valid number and less than 6
+ case "$major_version" in
+ [0-9]*)
+ if [ "$major_version" -lt 6 ]; then
+ echo false
+ return 0
+ fi
+ ;;
esac
echo true
@@ -950,6 +956,37 @@ get_absolute_path() {
return 0
}
+# args:
+# override - $1 (boolean, true or false)
+get_cp_options() {
+ eval $invocation
+
+ local override="$1"
+ local override_switch=""
+
+ if [ "$override" = false ]; then
+ override_switch="-n"
+
+ # create temporary files to check if 'cp -u' is supported
+ tmp_dir="$(mktemp -d)"
+ tmp_file="$tmp_dir/testfile"
+ tmp_file2="$tmp_dir/testfile2"
+
+ touch "$tmp_file"
+
+ # use -u instead of -n if it's available
+ if cp -u "$tmp_file" "$tmp_file2" 2>/dev/null; then
+ override_switch="-u"
+ fi
+
+ # clean up
+ rm -f "$tmp_file" "$tmp_file2"
+ rm -rf "$tmp_dir"
+ fi
+
+ echo "$override_switch"
+}
+
# args:
# input_files - stdin
# root_path - $1
@@ -961,15 +998,7 @@ copy_files_or_dirs_from_list() {
local root_path="$(remove_trailing_slash "$1")"
local out_path="$(remove_trailing_slash "$2")"
local override="$3"
- local osname="$(get_current_os_name)"
- local override_switch=$(
- if [ "$override" = false ]; then
- if [ "$osname" = "linux-musl" ]; then
- printf -- "-u";
- else
- printf -- "-n";
- fi
- fi)
+ local override_switch="$(get_cp_options "$override")"
cat | uniq | while read -r file_path; do
local path="$(remove_beginning_slash "${file_path#$root_path}")"
@@ -1243,6 +1272,61 @@ downloadwget() {
return 0
}
+extract_stem() {
+ local url="$1"
+ # extract the protocol
+ proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
+ # remove the protocol
+ url="${1/$proto/}"
+ # extract the path (if any) - since we know all of our feeds have a first path segment, we can skip the first one. otherwise we'd use -f2- to get the full path
+ full_path="$(echo $url | grep / | cut -d/ -f2-)"
+ path="$(echo $full_path | cut -d/ -f2-)"
+ echo $path
+}
+
+check_url_exists() {
+ eval $invocation
+ local url="$1"
+
+ local code=""
+ if machine_has "curl"
+ then
+ code=$(curl --head -o /dev/null -w "%{http_code}" -s --fail "$url");
+ elif machine_has "wget"
+ then
+ # get the http response, grab the status code
+ server_response=$(wget -qO- --method=HEAD --server-response "$url" 2>&1)
+ code=$(echo "$server_response" | grep "HTTP/" | awk '{print $2}')
+ fi
+ if [ $code = "200" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+sanitize_redirect_url() {
+ eval $invocation
+
+ local url_stem
+ url_stem=$(extract_stem "$1")
+ say_verbose "Checking configured feeds for the asset at ${yellow:-}$url_stem${normal:-}"
+
+ for feed in "${feeds[@]}"
+ do
+ local trial_url="$feed/$url_stem"
+ say_verbose "Checking ${yellow:-}$trial_url${normal:-}"
+ if check_url_exists "$trial_url"; then
+ say_verbose "Found a match at ${yellow:-}$trial_url${normal:-}"
+ echo "$trial_url"
+ return 0
+ else
+ say_verbose "No match at ${yellow:-}$trial_url${normal:-}"
+ fi
+ done
+ return 1
+}
+
get_download_link_from_aka_ms() {
eval $invocation
@@ -1295,6 +1379,11 @@ get_download_link_from_aka_ms() {
return 1
fi
+ sanitized_redirect_url=$(sanitize_redirect_url "$aka_ms_download_link")
+ if [[ -n "$sanitized_redirect_url" ]]; then
+ aka_ms_download_link="$sanitized_redirect_url"
+ fi
+
say_verbose "The redirect location retrieved: '$aka_ms_download_link'."
return 0
else
@@ -1306,7 +1395,9 @@ get_download_link_from_aka_ms() {
get_feeds_to_use()
{
feeds=(
+ "https://builds.dotnet.microsoft.com/dotnet"
"https://dotnetcli.azureedge.net/dotnet"
+ "https://ci.dot.net/public"
"https://dotnetbuilds.azureedge.net/public"
)
@@ -1735,7 +1826,7 @@ do
zip_path="$1"
;;
-?|--?|-h|--help|-[Hh]elp)
- script_name="$(basename "$0")"
+ script_name="dotnet-install.sh"
echo ".NET Tools Installer"
echo "Usage:"
echo " # Install a .NET SDK of a given Quality from a given Channel"
@@ -1865,4 +1956,4 @@ fi
say "Note that the script does not resolve dependencies during installation."
say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section."
-say "Installation finished successfully."
+say "Installation finished successfully."
\ No newline at end of file
diff --git a/src/oryx/devcontainer-feature.json b/src/oryx/devcontainer-feature.json
index 1a3ee1b2f..ab9a8e782 100644
--- a/src/oryx/devcontainer-feature.json
+++ b/src/oryx/devcontainer-feature.json
@@ -1,6 +1,6 @@
{
"id": "oryx",
- "version": "1.3.7",
+ "version": "1.4.0",
"name": "Oryx",
"description": "Installs the oryx CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/oryx",
diff --git a/src/oryx/scripts/vendor/dotnet-install.sh b/src/oryx/scripts/vendor/dotnet-install.sh
index 38a160cf1..122ee68ed 100755
--- a/src/oryx/scripts/vendor/dotnet-install.sh
+++ b/src/oryx/scripts/vendor/dotnet-install.sh
@@ -423,11 +423,17 @@ get_normalized_architecture_for_specific_sdk_version() {
# args:
# version or channel - $1
is_arm64_supported() {
- #any channel or version that starts with the specified versions
- case "$1" in
- ( "1"* | "2"* | "3"* | "4"* | "5"*)
- echo false
- return 0
+ # Extract the major version by splitting on the dot
+ major_version="${1%%.*}"
+
+ # Check if the major version is a valid number and less than 6
+ case "$major_version" in
+ [0-9]*)
+ if [ "$major_version" -lt 6 ]; then
+ echo false
+ return 0
+ fi
+ ;;
esac
echo true
@@ -950,6 +956,37 @@ get_absolute_path() {
return 0
}
+# args:
+# override - $1 (boolean, true or false)
+get_cp_options() {
+ eval $invocation
+
+ local override="$1"
+ local override_switch=""
+
+ if [ "$override" = false ]; then
+ override_switch="-n"
+
+ # create temporary files to check if 'cp -u' is supported
+ tmp_dir="$(mktemp -d)"
+ tmp_file="$tmp_dir/testfile"
+ tmp_file2="$tmp_dir/testfile2"
+
+ touch "$tmp_file"
+
+ # use -u instead of -n if it's available
+ if cp -u "$tmp_file" "$tmp_file2" 2>/dev/null; then
+ override_switch="-u"
+ fi
+
+ # clean up
+ rm -f "$tmp_file" "$tmp_file2"
+ rm -rf "$tmp_dir"
+ fi
+
+ echo "$override_switch"
+}
+
# args:
# input_files - stdin
# root_path - $1
@@ -961,15 +998,7 @@ copy_files_or_dirs_from_list() {
local root_path="$(remove_trailing_slash "$1")"
local out_path="$(remove_trailing_slash "$2")"
local override="$3"
- local osname="$(get_current_os_name)"
- local override_switch=$(
- if [ "$override" = false ]; then
- if [ "$osname" = "linux-musl" ]; then
- printf -- "-u";
- else
- printf -- "-n";
- fi
- fi)
+ local override_switch="$(get_cp_options "$override")"
cat | uniq | while read -r file_path; do
local path="$(remove_beginning_slash "${file_path#$root_path}")"
@@ -1243,6 +1272,61 @@ downloadwget() {
return 0
}
+extract_stem() {
+ local url="$1"
+ # extract the protocol
+ proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
+ # remove the protocol
+ url="${1/$proto/}"
+ # extract the path (if any) - since we know all of our feeds have a first path segment, we can skip the first one. otherwise we'd use -f2- to get the full path
+ full_path="$(echo $url | grep / | cut -d/ -f2-)"
+ path="$(echo $full_path | cut -d/ -f2-)"
+ echo $path
+}
+
+check_url_exists() {
+ eval $invocation
+ local url="$1"
+
+ local code=""
+ if machine_has "curl"
+ then
+ code=$(curl --head -o /dev/null -w "%{http_code}" -s --fail "$url");
+ elif machine_has "wget"
+ then
+ # get the http response, grab the status code
+ server_response=$(wget -qO- --method=HEAD --server-response "$url" 2>&1)
+ code=$(echo "$server_response" | grep "HTTP/" | awk '{print $2}')
+ fi
+ if [ $code = "200" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+sanitize_redirect_url() {
+ eval $invocation
+
+ local url_stem
+ url_stem=$(extract_stem "$1")
+ say_verbose "Checking configured feeds for the asset at ${yellow:-}$url_stem${normal:-}"
+
+ for feed in "${feeds[@]}"
+ do
+ local trial_url="$feed/$url_stem"
+ say_verbose "Checking ${yellow:-}$trial_url${normal:-}"
+ if check_url_exists "$trial_url"; then
+ say_verbose "Found a match at ${yellow:-}$trial_url${normal:-}"
+ echo "$trial_url"
+ return 0
+ else
+ say_verbose "No match at ${yellow:-}$trial_url${normal:-}"
+ fi
+ done
+ return 1
+}
+
get_download_link_from_aka_ms() {
eval $invocation
@@ -1295,6 +1379,11 @@ get_download_link_from_aka_ms() {
return 1
fi
+ sanitized_redirect_url=$(sanitize_redirect_url "$aka_ms_download_link")
+ if [[ -n "$sanitized_redirect_url" ]]; then
+ aka_ms_download_link="$sanitized_redirect_url"
+ fi
+
say_verbose "The redirect location retrieved: '$aka_ms_download_link'."
return 0
else
@@ -1306,7 +1395,9 @@ get_download_link_from_aka_ms() {
get_feeds_to_use()
{
feeds=(
+ "https://builds.dotnet.microsoft.com/dotnet"
"https://dotnetcli.azureedge.net/dotnet"
+ "https://ci.dot.net/public"
"https://dotnetbuilds.azureedge.net/public"
)
@@ -1735,7 +1826,7 @@ do
zip_path="$1"
;;
-?|--?|-h|--help|-[Hh]elp)
- script_name="$(basename "$0")"
+ script_name="dotnet-install.sh"
echo ".NET Tools Installer"
echo "Usage:"
echo " # Install a .NET SDK of a given Quality from a given Channel"
@@ -1865,4 +1956,4 @@ fi
say "Note that the script does not resolve dependencies during installation."
say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section."
-say "Installation finished successfully."
+say "Installation finished successfully."
\ No newline at end of file
diff --git a/test/dotnet/dotnet_helpers.sh b/test/dotnet/dotnet_helpers.sh
index a24bd1ce2..01e554f66 100644
--- a/test/dotnet/dotnet_helpers.sh
+++ b/test/dotnet/dotnet_helpers.sh
@@ -9,11 +9,11 @@ fetch_latest_version_in_channel() {
local channel="$1"
local runtime="$2"
if [ "$runtime" = "dotnet" ]; then
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/Runtime/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/Runtime/$channel/latest.version"
elif [ "$runtime" = "aspnetcore" ]; then
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$channel/latest.version"
else
- wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
+ wget -qO- "https://builds.dotnet.microsoft.com/dotnet/Sdk/$channel/latest.version"
fi
}
diff --git a/test/dotnet/install_dotnet_latest_when_version_is_empty.sh b/test/dotnet/install_dotnet_latest_when_version_is_empty.sh
index cd23edcf1..b28c45a2f 100644
--- a/test/dotnet/install_dotnet_latest_when_version_is_empty.sh
+++ b/test/dotnet/install_dotnet_latest_when_version_is_empty.sh
@@ -18,9 +18,6 @@ expected=$(fetch_latest_version)
check "Latest .NET SDK version installed" \
is_dotnet_sdk_version_installed "$expected"
-check "Build and run example project" \
-dotnet run --project projects/net8.0
-
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults
\ No newline at end of file
diff --git a/test/dotnet/install_dotnet_lts.sh b/test/dotnet/install_dotnet_lts.sh
index bc2d40782..da9175c15 100644
--- a/test/dotnet/install_dotnet_lts.sh
+++ b/test/dotnet/install_dotnet_lts.sh
@@ -18,9 +18,6 @@ expected=$(fetch_latest_version_in_channel "LTS")
check "Latest LTS version installed" \
is_dotnet_sdk_version_installed "$expected"
-check "Build and run example project" \
-dotnet run --project projects/net8.0
-
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults
\ No newline at end of file
diff --git a/test/dotnet/install_dotnet_multiple_versions.sh b/test/dotnet/install_dotnet_multiple_versions.sh
index 5f0f12533..87bf8caf4 100644
--- a/test/dotnet/install_dotnet_multiple_versions.sh
+++ b/test/dotnet/install_dotnet_multiple_versions.sh
@@ -13,6 +13,9 @@ source dev-container-features-test-lib
source dotnet_env.sh
source dotnet_helpers.sh
+check ".NET SDK 9.0 installed" \
+is_dotnet_sdk_version_installed "9.0"
+
check ".NET SDK 8.0 installed" \
is_dotnet_sdk_version_installed "8.0"
@@ -22,9 +25,33 @@ is_dotnet_sdk_version_installed "7.0"
check ".NET SDK 6.0 installed" \
is_dotnet_sdk_version_installed "6.0"
+check ".NET SDK 5.0 installed" \
+is_dotnet_sdk_version_installed "5.0"
+
+check ".NET Core SDK 3.1 installed" \
+is_dotnet_sdk_version_installed "3.1"
+
check "Build example class library" \
dotnet build projects/multitargeting
+check "Build and run .NET 9.0 project" \
+dotnet run --project projects/net9.0
+
+check "Build and run .NET 8.0 project" \
+dotnet run --project projects/net8.0
+
+check "Build and run .NET 7.0 project" \
+dotnet run --project projects/net7.0
+
+check "Build and run .NET 6.0 project" \
+dotnet run --project projects/net6.0
+
+check "Build and run .NET 5.0 project" \
+dotnet run --project projects/net5.0
+
+check "Build and run .NET Core 3.1 project" \
+dotnet run --project projects/netcoreapp3.1
+
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults
\ No newline at end of file
diff --git a/test/dotnet/projects/multitargeting/example_classlib.csproj b/test/dotnet/projects/multitargeting/example_classlib.csproj
index ba47b0f46..8a8ca0c32 100644
--- a/test/dotnet/projects/multitargeting/example_classlib.csproj
+++ b/test/dotnet/projects/multitargeting/example_classlib.csproj
@@ -1,7 +1,7 @@
- net8.0;net7.0;net6.0
+ net9.0;net8.0;net7.0;net6.0
enable
enable
diff --git a/test/dotnet/projects/net5.0/example_project.csproj b/test/dotnet/projects/net5.0/example_project.csproj
index 63450c349..9fa0cf3f7 100644
--- a/test/dotnet/projects/net5.0/example_project.csproj
+++ b/test/dotnet/projects/net5.0/example_project.csproj
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/test/dotnet/projects/net6.0/example_project.csproj b/test/dotnet/projects/net6.0/example_project.csproj
index aa2434cb7..48c9b4e8a 100644
--- a/test/dotnet/projects/net6.0/example_project.csproj
+++ b/test/dotnet/projects/net6.0/example_project.csproj
@@ -8,7 +8,7 @@
-
+
\ No newline at end of file
diff --git a/test/dotnet/projects/net7.0/example_project.csproj b/test/dotnet/projects/net7.0/example_project.csproj
index 424f54ae7..11953d275 100644
--- a/test/dotnet/projects/net7.0/example_project.csproj
+++ b/test/dotnet/projects/net7.0/example_project.csproj
@@ -8,7 +8,7 @@
-
+
\ No newline at end of file
diff --git a/test/dotnet/projects/net8.0/example_project.csproj b/test/dotnet/projects/net8.0/example_project.csproj
index 2f1f60891..ddac9409e 100644
--- a/test/dotnet/projects/net8.0/example_project.csproj
+++ b/test/dotnet/projects/net8.0/example_project.csproj
@@ -8,7 +8,7 @@
-
+
\ No newline at end of file
diff --git a/test/dotnet/projects/net9.0/Program.cs b/test/dotnet/projects/net9.0/Program.cs
new file mode 100644
index 000000000..59fb426af
--- /dev/null
+++ b/test/dotnet/projects/net9.0/Program.cs
@@ -0,0 +1,32 @@
+using Newtonsoft.Json;
+
+string json = """
+{
+ "Name": "Inception",
+ "ReleaseDate": "2010-07-08T00:00:00",
+ "Genres": [
+ "Action",
+ "Thriller"
+ ]
+}
+""";
+
+Movie? m = JsonConvert.DeserializeObject(json);
+
+if (m == default)
+{
+ Console.WriteLine("Decoding failed!");
+}
+else
+{
+ Console.WriteLine($"Movie name: {m.Name}");
+ Console.WriteLine($"Release Date: {m.ReleaseDate}");
+ Console.WriteLine($"Genres: {string.Join(", ", m.Genres)}");
+}
+
+class Movie(string? name, DateTime releaseDate, List? genres)
+{
+ public string Name { get; set; } = name ?? "Default Name";
+ public DateTime ReleaseDate { get; set; } = releaseDate;
+ public List Genres { get; set; } = genres ?? [];
+}
\ No newline at end of file
diff --git a/test/dotnet/projects/net9.0/example_project.csproj b/test/dotnet/projects/net9.0/example_project.csproj
new file mode 100644
index 000000000..6cc1da0d0
--- /dev/null
+++ b/test/dotnet/projects/net9.0/example_project.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/dotnet/projects/netcoreapp3.1/example_project.csproj b/test/dotnet/projects/netcoreapp3.1/example_project.csproj
index e2e909c17..f8859db8b 100644
--- a/test/dotnet/projects/netcoreapp3.1/example_project.csproj
+++ b/test/dotnet/projects/netcoreapp3.1/example_project.csproj
@@ -3,12 +3,11 @@
Exe
netcoreapp3.1
- enable
enable
-
+
\ No newline at end of file
diff --git a/test/dotnet/scenarios.json b/test/dotnet/scenarios.json
index 2fd74f587..641753632 100644
--- a/test/dotnet/scenarios.json
+++ b/test/dotnet/scenarios.json
@@ -40,10 +40,13 @@
"remoteUser": "vscode",
"features": {
"dotnet": {
- "version": "8.0.100-preview.6.23330.14",
+ "version": "9.0",
"additionalVersions": [
+ "8.0",
"7.0",
- "6.0"
+ "6.0",
+ "5.0",
+ "3.1"
]
}
}
@@ -92,4 +95,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/test/dotnet/test.sh b/test/dotnet/test.sh
index 50d79941f..11cc73126 100644
--- a/test/dotnet/test.sh
+++ b/test/dotnet/test.sh
@@ -24,9 +24,6 @@ expected=$(fetch_latest_version)
check "Latest .NET SDK version installed" \
is_dotnet_sdk_version_installed "$expected"
-check "Build and run example project" \
-dotnet run --project projects/net8.0
-
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults
\ No newline at end of file