Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use dotnet-install.sh in .NET feature #628

Merged
merged 38 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
302a34f
Use dotnet-install.sh in .NET feature
sliekens Jul 21, 2023
53b8f30
Use latest.version files
sliekens Jul 22, 2023
c035e85
Cleanup runtime args
sliekens Jul 22, 2023
d3d149b
Use latest.version files in tests as well
sliekens Jul 22, 2023
bfb12d6
Improve tests, remove code duplication
sliekens Jul 23, 2023
00ce410
Add stderr helper
sliekens Jul 27, 2023
e2ce463
Validate version inputs
sliekens Jul 27, 2023
5b0b8d9
Use suggested description
sliekens Jul 27, 2023
6f7ddd1
Shorter version description
sliekens Jul 27, 2023
cde0ae6
Shorter version description
sliekens Jul 27, 2023
fd101aa
Clean up apt lists
sliekens Jul 27, 2023
dc7a0be
Verify 7.0 is latest
sliekens Jul 29, 2023
2d0fb73
Fix PATH, add test for .NET global tools
sliekens Jul 29, 2023
5200853
Include a copy of dotnet-install.sh in the Feature
sliekens Aug 1, 2023
ad0bf10
Configure useful env variables
sliekens Aug 2, 2023
e39d87b
Use stringly typed booleans
sliekens Aug 3, 2023
440d47b
Keep imperative writing style in option hints
sliekens Aug 7, 2023
3d48f77
Update maintainers
sliekens Aug 7, 2023
c415add
Move dotnet-install.sh into a vendor directory
sliekens Aug 7, 2023
d19b609
Refactor variables
sliekens Aug 7, 2023
ea94b44
Amend
sliekens Aug 7, 2023
27b0f44
Amend 2
sliekens Aug 7, 2023
2b2e683
Use default options from devcontainer-feature.json
sliekens Aug 14, 2023
37e14cc
Add back variables
sliekens Aug 27, 2023
deb2094
Fix shellchek warning in fetch_latest_sdk_version
sliekens Aug 27, 2023
95028c4
Inline install_version function
sliekens Aug 27, 2023
fd39498
Fix ShellCheck warnings
sliekens Aug 27, 2023
8ec9c02
Improve CSV parsing
sliekens Aug 27, 2023
9361e33
Default to latest when configuring an empty version
sliekens Aug 28, 2023
f9167de
Add support for runtime-only configurations
sliekens Aug 28, 2023
3ba0f40
Move 'none' check higher up
sliekens Aug 29, 2023
6cdab1e
Deduplicate helper functions, sort into files
sliekens Aug 29, 2023
dd3f3ec
Address the user more directly in NOTES
sliekens Aug 29, 2023
a5ca01b
Remove unnecessary defaults
sliekens Aug 30, 2023
5b71121
Replace feature -> Feature
sliekens Sep 6, 2023
37c220f
Add update-dotnet-install-script workflow
sliekens Sep 6, 2023
059221f
Apply suggestions from code review
sliekens Sep 6, 2023
8eef025
Don't skip ci for automated script update
sliekens Sep 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/dotnet/NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,71 @@
## Configuration examples
sliekens marked this conversation as resolved.
Show resolved Hide resolved

Installing only the latest .NET SDK version (the default).

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": "latest" // or "" or {}
}
```

Installing an additional SDK version. Multiple versions can be specified as comma-separated values.

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"additionalVersions": "lts"
}
}
```

Installing specific SDK versions.

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0",
"additionalVersions": "7.0, 8.0"
}
}
```

Installing a specific SDK feature band.

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.4xx",
}
}
```

