diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fb124d..69f56ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: "build-essential", "cypress-deps", "docker-out", + "dotnet", "eclipse-deps", "git-lfs", "gitlab-cli", diff --git a/README.md b/README.md index a964ddb..779df87 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ Below is a list with included features, click on the link for more details. | [build-essential](./features/src/build-essential/README.md) | Installs build essentials like gcc. | | [cypress-deps](./features/src/cypress-deps/README.md) | Installs all dependencies required to run Cypress. | | [docker-out](./features/src/docker-out/README.md) | Installs a Docker client which re-uses the host Docker socket. | +| [dotnet](./features/src/dotnet/README.md) | A package which installs .NET SDKs, runtimes and workloads. | | [eclipse-deps](./features/src/eclipse-deps/README.md) | Installs all dependencies required to run the Eclipse IDE. | | [git-lfs](./features/src/git-lfs/README.md) | Installs Git LFS. | | [gitlab-cli](./features/src/gitlab-cli/README.md) | Installs the GitLab CLI. | diff --git a/build/build.go b/build/build.go index d5157f1..442f5d6 100644 --- a/build/build.go +++ b/build/build.go @@ -100,6 +100,11 @@ func init() { gotaskr.Task("Feature:docker-out:Test", func() error { return testFeature("docker-out") }) gotaskr.Task("Feature:docker-out:Publish", func() error { return publishFeature("docker-out") }) + ////////// dotnet + gotaskr.Task("Feature:dotnet:Package", func() error { return packageFeature("dotnet") }) + gotaskr.Task("Feature:dotnet:Test", func() error { return testFeature("dotnet") }) + gotaskr.Task("Feature:dotnet:Publish", func() error { return publishFeature("dotnet") }) + ////////// eclipse-deps gotaskr.Task("Feature:eclipse-deps:Package", func() error { return packageFeature("eclipse-deps") }) gotaskr.Task("Feature:eclipse-deps:Test", func() error { return testFeature("eclipse-deps") }) diff --git a/features/src/dotnet/NOTES.md b/features/src/dotnet/NOTES.md new file mode 100644 index 0000000..e627ae8 --- /dev/null +++ b/features/src/dotnet/NOTES.md @@ -0,0 +1,33 @@ +## Dotnet Tools + +If you need additional tools for example like the Powerapps CLI you can install them using `dotnet tool install --create-manifest-if-needed `. +This installs the tool creates a manifest file: `.config/dotnet-tools.json`. + +```json +{ + "version": 1, + "isRoot": true, + "tools": { + "microsoft.powerapps.cli.tool": { + "version": "1.43.6", + "commands": [ + "pac" + ], + "rollForward": false + } + } +} +``` + +After this step, the tool can be invoked using `dotnet `. + +If you already have a manifest, all tools can be installed using `dotnet tool restore`. + +To do that automatically, include the command in your `devcontainer.json` like this: +```json +"postCreateCommand": "dotnet tool restore" +``` + +### System Compatibility + +Debian, Ubuntu, Alpine diff --git a/features/src/dotnet/README.md b/features/src/dotnet/README.md new file mode 100755 index 0000000..0a8772a --- /dev/null +++ b/features/src/dotnet/README.md @@ -0,0 +1,73 @@ +# .NET (dotnet) + +A package which installs .NET SDKs, runtimes and workloads. + +## Example Usage + +```json +"features": { + "ghcr.io/postfinance/devcontainer-features/dotnet:0.1.0": { + "version": "10.0", + "additionalVersions": "", + "dotnetRuntimeVersions": "", + "aspNetCoreRuntimeVersions": "", + "workloads": "", + "downloadUrl": "", + "versionsUrl": "", + "nugetConfigPath": "" + } +} +``` + +## Options + +| Option | Description | Type | Default Value | Proposals | +|-----|-----|-----|-----|-----| +| version | Select or enter a .NET SDK version. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | 10.0 | lts, none, 8.0, 9.0, 10.0, 8.0.408 | +| additionalVersions | Enter additional .NET SDK versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | <empty> | 8.0,9.0, 8.0.408 | +| dotnetRuntimeVersions | Enter additional .NET runtime versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | <empty> | 8.0.15, 9.0, lts, 8.0 | +| aspNetCoreRuntimeVersions | Enter additional ASP.NET Core runtime versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | <empty> | 8.0.15, lts, 8.0 | +| workloads | Enter additional .NET SDK workloads, separated by commas. Use 'dotnet workload search' to learn what workloads are available to install. | string | <empty> | wasm-tools, android, macos | +| downloadUrl | The download URL to use for Dotnet binaries. | string | <empty> | | +| versionsUrl | The URL to use for fetching available Dotnet versions. | string | <empty> | | +| nugetConfigPath | Path to a NuGet.Config file to copy into the container. This can be used to configure private package sources for the dotnet CLI. | string | <empty> | | + +## Customizations + +### VS Code Extensions + +- `ms-dotnettools.csharp` + +## Dotnet Tools + +If you need additional tools for example like the Powerapps CLI you can install them using `dotnet tool install --create-manifest-if-needed `. +This installs the tool creates a manifest file: `.config/dotnet-tools.json`. + +```json +{ + "version": 1, + "isRoot": true, + "tools": { + "microsoft.powerapps.cli.tool": { + "version": "1.43.6", + "commands": [ + "pac" + ], + "rollForward": false + } + } +} +``` + +After this step, the tool can be invoked using `dotnet `. + +If you already have a manifest, all tools can be installed using `dotnet tool restore`. + +To do that automatically, include the command in your `devcontainer.json` like this: +```json +"postCreateCommand": "dotnet tool restore" +``` + +### System Compatibility + +Debian, Ubuntu, Alpine diff --git a/features/src/dotnet/devcontainer-feature.json b/features/src/dotnet/devcontainer-feature.json new file mode 100644 index 0000000..3b54053 --- /dev/null +++ b/features/src/dotnet/devcontainer-feature.json @@ -0,0 +1,87 @@ +{ + "id": "dotnet", + "version": "0.1.0", + "name": ".NET", + "description": "A package which installs .NET SDKs, runtimes and workloads.", + "options": { + "version": { + "type": "string", + "proposals": [ + "lts", + "none", + "8.0", + "9.0", + "10.0", + "8.0.408" + ], + "default": "10.0", + "description": "Select or enter a .NET SDK version. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version." + }, + "additionalVersions": { + "type": "string", + "default": "", + "description": "Enter additional .NET SDK versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version.", + "proposals": [ + "8.0,9.0", + "8.0.408" + ] + }, + "dotnetRuntimeVersions": { + "type": "string", + "default": "", + "description": "Enter additional .NET runtime versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version.", + "proposals": [ + "8.0.15", + "9.0", + "lts, 8.0" + ] + }, + "aspNetCoreRuntimeVersions": { + "type": "string", + "default": "", + "description": "Enter additional ASP.NET Core runtime versions, separated by commas. Use 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version.", + "proposals": [ + "8.0.15", + "lts, 8.0" + ] + }, + "workloads": { + "type": "string", + "default": "", + "description": "Enter additional .NET SDK workloads, separated by commas. Use 'dotnet workload search' to learn what workloads are available to install.", + "proposals": [ + "wasm-tools", + "android, macos" + ] + }, + "downloadUrl": { + "type": "string", + "default": "", + "description": "The download URL to use for Dotnet binaries." + }, + "versionsUrl": { + "type": "string", + "default": "", + "description": "The URL to use for fetching available Dotnet versions." + }, + "nugetConfigPath": { + "type": "string", + "default": "", + "description": "Path to a NuGet.Config file to copy into the container. This can be used to configure private package sources for the dotnet CLI." + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-dotnettools.csharp" + ] + } + }, + "containerEnv": { + "DOTNET_ROOT": "/usr/share/dotnet", + "PATH": "$PATH:$DOTNET_ROOT:~/.dotnet/tools", + "DOTNET_RUNNING_IN_CONTAINER": "true", + "DOTNET_USE_POLLING_FILE_WATCHER": "true", + "DOTNET_CLI_TELEMETRY_OPTOUT": "1" + } +} \ No newline at end of file diff --git a/features/src/dotnet/install.sh b/features/src/dotnet/install.sh new file mode 100755 index 0000000..b383cc0 --- /dev/null +++ b/features/src/dotnet/install.sh @@ -0,0 +1,11 @@ +. ./functions.sh + +"./installer_$(detect_arch)" \ +-version="${VERSION:-"latest"}" \ +-additionalVersions="${ADDITIONALVERSIONS:-""}" \ +-dotnetRuntimeVersions="${DOTNETRUNTIMEVERSIONS:-""}" \ +-aspNetCoreRuntimeVersions="${ASPNETCORERUNTIMEVERSIONS:-""}" \ +-workloads="${WORKLOADS:-""}" \ +-downloadUrl="${DOWNLOADURL:-""}" \ +-versionsUrl="${VERSIONSURL:-""}" \ +-nugetConfigPath="${NUGETCONFIGPATH:-""}" diff --git a/features/src/dotnet/installer.go b/features/src/dotnet/installer.go new file mode 100644 index 0000000..0fbc172 --- /dev/null +++ b/features/src/dotnet/installer.go @@ -0,0 +1,372 @@ +package main + +import ( + "builder/installer" + "flag" + "fmt" + "os" + "regexp" + "strings" + + "github.com/roemer/gotaskr/execr" + "github.com/roemer/gover" +) + +////////// +// Configuration +////////// + +type Product int + +const ( + sdk Product = iota + runtime + aspNetRuntime +) + +func (c Product) String() string { + switch c { + case sdk: + return "Sdk" + case runtime: + return "Runtime" + case aspNetRuntime: + return "aspnetcore/Runtime" + default: + return "" + } +} + +////////// +// Main +////////// + +func main() { + if err := runMain(); err != nil { + fmt.Printf("Error: %v\n", err) + os.Exit(1) + } +} + +func runMain() error { + // Handle the flags + version := flag.String("version", "latest", "") + additionalVersions := flag.String("additionalVersions", "", "") + dotnetRuntimeVersions := flag.String("dotnetRuntimeVersions", "", "") + aspNetCoreRuntimeVersions := flag.String("aspNetCoreRuntimeVersions", "", "") + workloads := flag.String("workloads", "", "") + downloadUrl := flag.String("downloadUrl", "", "") + versionsUrl := flag.String("versionsUrl", "", "") + nugetConfigPath := flag.String("nugetConfigPath", "", "") + + flag.Parse() + + // Load settings from an external file + if err := installer.LoadOverrides(); err != nil { + return err + } + + installer.HandleOverride(downloadUrl, "https://builds.dotnet.microsoft.com/dotnet", "dotnet-download-url") + installer.HandleOverride(versionsUrl, "https://builds.dotnet.microsoft.com/dotnet", "dotnet-versions-url") + installer.HandleOverride(nugetConfigPath, "", "dotnet-nuget-config-path") + + // Handle multi value fields + var allSdks = []string{*version} + if len(*additionalVersions) > 0 { + allSdks = append(allSdks, strings.Split(*additionalVersions, ",")...) + } + var additionalRuntimes = []string{} + if len(*dotnetRuntimeVersions) > 0 { + additionalRuntimes = strings.Split(*dotnetRuntimeVersions, ",") + } + var additionalaspNetCoreRuntimes = []string{} + if len(*aspNetCoreRuntimeVersions) > 0 { + additionalaspNetCoreRuntimes = strings.Split(*aspNetCoreRuntimeVersions, ",") + } + // Create the feature + feature := installer.NewFeature(".NET", true) + if *nugetConfigPath != "" { + feature.AddComponents(&nugetConfigComponent{ + ComponentBase: installer.NewComponentBase("Nuget Config", installer.VERSION_IRRELEVANT), + NugetConfigPath: *nugetConfigPath, + }) + } + // add sdks + for _, sdkVersion := range allSdks { + component := &sdkComponent{ + ComponentBase: installer.NewComponentBase(fmt.Sprintf("SDK [%s]", sdkVersion), strings.TrimSpace(sdkVersion)), + DownloadUrl: *downloadUrl, + VersionsUrl: *versionsUrl, + } + feature.AddComponents(component) + } + // add runtimes + for _, runtimeVersion := range additionalRuntimes { + component := &runtimeComponent{ + ComponentBase: installer.NewComponentBase(fmt.Sprintf("Runtime [%s]", runtimeVersion), strings.TrimSpace(runtimeVersion)), + DownloadUrl: *downloadUrl, + VersionsUrl: *versionsUrl, + } + feature.AddComponents(component) + } + // add runtimes + for _, aspCoreVersion := range additionalaspNetCoreRuntimes { + component := &aspNetRuntimeComponent{ + ComponentBase: installer.NewComponentBase(fmt.Sprintf("ASP.NET Core runtime [%s]", aspCoreVersion), strings.TrimSpace(aspCoreVersion)), + DownloadUrl: *downloadUrl, + VersionsUrl: *versionsUrl, + } + feature.AddComponents(component) + } + // workloads + if len(*workloads) > 0 { + feature.AddComponents(&workloadComponent{ + ComponentBase: installer.NewComponentBase("Workloads", installer.VERSION_IRRELEVANT), + workloads: strings.Split(strings.ReplaceAll(*workloads, " ", ""), ","), + }) + } + + feature.AddComponents(&symlinkComponent{ + ComponentBase: installer.NewComponentBase("SymLink", installer.VERSION_IRRELEVANT), + }) + + // Process the feature + return feature.Process() +} + +////////// +// Implementation +////////// + +type sdkComponent struct { + *installer.ComponentBase + DownloadUrl string + VersionsUrl string +} + +func (c *sdkComponent) GetAllVersions() ([]*gover.Version, error) { + latestVersion, err := resolveSdkVersion(c.VersionsUrl, c.GetRequestedVersion()) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return []*gover.Version{version}, err +} + +func (c *sdkComponent) GetLatestVersion() (*gover.Version, error) { + latestVersion, err := resolveSdkVersion(c.VersionsUrl, installer.VERSION_LTS) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return version, err +} + +func (c *sdkComponent) InstallVersion(version *gover.Version) error { + return installSdk(c.DownloadUrl, version) +} + +type runtimeComponent struct { + *installer.ComponentBase + DownloadUrl string + VersionsUrl string +} + +func (c *runtimeComponent) GetAllVersions() ([]*gover.Version, error) { + latestVersion, err := resolveRuntimeVersion(c.VersionsUrl, c.GetRequestedVersion()) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return []*gover.Version{version}, err +} + +func (c *runtimeComponent) GetLatestVersion() (*gover.Version, error) { + latestVersion, err := resolveRuntimeVersion(c.VersionsUrl, installer.VERSION_LTS) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return version, err +} + +func (c *runtimeComponent) InstallVersion(version *gover.Version) error { + return installRuntime(c.DownloadUrl, version) +} + +type aspNetRuntimeComponent struct { + *installer.ComponentBase + DownloadUrl string + VersionsUrl string +} + +func (c *aspNetRuntimeComponent) GetAllVersions() ([]*gover.Version, error) { + latestVersion, err := resolveAspNetRuntimeVersion(c.VersionsUrl, c.GetRequestedVersion()) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return []*gover.Version{version}, err +} + +func (c *aspNetRuntimeComponent) GetLatestVersion() (*gover.Version, error) { + latestVersion, err := resolveAspNetRuntimeVersion(c.VersionsUrl, installer.VERSION_LTS) + if err != nil { + return nil, err + } + version, err := gover.ParseVersionFromRegex(latestVersion, gover.RegexpSemver) + if err != nil { + return nil, err + } + return version, err +} + +func (c *aspNetRuntimeComponent) InstallVersion(version *gover.Version) error { + return installAspNetRuntime(c.DownloadUrl, version) +} + +func installSdk(downloadUrl string, version *gover.Version) error { + return installDotnetBinary(downloadUrl, sdk, "dotnet-sdk", "downloading sdk", version) +} + +func installRuntime(downloadUrl string, version *gover.Version) error { + return installDotnetBinary(downloadUrl, runtime, "dotnet-runtime", "downloading runtime", version) +} + +func installAspNetRuntime(downloadUrl string, version *gover.Version) error { + return installDotnetBinary(downloadUrl, aspNetRuntime, "aspnetcore-runtime", "downloading ASP.NET runtime", version) +} + +func installDotnetBinary(downloadUrl string, product Product, fileName string, progressName string, version *gover.Version) error { + osInfo, err := installer.Tools.System.GetOsInfo() + if err != nil { + return err + } + + // Determine the architecture part of the url + archPart, err := installer.Tools.System.MapArchitecture(map[string]string{ + installer.AMD64: "x64", + installer.ARM64: "arm64", + }) + if err != nil { + return err + } + arch := "" + if osInfo.IsAlpine() { + arch = fmt.Sprintf("linux-musl-%s", archPart) + if err := installer.Tools.System.InstallPackages("ca-certificates", "libgcc", "libssl3", "libstdc++", "zlib", "icu-libs", "icu-data-full", "tzdata", "krb5"); err != nil { + return err + } + } else if osInfo.IsDebian() || osInfo.IsUbuntu() { + arch = fmt.Sprintf("linux-%s", archPart) + } else { + return fmt.Errorf("unsupported OS for .NET install") + } + + // Download file + downloadedFileName := fmt.Sprintf("%s-%s-%s.tar.gz", fileName, version.Raw, arch) + fullUrl := fmt.Sprintf("%s/%s/%s/%s", downloadUrl, product.String(), version.Raw, downloadedFileName) + if err := installer.Tools.Download.ToFile(fullUrl, downloadedFileName, progressName); err != nil { + return err + } + + // Extract it + if err := installer.Tools.Compression.ExtractTarGz(downloadedFileName, os.Getenv("DOTNET_ROOT"), false); err != nil { + return err + } + // Cleanup + if err := os.Remove(downloadedFileName); err != nil { + return err + } + return nil +} + +func resolveSdkVersion(versionsUrl string, requestedVersion string) (string, error) { + return resolveVersion(versionsUrl, requestedVersion, sdk) +} + +func resolveRuntimeVersion(versionsUrl string, requestedVersion string) (string, error) { + return resolveVersion(versionsUrl, requestedVersion, runtime) +} + +func resolveAspNetRuntimeVersion(versionsUrl string, requestedVersion string) (string, error) { + return resolveVersion(versionsUrl, requestedVersion, aspNetRuntime) +} + +func resolveVersion(versionsUrl string, requestedVersion string, product Product) (string, error) { + var latestVersion string + regexChannel := regexp.MustCompile(`^(sts|lts|\d+\.\d+)$`) + if regexChannel.MatchString(strings.ToLower(requestedVersion)) { + // 4.0 works as channel + // 8.0.1xx feature band should work according to docs but does somehow work only for some versions, e.g. 8.0.2xx does not work... + latestVersionUrl := fmt.Sprintf("%s/%s/%s/latest.version", versionsUrl, product.String(), strings.ToUpper(requestedVersion)) + + version, err := installer.Tools.Download.AsString(latestVersionUrl) + if err != nil { + return "", err + } + // Normalize potential CRLF line endings and trim surrounding whitespace/newlines. + normalized := strings.ReplaceAll(version, "\r\n", "\n") + latestVersion = strings.TrimSpace(normalized) + } else { + latestVersion = requestedVersion + } + return latestVersion, nil +} + +type workloadComponent struct { + *installer.ComponentBase + workloads []string +} + +func (c *workloadComponent) InstallVersion(version *gover.Version) error { + arguments := append([]string{"workload", "install", "--temp-dir", "/tmp/dotnet-workload-temp-dir"}, c.workloads...) + if err := execr.Run(true, "dotnet", arguments...); err != nil { + return err + } + // # Clean up + return os.RemoveAll("/tmp/dotnet-workload-temp-dir") +} + +type symlinkComponent struct { + *installer.ComponentBase +} + +func (c *symlinkComponent) InstallVersion(version *gover.Version) error { + return installer.Tools.FileSystem.CreateSymLink(fmt.Sprintf("%s/dotnet", os.Getenv("DOTNET_ROOT")), "/usr/bin/dotnet", false) +} + +type nugetConfigComponent struct { + *installer.ComponentBase + NugetConfigPath string +} + +func (c *nugetConfigComponent) InstallVersion(version *gover.Version) error { + fileContent, err := installer.ReadFileFromUrlOrLocal(c.NugetConfigPath) + if err != nil { + return err + } + // ensure nuget.org source is disabled and we only access sources defined in the provided config file + if err := execr.Run(true, "dotnet", "nuget", "disable", "source", "nuget.org"); err != nil { + return fmt.Errorf("failed to disable nuget.org NuGet source: %w", err) + } + if err := os.MkdirAll("/etc/opt/NuGet", 0755); err != nil { + return err + } + return os.WriteFile("/etc/opt/NuGet/NuGetDefaults.config", fileContent, 0644) +} diff --git a/features/test/dotnet/multiple-sdk.sh b/features/test/dotnet/multiple-sdk.sh new file mode 100755 index 0000000..25d03f1 --- /dev/null +++ b/features/test/dotnet/multiple-sdk.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh" +[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh" + +check_command_exists "dotnet" +check_file_exists /usr/bin/dotnet +check_command_exists /usr/bin/dotnet +check_version "$(dotnet --list-sdks)" "8.0." +check_version "$(dotnet --list-sdks)" "9.0." +check_version "$(dotnet --list-runtimes)" "NETCore.App 6.0." +check_version "$(dotnet --list-runtimes)" "AspNetCore.App 8.0.15" +check_version "$(dotnet workload list)" "wasm-tools-net8" +check_version "$(dotnet workload list)" "maui-android" diff --git a/features/test/dotnet/scenarios.json b/features/test/dotnet/scenarios.json new file mode 100644 index 0000000..d8ea22d --- /dev/null +++ b/features/test/dotnet/scenarios.json @@ -0,0 +1,32 @@ +{ + "sdk-only": { + "build": { + "dockerfile": "Dockerfile", + "options": [ + "--add-host=host.docker.internal:host-gateway" + ] + }, + "features": { + "./dotnet": { + "version": "8.0" + } + } + }, + "multiple-sdk": { + "build": { + "dockerfile": "Dockerfile", + "options": [ + "--add-host=host.docker.internal:host-gateway" + ] + }, + "features": { + "./dotnet": { + "version": "8.0", + "additionalVersions": "9.0.102", + "dotnetRuntimeVersions": "6.0", + "aspNetCoreRuntimeVersions": "8.0.15", + "workloads": "maui-android, wasm-tools-net8" + } + } + } +} \ No newline at end of file diff --git a/features/test/dotnet/sdk-only.sh b/features/test/dotnet/sdk-only.sh new file mode 100755 index 0000000..3e0e347 --- /dev/null +++ b/features/test/dotnet/sdk-only.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +[[ -f "$(dirname "$0")/../functions.sh" ]] && source "$(dirname "$0")/../functions.sh" +[[ -f "$(dirname "$0")/functions.sh" ]] && source "$(dirname "$0")/functions.sh" + +check_command_exists "dotnet" +check_version "$(dotnet --version | head -1)" "8.0." +check_file_exists /usr/bin/dotnet +check_command_exists /usr/bin/dotnet diff --git a/features/test/dotnet/test-images.json b/features/test/dotnet/test-images.json new file mode 100644 index 0000000..12913d7 --- /dev/null +++ b/features/test/dotnet/test-images.json @@ -0,0 +1,5 @@ +[ + "mcr.microsoft.com/devcontainers/base:debian-12", + "mcr.microsoft.com/devcontainers/base:alpine", + "mcr.microsoft.com/devcontainers/base:ubuntu-24.04" +] \ No newline at end of file diff --git a/override-all.env b/override-all.env index a8d5c61..284649e 100644 --- a/override-all.env +++ b/override-all.env @@ -19,6 +19,11 @@ DOCKER_OUT_COMPOSE_DOWNLOAD_URL="" DOCKER_OUT_BUILDX_DOWNLOAD_URL="" DOCKER_OUT_CONFIG_PATH="" +# dotnet +DOTNET_DOWNLOAD_URL="" +DOTNET_VERSIONS_URL="" +DOTNET_NUGET_CONFIG_PATH="" + # git-lfs GIT_LFS_DOWNLOAD_URL=""