Installing a specific SDK patch version.

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "6.0.412",
}
}
```

Installing only the .NET Runtime or the ASP.NET Core Runtime. Note that the SDK includes all runtimes so this configuration is only useful for users who need to run .NET apps without building them from source.

``` json
{
"features": {
"ghcr.io/devcontainers/features/dotnet:2": {
"version": "none",
"dotnetRuntimeVersions": "latest, lts",
"aspnetCoreRuntimeVersions": "latest, lts",
}
}
```

## OS Support

Expand Down
11 changes: 11 additions & 0 deletions src/dotnet/devcontainer-feature.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"proposals": [
"latest",
"lts",
"none",
"8.0",
"7.0",
"6.0"
Expand All @@ -21,6 +22,16 @@
"type": "string",
sliekens marked this conversation as resolved.
Show resolved Hide resolved
"default": "",
"description": "Enter additional .NET SDK versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
},
"dotnetRuntimeVersions": {
"type": "string",
"default": "",
"description": "Enter additional .NET runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
},
"aspNetCoreRuntimeVersions": {
"type": "string",
"default": "",
"description": "Enter additional ASP.NET Core runtime versions, separated by commas. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
}
},
"containerEnv": {
sliekens marked this conversation as resolved.
Show resolved Hide resolved
Expand Down
168 changes: 76 additions & 92 deletions src/dotnet/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@
#
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The Dev Container spec maintainers
DOTNET_VERSION="${VERSION:-"latest"}"
ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS:-""}"
sliekens marked this conversation as resolved.
Show resolved Hide resolved
DOTNET_RUNTIME_VERSIONS="${DOTNETRUNTIMEVERSIONS:-""}"
ASPNETCORE_RUNTIME_VERSIONS="${ASPNETCORERUNTIMEVERSIONS:-""}"

DOTNET_INSTALL_SCRIPT='scripts/vendor/dotnet-install.sh'
DOTNET_INSTALL_DIR='/usr/share/dotnet'

set -e

source "scripts/install-dotnet-sdk.sh"
source "scripts/install-dotnet-runtime.sh"
source "scripts/install-aspnetcore-runtime.sh"

# Clean up
rm -rf /var/lib/apt/lists/*
sliekens marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -34,128 +43,103 @@ check_packages() {
fi
}

fetch_latest_sdk_version_in_channel() {
local channel="$1"
wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
}
# Removes leading and trailing whitespace from an input string
trim_whitespace() {
text="$1"

fetch_latest_sdk_version() {
local latest_STS=$(fetch_latest_sdk_version_in_channel "STS")
local latest_LTS=$(fetch_latest_sdk_version_in_channel "LTS")
if [[ "$latest_STS" > "$latest_LTS" ]]; then
echo "$latest_STS"
else
echo "$latest_LTS"
fi
}
# Remove leading spaces
while [ "${text:0:1}" == " " ]; do
text="${text:1}"
done

# Installs a version of .NET using the DOTNET_INSTALLER_SCRIPT
install_version() {
local inputVersion="$1"

echo "Installing version '$inputVersion'..."

# Quick options reminder for dotnet-install.sh:
#
# --version: 'latest' (default) or an exact version in the form 'A.B.C' like '6.0.412'
# --channel: 'LTS' (default), 'STS', a two-part version in the form 'A.B' like '6.0' or three-part form 'A.B.Cxx' like '6.0.1xx'
# --quality: 'daily', 'signed', 'validated', 'preview' or 'GA'
#
# Valid examples
#
# dotnet-install.sh [--version latest] [--channel LTS]
# dotnet-install.sh [--version latest] --channel STS
# dotnet-install.sh [--version latest] --channel 6.0 [--quality GA]
# dotnet-install.sh [--version latest] --channel 6.0.4xx [--quality GA]
# dotnet-install.sh [--version latest] --channel 8.0 --quality preview
# dotnet-install.sh [--version latest] --channel 8.0 --quality daily
# dotnet-install.sh --version 6.0.412
#
# The channel option is only used when version is 'latest' because an exact version overrides the channel option
# The quality option is only used when channel is 'A.B' or 'A.B.Cxx' because it can't be used with STS or LTS
#
# This script aims to reduce these combinations of options to a single 'version' input
# Currently this script does not make it possible to request a version in the form 'A.B' or 'A.B.Cxx' and a quality other than 'GA'
local version=""
local channel=""
if [[ "$inputVersion" == "latest" ]]; then
# Fetch the latest version manually, because dotnet-install.sh does not support it directly
version=$(fetch_latest_sdk_version)
channel=""
elif [[ "$inputVersion" == "lts" ]]; then
# When user input is 'lts'
# Then version=latest, channel=LTS
version="latest"
channel="LTS"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+$ ]]; then
# When user input is form 'A.B' like '3.1'
# Then version=latest, channel=3.1
version="latest"
channel="$inputVersion"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+\.[0-9]xx$ ]]; then
# When user input is form 'A.B.Cxx' like '6.0.4xx'
# Then version=latest, channel=6.0.4xx
version="latest"
channel="$inputVersion"
else
# Assume version is an exact version string like '6.0.412' or '8.0.100-rc.1.23371.5''
version="$inputVersion"
channel=""
fi
# Remove trailing spaces
while [ "${text: -1}" == " " ]; do
text="${text:0:-1}"
done

echo "Executing $DOTNET_INSTALL_SCRIPT --install-dir $DOTNET_INSTALL_DIR --version $version --channel $channel --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--install-dir "$DOTNET_INSTALL_DIR" \
--version "$version" \
--channel "$channel" \
--no-path
echo "$text"
}

# Splits comma-separated values into an array
# Splits comma-separated values into an array while ignoring empty entries
split_csv() {
local OLD_IFS=$IFS
IFS=","
read -a values <<< "$1"
IFS=$OLD_IFS
echo "${values[@]}"
}
local -a values=()
while IFS="," read -ra entries; do
for entry in "${entries[@]}"; do
entry="$(trim_whitespace "$entry")"
if [ -n "$entry" ]; then
values+=("$entry")
fi
done
done <<< "$1"

# Removes leading and trailing whitespace from an input string
trim_whitespace() {
echo $1 | tr -d '[:space:]'
echo "${values[@]}"
}

if [ "$(id -u)" -ne 0 ]; then
err 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi

# For our own convenience, combine VERSION and ADDITIONALVERSIONS into a single 'versions' array

# For our own convenience, combine DOTNET_VERSION and ADDITIONAL_VERSIONS into a single 'versions' array
# Ensure there are no leading or trailing spaces that can break regex pattern matching
versions=($(trim_whitespace "$VERSION"))
for additional_version in $(split_csv "$ADDITIONALVERSIONS"); do
versions+=($(trim_whitespace "$additional_version"))
versions=("$(trim_whitespace "$DOTNET_VERSION")")
for additional_version in $(split_csv "$ADDITIONAL_VERSIONS"); do
versions+=("$additional_version")
done

dotnetRuntimeVersions=()
for dotnetRuntimeVersion in $(split_csv "$DOTNET_RUNTIME_VERSIONS"); do
dotnetRuntimeVersions+=("$dotnetRuntimeVersion")
done

aspNetCoreRuntimeVersions=()
for aspNetCoreRuntimeVersion in $(split_csv "$ASPNETCORE_RUNTIME_VERSIONS"); do
aspNetCoreRuntimeVersions+=("$aspNetCoreRuntimeVersion")
done

# Fail fast in case of bad input to avoid unneccesary work
# v1 of the .NET feature allowed specifying only a major version 'X' like '3'
# v2 removed this ability
# - because install-dotnet.sh does not support it directly
# - because the previous behavior installed an old version like '3.0.103', not the newest version '3.1.426', which was counterintuitive
for version in "${versions[@]}"; do
if [[ "$version" =~ ^[0-9]+$ ]]; then
# v1 of the .NET feature allowed specifying only a major version 'X' like '3'
# v2 removed this ability
# - because install-dotnet.sh does not support it directly
# - because the previous behavior installed an old version like '3.0.103', not the newest version '3.1.426', which was counterintuitive
err "Unsupported .NET SDK version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
exit 1
fi
done

for version in "${dotnetRuntimeVersions[@]}"; do
if [[ "$version" =~ ^[0-9]+$ ]]; then
err "Unsupported .NET Runtime version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
exit 1
fi
done

for version in "${aspNetCoreRuntimeVersions[@]}"; do
if [[ "$version" =~ ^[0-9]+$ ]]; then
err "Unsupported ASP.NET Core Runtime version '${version}'. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
exit 1
fi
done

# Install .NET versions and dependencies
# icu-devtools includes dependencies for .NET
check_packages wget ca-certificates icu-devtools

if [ "${versions[0]}" != "none" ]; then
sliekens marked this conversation as resolved.
Show resolved Hide resolved
for version in "${versions[@]}"; do
install_dotnet_sdk "$version"
done
fi

for version in "${versions[@]}"; do
install_version $version
for version in "${dotnetRuntimeVersions[@]}"; do
install_dotnet_runtime "$version"
done

for version in "${aspNetCoreRuntimeVersions[@]}"; do
install_aspnetcore_runtime "$version"
done

# Clean up
Expand Down
57 changes: 57 additions & 0 deletions src/dotnet/scripts/install-aspnetcore-runtime.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/bin/bash
#-------------------------------------------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#-------------------------------------------------------------------------------------------------------------
#
# Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The Dev Container spec maintainers
fetch_latest_aspnetcore_runtime_version_in_channel() {
local channel="$1"
wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
}

fetch_latest_aspnetcore_runtime_version() {
local sts_version
local lts_version
sts_version=$(fetch_latest_aspnetcore_runtime_version_in_channel "STS")
lts_version=$(fetch_latest_aspnetcore_runtime_version_in_channel "LTS")
if [[ "$sts_version" > "$lts_version" ]]; then
echo "$sts_version"
else
echo "$lts_version"
fi
}

install_aspnetcore_runtime() {
local inputVersion="$1"
local version=""
local channel=""
if [[ "$inputVersion" == "latest" ]]; then
# Fetch the latest version manually, because dotnet-install.sh does not support it directly
version=$(fetch_latest_aspnetcore_runtime_version)
channel=""
elif [[ "$inputVersion" == "lts" ]]; then
# When user input is 'lts'
# Then version=latest, channel=LTS
version="latest"
channel="LTS"
elif [[ "$inputVersion" =~ ^[0-9]+\.[0-9]+$ ]]; then
# When user input is form 'A.B' like '3.1'
# Then version=latest, channel=3.1
version="latest"
channel="$inputVersion"
else
# Assume version is an exact version string like '6.0.21' or '8.0.0-rc.2.23425.11'
version="$inputVersion"
channel=""
fi

echo "Executing $DOTNET_INSTALL_SCRIPT --runtime aspnetcore --version $version --channel $channel --install-dir $DOTNET_INSTALL_DIR --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--runtime aspnetcore \
--version "$version" \
--channel "$channel" \
--install-dir "$DOTNET_INSTALL_DIR" \
--no-path
}
Loading