Compare commits

...

41 commits

Author SHA1 Message Date
768d4c8398 Удалить .woodpecker.yaml 2024-01-05 04:35:25 +03:00
0a4f338966 Обновить .woodpecker.yaml
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
2024-01-05 04:34:48 +03:00
297c9da2d8 Обновить .woodpecker.yaml 2024-01-05 04:33:19 +03:00
f0abbb5ccd Обновить .woodpecker.yaml 2024-01-05 04:32:46 +03:00
fd426c9ceb Обновить .woodpecker.yaml 2024-01-05 04:31:51 +03:00
dfb174d4e4 Добавить .woodpecker.yaml 2024-01-05 04:31:32 +03:00
Josh Spicer
3ea4d6bbd7
Fix .zprofile is owned by root (#775)
* revert behavior of sourcing .profile in .zprofile

https://github.com/devcontainers/features/pull/736/files#diff-3e71c0a0669a0410f7dd0d8f2b83f3b6bf6b525d3eabd354f19f32822da669fcR460-R463

* add test

* increment version

* create empty .zprofile is not present, and fix test

* temporarily add back .profile to .zprofile (only if file did not exist)
2023-12-07 10:49:18 -08:00
Philipp Wagner
def3a41f57
Fix common-utils installation on RHEL (and friends) (#772)
* Fix indentation in common-utils/main.sh

* common-utils: Fix install error on RHEL

On RHEL (and derivatives) the installation of the common-utils feature
could fail if the feature ran before (i.e., `PACKAGES_ALREADY_INSTALLED`
is set) and if either `INSTALL_ZSH` is false, or zsh was installed
earlier and `ZSH_ALREADY_INSTALLED` is true.

In these cases the script the `package_list` is empty, and `dnf`
terminates with the following error message:

```
usage: dnf install [-c [config file]] [-q] [-v] [--version]
                   [--installroot [path]] [--nodocs] [--noplugins]
                   [--enableplugin [plugin]] [--disableplugin [plugin]]
                   [--releasever RELEASEVER] [--setopt SETOPTS]
                   [--skip-broken] [-h] [--allowerasing] [-b | --nobest] [-C]
                   [-R [minutes]] [-d [debug level]] [--debugsolver]
                   [--showduplicates] [-e ERRORLEVEL] [--obsoletes]
                   [--rpmverbosity [debug level name]] [-y] [--assumeno]
                   [--enablerepo [repo]] [--disablerepo [repo] | --repo
                   [repo]] [--enable | --disable] [-x [package]]
                   [--disableexcludes [repo]] [--repofrompath [repo,path]]
                   [--noautoremove] [--nogpgcheck] [--color COLOR] [--refresh]
                   [-4] [-6] [--destdir DESTDIR] [--downloadonly]
                   [--comment COMMENT] [--bugfix] [--enhancement]
                   [--newpackage] [--security] [--advisory ADVISORY]
                   [--bz BUGZILLA] [--cve CVES]
                   [--sec-severity {Critical,Important,Moderate,Low}]
                   [--forcearch ARCH]
                   PACKAGE [PACKAGE ...]
dnf install: error: the following arguments are required: PACKAGE
```

Fix the problem by running `dnf` only with a non-zero `package_list`.
2023-12-05 17:02:58 -08:00
Dev containers Bot
d53b9d1816
[Updates] Automated vendor dotnet-install script (#750)
* Automated dotnet-install script update

* Bump version

---------

Co-authored-by: github-actions <github-actions@github.com>
2023-11-14 09:56:03 -08:00
Dev containers Bot
710103a638
Automated documentation update (#738)
Automated documentation update [skip ci]

Co-authored-by: github-actions <github-actions@github.com>
2023-10-30 10:20:06 -07:00
Oscar
7324b6d03c
Remove rebornix.Ruby extension (#737)
Remove the deprecated rebornix.Ruby extension from the list of installed extensions.
2023-10-30 10:16:28 -07:00
Samruddhi Khandale
d4ecc7be06
Update Features - Remove use of get_common_setting (#735) 2023-10-27 15:47:44 -07:00
Samruddhi Khandale
719901a1b4
[common-utils] Bug fix: zsh missing rc snippet (#736)
* [common-utils] Bug fix: zsp missing rc snippet

* condion rc snippet

* fix bug

* remove debugging step
2023-10-27 15:47:26 -07:00
Dev containers Bot
4c2c9f7b95
Automated documentation update (#732)
Automated documentation update [skip ci]

Co-authored-by: github-actions <github-actions@github.com>
2023-10-23 16:14:35 -07:00
Lyon Till
e645120a39
Add PowerShell extension (#731)
* Add PowerShell extension

* Bump feature version
2023-10-23 16:08:27 -07:00
Richard Höchenberger
1155a9906b
Try connecting to Ubuntu keyserver on port 80 if default HKP port fails (#722)
* Try Ubuntu keyserver on port 80 if default HKP port fails

Closes #719
Closes #717

* Bump feature versions

---------

Co-authored-by: Samruddhi Khandale <skhandale@microsoft.com>
2023-10-17 23:45:04 +00:00
Samruddhi Khandale
36a3604b90
[Python] - Support 3.12 (#726) 2023-10-17 15:05:01 -07:00
Samruddhi Khandale
4fca96b5e8
[Docker-in-docker] Updates docker-init scripts (#715)
* updates

* version bump
2023-10-05 11:27:01 -07:00
Jan-Marco Edelmann
0d7ad7c5fc
Configure npm proxy when proxy environmentals exist (#712) 2023-10-04 15:09:35 -07:00
Alexander Smolyakov
90a9b1d604
[terraform] Update retry logic to handle DNS-related errors more gracefully (#713)
* [terraform] Update retry logic

- Add `dnsutils` package to dependencies
- Update retry logic to handle DNS-related errors more gracefully

* Update install.sh

* Bump version
2023-10-04 10:00:11 -07:00
Luciana Abud
c3f9105048
Remove deprecated Python settings (#625)
* Remove deprecated Python settings

* No longer install Python linters and formatters

* Revert "No longer install Python linters and formatters"

This reverts commit 57c7bec14d.

* Bump minor version
2023-10-02 08:59:25 -07:00
Samruddhi Khandale
04cb7d1e79
Docker-in-docker: Updates docker-init with "pkill" (#711)
* Docker-in-docker: Updates docker-init with "pkill"

* pkill only if container didn't start at first

* pkill with condition

* add back sudo_if
2023-09-29 12:02:54 -07:00
Samruddhi Khandale
cf92754bf4
Automated documentation update (#709)
Automated documentation update [skip ci]

Co-authored-by: github-actions <github-actions@github.com>
2023-09-29 11:50:09 -07:00
Steven
803ec6e0e5
Bump the dotnet version after a script update (#707)
* Bump the dotnet version after a script update

* Forgot to add file

* Keep 4 spaces indentation
2023-09-28 10:50:20 -07:00
Samruddhi Khandale
0599c63f8f
[Updates] Automated vendor dotnet-install script (#698)
* Automated dotnet-install script update

* version bump

---------

Co-authored-by: github-actions <github-actions@github.com>
2023-09-28 10:08:39 -07:00
Samruddhi Khandale
3aad9a72f4
Terraform - Adds licensing information (#697)
* Terraform - Adds licensing information

* Update src/terraform/NOTES.md

Co-authored-by: Brigit Murtaugh <brigit.murtaugh@microsoft.com>

---------

Co-authored-by: Brigit Murtaugh <brigit.murtaugh@microsoft.com>
2023-09-28 09:59:04 -07:00
Masahiro Wada
243671eece
Enhancements in CUDA Support (#699)
* add cuda tookit installing support

* add nvcc installing support

* add libcudnn8-dev installing support

* add new cuda versions adn cudnn versions

* add specificaitons of new option

* remove installNvcc

installNvcc process is included in installToolkit

* add tests for new nvidia-cuda options

* fix wrong scenario name

* Revert "add specificaitons of new option"

This reverts commit 0dcdb92406.

README.md should be generated by CI workflow.

* make cudnn-dev installation isolated

* dump minor version to 1.1.0
2023-09-27 12:17:31 -07:00
Alexander Smolyakov
0d2fc3ad5b
[python] Change the sudo_if function to avoid issues with Bash (#694)
* [python] Updated `sudo_if` function

- Remove double quotes to avoid issues with string tokenization

* Add test scenario

* Revert "Add test scenario"

This reverts commit 9e62a3797a.

* Update `jupyterlab git` package name

* Bump feature version

* Test: Install jupyterlab under root user

* Refactor changes

* Bump patch version

* Address review points
2023-09-27 11:51:26 -07:00
Pablo Ulloa
e7f7d194d6
[common-utils] fix bug when user home is custom by base docker (#703)
* [common-utils] fix bug when user home is custom by base docker

* bumped minor version

* reduced conditions to improve readability
2023-09-27 11:45:29 -07:00
Pablo Ulloa
9852ca8888
[java] fix version regex to support graalce jdk 21 (#701)
* [java] fixed feature test scenario check

* [java] support for non conentional version

* Update src/java/install.sh to not include whitespaces

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* bumped patch version

---------

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>
2023-09-27 09:26:53 -07:00
Steven
3a22c41940
Configure environment for update-dotnet-install-script.yml (#693) 2023-09-19 09:58:33 -07:00
Samruddhi Khandale
f92f7ba7ae
[common-utils]: Bug fix: Install zsh on an image previously built with "installZsh:false" (#649)
* [common-utils]: Bug fix: Installs zsh on an image built with installZsh:false

* version bump

* nit: fix merge conflicts

* Version bump
2023-09-14 09:05:50 -07:00
Samruddhi Khandale
97eea5deee
[Go] Update proposals for the "version" option (#654)
Update devcontainer-feature.json
2023-09-13 08:42:41 -07:00
Samruddhi Khandale
a771e8cb42
[Docker-in-docker] Support bookworm (#661)
* [Docker-in-docker] Support bookworm

* Update devcontainer-feature.json
2023-09-11 11:32:53 -07:00
Samruddhi Khandale
8ad7c749b3
Automated documentation update (#688)
Automated documentation update [skip ci]

Co-authored-by: github-actions <github-actions@github.com>
2023-09-11 11:28:49 -07:00
Steven
96c1eea40f
Use dotnet-install.sh in .NET feature (#628)
* Use dotnet-install.sh in .NET feature

* Use latest.version files

* Cleanup runtime args

* Use latest.version files in tests as well

* Improve tests, remove code duplication

* Add stderr helper

* Validate version inputs

* Use suggested description

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Shorter version description

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Shorter version description

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Clean up apt lists

* Verify 7.0 is latest

* Fix PATH, add test for .NET global tools

* Include a copy of dotnet-install.sh in the Feature

* Configure useful env variables

* Use stringly typed booleans

* Keep imperative writing style in option hints

* Update maintainers

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Move dotnet-install.sh into a vendor directory

* Refactor variables

* Amend

* Amend 2

* Use default options from devcontainer-feature.json

* Add back variables

* Fix shellchek warning in fetch_latest_sdk_version

* Inline install_version function

* Fix ShellCheck warnings

* Improve CSV parsing

* Default to latest when configuring an empty version

* Add support for runtime-only configurations

* Move 'none' check higher up

* Deduplicate helper functions, sort into files

* Address the user more directly in NOTES

* Remove unnecessary defaults

* Replace feature -> Feature

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Add update-dotnet-install-script workflow

* Apply suggestions from code review

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>

* Don't skip ci for automated script update

---------

Co-authored-by: Samruddhi Khandale <samruddhikhandale@github.com>
2023-09-11 11:16:24 -07:00
eitsupi
038bed3d58
ci: remove ubuntu:bionic from test matrixes (#677)
ci: remove ubuntu:bionic from test matrixes
2023-08-29 09:32:46 -07:00
Samruddhi Khandale
96bff00970
Automated documentation update (#676)
Automated documentation update [skip ci]

Co-authored-by: github-actions <github-actions@github.com>
2023-08-28 15:15:43 -07:00
eitsupi
95a169b9ef
[node] Install the latest version of nvm by default (#673)
feat(node): install the latest nvm by default
2023-08-28 14:26:30 -07:00
naturedamends
0668db5ede
Commonutils: chown: cannot access '/home/vscode/.zshrc': No such file or directory (#662)
* Update main.sh

* Update devcontainer-feature.json

* Test install as non root user.

---------

Co-authored-by: Samruddhi Khandale <skhandale@microsoft.com>
2023-08-25 16:39:32 -07:00
Samruddhi Khandale
0cf690ee14
[docker in docker] Adds retries for docker daemon startup and cgroup nesting (#669)
* adds retry logic

* adds stress test workflow

* Adds cgroup retries

* update workflow

* move sleep cmd

* update test

* cleanup
2023-08-24 12:31:40 -07:00
115 changed files with 3751 additions and 1070 deletions

View file

@ -1,18 +1,17 @@
{ {
"image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:0-18", "image": "mcr.microsoft.com/devcontainers/javascript-node:1-18",
"remoteUser": "node",
"customizations": {
"vscode": {
"extensions": [
"mads-hartmann.bash-ide-vscode"
]
}
},
"features": { "features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {} "ghcr.io/devcontainers/features/docker-in-docker:2": {}
}, },
"postCreateCommand": "npm install -g @devcontainers/cli", "postCreateCommand": "npm install -g @devcontainers/cli",
"hostRequirements": { "hostRequirements": {
"cpus": 4 "cpus": 4
},
"customizations": {
"vscode": {
"extensions": [
"mads-hartmann.bash-ide-vscode"
]
}
} }
} }

View file

@ -0,0 +1,37 @@
name: "Stress test - Docker in Docker"
on:
pull_request:
paths:
- 'src/docker-in-docker/**'
workflow_dispatch:
jobs:
test:
strategy:
matrix:
test-pass: [ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50 ]
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Generating tests for 'docker-in-docker' which validates if docker daemon is running"
run: devcontainer features test --skip-scenarios -f docker-in-docker -i mcr.microsoft.com/devcontainers/base:ubuntu .
test-onCreate:
strategy:
matrix:
test-pass: [ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50 ]
fail-fast: false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: "Install latest devcontainer CLI"
run: npm install -g @devcontainers/cli
- name: "Generating tests for 'docker-in-docker' which validates if docker daemon is available within 'onCreateCommand'"
run: devcontainer features test -f docker-in-docker --skip-autogenerated --filter "docker_with_on_create_command"

View file

@ -42,7 +42,6 @@ jobs:
] ]
baseImage: baseImage:
[ [
"ubuntu:bionic",
"ubuntu:focal", "ubuntu:focal",
"ubuntu:jammy", "ubuntu:jammy",
"debian:11", "debian:11",

View file

@ -49,7 +49,6 @@ jobs:
features: ${{ fromJSON(needs.detect-changes.outputs.features) }} features: ${{ fromJSON(needs.detect-changes.outputs.features) }}
baseImage: baseImage:
[ [
"ubuntu:bionic",
"ubuntu:focal", "ubuntu:focal",
"ubuntu:jammy", "ubuntu:jammy",
"debian:11", "debian:11",

View file

@ -0,0 +1,52 @@
name: "Updates vendor 'dotnet-install' script"
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Runs every Sunday at midnight UTC (adjust as needed)
jobs:
fetch-latest-dotnet-install:
runs-on: ubuntu-latest
environment: documentation # grants access to secrets.PAT, for creating pull requests
steps:
- uses: actions/checkout@v3
- name: Run fetch-latest-dotnet-install.sh
run: src/dotnet/scripts/fetch-latest-dotnet-install.sh
- name: Create a PR for dotnet-install.sh
id: push_image_info
env:
GITHUB_TOKEN: ${{ secrets.PAT }}
run: |
set -e
echo "Start."
# Configure git and Push updates
git config --global user.email github-actions@github.com
git config --global user.name github-actions
git config pull.rebase false
branch=automated-script-update-$GITHUB_RUN_ID
git checkout -b $branch
message='[Updates] Automated vendor 'dotnet-install' script'
# Add / update and commit
git add src/dotnet/scripts/vendor/dotnet-install.sh
git commit -m 'Automated dotnet-install script update' || export NO_UPDATES=true
# Bump version and push
if [ "$NO_UPDATES" != "true" ] ; then
echo "$(jq --indent 4 '.version = (.version | split(".") | map(tonumber) | .[2] += 1 | join("."))' src/dotnet/devcontainer-feature.json)" > src/dotnet/devcontainer-feature.json
git add src/dotnet/devcontainer-feature.json
git commit -m 'Bump version'
git push origin "$branch"
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
/repos/${GITHUB_REPOSITORY}/pulls \
-f title="$message" \
-f body="$message" \
-f head="$branch" \
-f base="$GITHUB_REF_NAME"
fi

View file

@ -1,6 +1,6 @@
{ {
"id": "aws-cli", "id": "aws-cli",
"version": "1.0.6", "version": "1.0.7",
"name": "AWS CLI", "name": "AWS CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/aws-cli", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/aws-cli",
"description": "Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "description": "Installs the AWS CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",

View file

@ -50,21 +50,6 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1 exit 1
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
apt_get_update() apt_get_update()
{ {
echo "Running apt-get update..." echo "Running apt-get update..."
@ -89,9 +74,6 @@ check_packages curl ca-certificates gnupg2 dirmngr unzip
verify_aws_cli_gpg_signature() { verify_aws_cli_gpg_signature() {
local filePath=$1 local filePath=$1
local sigFilePath=$2 local sigFilePath=$2
get_common_setting AWSCLI_GPG_KEY
get_common_setting AWSCLI_GPG_KEY_MATERIAL true
local awsGpgKeyring=aws-cli-public-key.gpg local awsGpgKeyring=aws-cli-public-key.gpg
echo "${AWSCLI_GPG_KEY_MATERIAL}" | gpg --dearmor > "./${awsGpgKeyring}" echo "${AWSCLI_GPG_KEY_MATERIAL}" | gpg --dearmor > "./${awsGpgKeyring}"

View file

@ -1,6 +1,6 @@
{ {
"id": "azure-cli", "id": "azure-cli",
"version": "1.2.1", "version": "1.2.2",
"name": "Azure CLI", "name": "Azure CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/azure-cli", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/azure-cli",
"description": "Installs the Azure CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "description": "Installs the Azure CLI along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",

View file

@ -32,21 +32,6 @@ fi
echo "Effective REMOTE_USER: ${_REMOTE_USER}" echo "Effective REMOTE_USER: ${_REMOTE_USER}"
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
apt_get_update() apt_get_update()
{ {
echo "Running apt-get update..." echo "Running apt-get update..."
@ -110,7 +95,6 @@ install_using_apt() {
# Install dependencies # Install dependencies
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
# Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install # Import key safely (new 'signed-by' method rather than deprecated apt-key approach) and install
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/azure-cli/ ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/azure-cli.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/azure-cli/ ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/azure-cli.list
apt-get update apt-get update

View file

@ -1,6 +1,6 @@
{ {
"id": "common-utils", "id": "common-utils",
"version": "2.1.1", "version": "2.3.1",
"name": "Common Utilities", "name": "Common Utilities",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/common-utils", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/common-utils",
"description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.", "description": "Installs a set of common command line utilities, Oh My Zsh!, and sets up a non-root user.",

View file

@ -28,7 +28,10 @@ install_debian_packages() {
# Ensure apt is in non-interactive to avoid prompts # Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
local package_list="apt-utils \ local package_list=""
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
package_list="${package_list} \
apt-utils \
openssh-client \ openssh-client \
gnupg2 \ gnupg2 \
dirmngr \ dirmngr \
@ -70,6 +73,34 @@ install_debian_packages() {
manpages-dev \ manpages-dev \
init-system-helpers" init-system-helpers"
# Include libssl1.1 if available
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
package_list="${package_list} libssl1.1"
fi
# Include libssl3 if available
if [[ ! -z $(apt-cache --names-only search ^libssl3$) ]]; then
package_list="${package_list} libssl3"
fi
# Include appropriate version of libssl1.0.x if available
local libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
if [ "$(echo "$libssl_package" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
# Debian 9
package_list="${package_list} libssl1.0.2"
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
# Ubuntu 18.04
package_list="${package_list} libssl1.0.0"
fi
fi
# Include git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
package_list="${package_list} git"
fi
fi
# Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian # Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian
if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then
# Bring in variables from /etc/os-release like VERSION_CODENAME # Bring in variables from /etc/os-release like VERSION_CODENAME
@ -88,33 +119,6 @@ install_debian_packages() {
package_list="${package_list} manpages-posix manpages-posix-dev" package_list="${package_list} manpages-posix manpages-posix-dev"
fi fi
# Include libssl1.1 if available
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
package_list="${package_list} libssl1.1"
fi
# Include libssl3 if available
if [[ ! -z $(apt-cache --names-only search ^libssl3$) ]]; then
package_list="${package_list} libssl3"
fi
# Include appropriate version of libssl1.0.x if available
local libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
if [ "$(echo "$libssl_package" | grep -o 'libssl1\.0\.[0-9]:' | uniq | sort | wc -l)" -eq 0 ]; then
if [[ ! -z $(apt-cache --names-only search ^libssl1.0.2$) ]]; then
# Debian 9
package_list="${package_list} libssl1.0.2"
elif [[ ! -z $(apt-cache --names-only search ^libssl1.0.0$) ]]; then
# Ubuntu 18.04
package_list="${package_list} libssl1.0.0"
fi
fi
# Include git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
package_list="${package_list} git"
fi
# Install the list of packages # Install the list of packages
echo "Packages to verify are installed: ${package_list}" echo "Packages to verify are installed: ${package_list}"
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -139,6 +143,8 @@ install_debian_packages() {
LOCALE_ALREADY_SET="true" LOCALE_ALREADY_SET="true"
fi fi
PACKAGES_ALREADY_INSTALLED="true"
# Clean up # Clean up
apt-get -y clean apt-get -y clean
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -146,59 +152,69 @@ install_debian_packages() {
# RedHat / RockyLinux / CentOS / Fedora packages # RedHat / RockyLinux / CentOS / Fedora packages
install_redhat_packages() { install_redhat_packages() {
local package_list="\ local package_list=""
gawk \ local remove_epel="false"
openssh-clients \
gnupg2 \
iproute \
procps \
lsof \
net-tools \
psmisc \
wget \
ca-certificates \
rsync \
unzip \
zip \
nano \
vim-minimal \
less \
jq \
openssl-libs \
krb5-libs \
libicu \
zlib \
sudo \
sed \
grep \
which \
man-db \
strace"
local install_cmd=dnf local install_cmd=dnf
if ! type dnf > /dev/null 2>&1; then if ! type dnf > /dev/null 2>&1; then
install_cmd=yum install_cmd=yum
fi fi
# rockylinux:9 installs 'curl-minimal' which clashes with 'curl' if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
# Install 'curl' for every OS except this rockylinux:9 package_list="${package_list} \
if [[ "${ID}" = "rocky" ]] && [[ "${VERSION}" != *"9."* ]]; then gawk \
package_list="${package_list} curl" openssh-clients \
fi gnupg2 \
iproute \
procps \
lsof \
net-tools \
psmisc \
wget \
ca-certificates \
rsync \
unzip \
zip \
nano \
vim-minimal \
less \
jq \
openssl-libs \
krb5-libs \
libicu \
zlib \
sudo \
sed \
grep \
which \
man-db \
strace"
# Install OpenSSL 1.0 compat if needed # rockylinux:9 installs 'curl-minimal' which clashes with 'curl'
if ${install_cmd} -q list compat-openssl10 >/dev/null 2>&1; then # Install 'curl' for every OS except this rockylinux:9
package_list="${package_list} compat-openssl10" if [[ "${ID}" = "rocky" ]] && [[ "${VERSION}" != *"9."* ]]; then
fi package_list="${package_list} curl"
fi
# Install lsb_release if available # Install OpenSSL 1.0 compat if needed
if ${install_cmd} -q list redhat-lsb-core >/dev/null 2>&1; then if ${install_cmd} -q list compat-openssl10 >/dev/null 2>&1; then
package_list="${package_list} redhat-lsb-core" package_list="${package_list} compat-openssl10"
fi fi
# Install git if not already installed (may be more recent than distro version) # Install lsb_release if available
if ! type git > /dev/null 2>&1; then if ${install_cmd} -q list redhat-lsb-core >/dev/null 2>&1; then
package_list="${package_list} git" package_list="${package_list} redhat-lsb-core"
fi
# Install git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
package_list="${package_list} git"
fi
# Install EPEL repository if needed (required to install 'jq' for CentOS)
if ! ${install_cmd} -q list jq >/dev/null 2>&1; then
${install_cmd} -y install epel-release
remove_epel="true"
fi
fi fi
# Install zsh if needed # Install zsh if needed
@ -206,15 +222,10 @@ install_redhat_packages() {
package_list="${package_list} zsh" package_list="${package_list} zsh"
fi fi
# Install EPEL repository if needed (required to install 'jq' for CentOS) if [ -n "${package_list}" ]; then
local remove_epel="false" ${install_cmd} -y install ${package_list}
if ! ${install_cmd} -q list jq >/dev/null 2>&1; then
${install_cmd} -y install epel-release
remove_epel="true"
fi fi
${install_cmd} -y install ${package_list}
# Get to latest versions of all packages # Get to latest versions of all packages
if [ "${UPGRADE_PACKAGES}" = "true" ]; then if [ "${UPGRADE_PACKAGES}" = "true" ]; then
${install_cmd} upgrade -y ${install_cmd} upgrade -y
@ -223,63 +234,70 @@ install_redhat_packages() {
if [[ "${remove_epel}" = "true" ]]; then if [[ "${remove_epel}" = "true" ]]; then
${install_cmd} -y remove epel-release ${install_cmd} -y remove epel-release
fi fi
PACKAGES_ALREADY_INSTALLED="true"
} }
# Alpine Linux packages # Alpine Linux packages
install_alpine_packages() { install_alpine_packages() {
apk update apk update
apk add --no-cache \
openssh-client \
gnupg \
procps \
lsof \
htop \
net-tools \
psmisc \
curl \
wget \
rsync \
ca-certificates \
unzip \
zip \
nano \
vim \
less \
jq \
libgcc \
libstdc++ \
krb5-libs \
libintl \
libssl1.1 \
lttng-ust \
tzdata \
userspace-rcu \
zlib \
sudo \
coreutils \
sed \
grep \
which \
ncdu \
shadow \
strace
# Install man pages - package name varies between 3.12 and earlier versions if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
if apk info man > /dev/null 2>&1; then apk add --no-cache \
apk add --no-cache man man-pages openssh-client \
else gnupg \
apk add --no-cache mandoc man-pages procps \
fi lsof \
htop \
net-tools \
psmisc \
curl \
wget \
rsync \
ca-certificates \
unzip \
zip \
nano \
vim \
less \
jq \
libgcc \
libstdc++ \
krb5-libs \
libintl \
libssl1.1 \
lttng-ust \
tzdata \
userspace-rcu \
zlib \
sudo \
coreutils \
sed \
grep \
which \
ncdu \
shadow \
strace
# Install git if not already installed (may be more recent than distro version) # Install man pages - package name varies between 3.12 and earlier versions
if ! type git > /dev/null 2>&1; then if apk info man > /dev/null 2>&1; then
apk add --no-cache git apk add --no-cache man man-pages
else
apk add --no-cache mandoc man-pages
fi
# Install git if not already installed (may be more recent than distro version)
if ! type git > /dev/null 2>&1; then
apk add --no-cache git
fi
fi fi
# Install zsh if needed # Install zsh if needed
if [ "${INSTALL_ZSH}" = "true" ] && ! type zsh > /dev/null 2>&1; then if [ "${INSTALL_ZSH}" = "true" ] && ! type zsh > /dev/null 2>&1; then
apk add --no-cache zsh apk add --no-cache zsh
fi fi
PACKAGES_ALREADY_INSTALLED="true"
} }
# ****************** # ******************
@ -318,20 +336,17 @@ else
fi fi
# Install packages for appropriate OS # Install packages for appropriate OS
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then case "${ADJUSTED_ID}" in
case "${ADJUSTED_ID}" in "debian")
"debian") install_debian_packages
install_debian_packages ;;
;; "rhel")
"rhel") install_redhat_packages
install_redhat_packages ;;
;; "alpine")
"alpine") install_alpine_packages
install_alpine_packages ;;
;; esac
esac
PACKAGES_ALREADY_INSTALLED="true"
fi
# If in automatic mode, determine if a user already exists, if not use vscode # If in automatic mode, determine if a user already exists, if not use vscode
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
@ -394,6 +409,9 @@ fi
if [ "${USERNAME}" = "root" ]; then if [ "${USERNAME}" = "root" ]; then
user_home="/root" user_home="/root"
# Check if user already has a home directory other than /home/${USERNAME}
elif [ "/home/${USERNAME}" != $( getent passwd $USERNAME | cut -d: -f6 ) ]; then
user_home=$( getent passwd $USERNAME | cut -d: -f6 )
else else
user_home="/home/${USERNAME}" user_home="/home/${USERNAME}"
if [ ! -d "${user_home}" ]; then if [ ! -d "${user_home}" ]; then
@ -405,6 +423,7 @@ fi
# Restore user .bashrc / .profile / .zshrc defaults from skeleton file if it doesn't exist or is empty # Restore user .bashrc / .profile / .zshrc defaults from skeleton file if it doesn't exist or is empty
possible_rc_files=( ".bashrc" ".profile" ) possible_rc_files=( ".bashrc" ".profile" )
[ "$INSTALL_OH_MY_ZSH_CONFIG" == "true" ] && possible_rc_files+=('.zshrc') [ "$INSTALL_OH_MY_ZSH_CONFIG" == "true" ] && possible_rc_files+=('.zshrc')
[ "$INSTALL_ZSH" == "true" ] && possible_rc_files+=('.zprofile')
for rc_file in "${possible_rc_files[@]}"; do for rc_file in "${possible_rc_files[@]}"; do
if [ -f "/etc/skel/${rc_file}" ]; then if [ -f "/etc/skel/${rc_file}" ]; then
if [ ! -e "${user_home}/${rc_file}" ] || [ ! -s "${user_home}/${rc_file}" ]; then if [ ! -e "${user_home}/${rc_file}" ] || [ ! -s "${user_home}/${rc_file}" ]; then
@ -440,13 +459,19 @@ fi
# Optionally configure zsh and Oh My Zsh! # Optionally configure zsh and Oh My Zsh!
if [ "${INSTALL_ZSH}" = "true" ]; then if [ "${INSTALL_ZSH}" = "true" ]; then
if [ ! -f "${user_home}/.zprofile" ]; then
touch "${user_home}/.zprofile"
echo 'source $HOME/.profile' >> "${user_home}/.zprofile" # TODO: Reconsider adding '.profile' to '.zprofile'
chown ${USERNAME}:${group_name} "${user_home}/.zprofile"
fi
if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
if [ "${ADJUSTED_ID}" = "rhel" ]; then if [ "${ADJUSTED_ID}" = "rhel" ]; then
global_rc_path="/etc/zshrc" global_rc_path="/etc/zshrc"
else else
global_rc_path="/etc/zsh/zshrc" global_rc_path="/etc/zsh/zshrc"
fi fi
cat "${FEATURE_DIR}/scripts/rc_snippet.sh" >> /etc/zshrc cat "${FEATURE_DIR}/scripts/rc_snippet.sh" >> ${global_rc_path}
ZSH_ALREADY_INSTALLED="true" ZSH_ALREADY_INSTALLED="true"
fi fi
@ -500,7 +525,7 @@ if [ "${INSTALL_ZSH}" = "true" ]; then
copy_to_user_files=("${oh_my_install_dir}") copy_to_user_files=("${oh_my_install_dir}")
[ -f "$user_rc_file" ] && copy_to_user_files+=("$user_rc_file") [ -f "$user_rc_file" ] && copy_to_user_files+=("$user_rc_file")
cp -rf "${copy_to_user_files[@]}" /root cp -rf "${copy_to_user_files[@]}" /root
chown -R ${USERNAME}:${group_name} "${oh_my_install_dir}" "${user_rc_file}" chown -R ${USERNAME}:${group_name} "${copy_to_user_files[@]}"
fi fi
fi fi
fi fi

View file

@ -1,6 +1,6 @@
{ {
"id": "docker-in-docker", "id": "docker-in-docker",
"version": "2.3.1", "version": "2.7.1",
"name": "Docker (Docker-in-Docker)", "name": "Docker (Docker-in-Docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-in-docker", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-in-docker",
"description": "Create child containers *inside* a container, independent from the host's docker instance. Installs Docker extension in the container along with needed CLIs.", "description": "Create child containers *inside* a container, independent from the host's docker instance. Installs Docker extension in the container along with needed CLIs.",

View file

@ -16,8 +16,8 @@ DOCKER_DEFAULT_ADDRESS_POOL="${DOCKERDEFAULTADDRESSPOOL}"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}" USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}" INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy" DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy"
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy" DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy"
# Default: Exit on any failure. # Default: Exit on any failure.
set -e set -e
@ -57,21 +57,6 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
apt_get_update() apt_get_update()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
@ -137,8 +122,6 @@ architecture="$(dpkg --print-architecture)"
# Check if distro is supported # Check if distro is supported
if [ "${USE_MOBY}" = "true" ]; then if [ "${USE_MOBY}" = "true" ]; then
# 'get_common_setting' allows attribute to be updated remotely
get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}"
@ -146,7 +129,6 @@ if [ "${USE_MOBY}" = "true" ]; then
fi fi
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
else else
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
@ -177,7 +159,6 @@ if [ "${USE_MOBY}" = "true" ]; then
cli_package_name="moby-cli" cli_package_name="moby-cli"
# Import key safely and import Microsoft apt repo # Import key safely and import Microsoft apt repo
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
else else
@ -363,11 +344,10 @@ tee -a /usr/local/share/docker-init.sh > /dev/null \
<< 'EOF' << 'EOF'
dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL} $(cat << 'INNEREOF' dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAULT_ADDRESS_POOL=${DOCKER_DEFAULT_ADDRESS_POOL} $(cat << 'INNEREOF'
# explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly # explicitly remove dockerd and containerd PID file to ensure that it can start properly if it was stopped uncleanly
# ie: docker kill <ID>
find /run /var/run -iname 'docker*.pid' -delete || : find /run /var/run -iname 'docker*.pid' -delete || :
find /run /var/run -iname 'container*.pid' -delete || : find /run /var/run -iname 'container*.pid' -delete || :
## Dind wrapper script from docker team, adapted to a function # -- Start: dind wrapper script --
# Maintained: https://github.com/moby/moby/blob/master/hack/dind # Maintained: https://github.com/moby/moby/blob/master/hack/dind
export container=docker export container=docker
@ -384,31 +364,52 @@ dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAU
mount -t tmpfs none /tmp mount -t tmpfs none /tmp
fi fi
# cgroup v2: enable nesting set_cgroup_nesting()
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then {
# move the processes from the root group to the /init group, # cgroup v2: enable nesting
# otherwise writing subtree_control fails with EBUSY. if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
# An error during moving non-existent process (i.e., "cat") is ignored. # move the processes from the root group to the /init group,
mkdir -p /sys/fs/cgroup/init # otherwise writing subtree_control fails with EBUSY.
xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || : # An error during moving non-existent process (i.e., "cat") is ignored.
# enable controllers mkdir -p /sys/fs/cgroup/init
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \ xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || :
> /sys/fs/cgroup/cgroup.subtree_control # enable controllers
fi sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \
## Dind wrapper over. > /sys/fs/cgroup/cgroup.subtree_control
fi
}
# Set cgroup nesting, retrying if necessary
retry_cgroup_nesting=0
until [ "${retry_cgroup_nesting}" -eq "5" ];
do
set +e
set_cgroup_nesting
if [ $? -ne 0 ]; then
echo "(*) cgroup v2: Failed to enable nesting, retrying..."
else
break
fi
retry_cgroup_nesting=`expr $retry_cgroup_nesting + 1`
set -e
done
# -- End: dind wrapper script --
# Handle DNS # Handle DNS
set +e set +e
cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' cat /etc/resolv.conf | grep -i 'internal.cloudapp.net' > /dev/null 2>&1
if [ $? -eq 0 ] && [ "${AZURE_DNS_AUTO_DETECTION}" = "true" ] if [ $? -eq 0 ] && [ "${AZURE_DNS_AUTO_DETECTION}" = "true" ]
then then
echo "Setting dockerd Azure DNS." echo "Setting dockerd Azure DNS."
CUSTOMDNS="--dns 168.63.129.16" CUSTOMDNS="--dns 168.63.129.16"
else else
echo "Not setting dockerd DNS manually." echo "Not setting dockerd DNS manually."
CUSTOMDNS="" CUSTOMDNS=""
fi fi
set -e set -e
if [ -z "$DOCKER_DEFAULT_ADDRESS_POOL" ] if [ -z "$DOCKER_DEFAULT_ADDRESS_POOL" ]
@ -423,12 +424,49 @@ dockerd_start="AZURE_DNS_AUTO_DETECTION=${AZURE_DNS_AUTO_DETECTION} DOCKER_DEFAU
INNEREOF INNEREOF
)" )"
# Start using sudo if not invoked as root sudo_if() {
if [ "$(id -u)" -ne 0 ]; then COMMAND="$*"
sudo /bin/sh -c "${dockerd_start}"
else if [ "$(id -u)" -ne 0 ]; then
eval "${dockerd_start}" sudo $COMMAND
fi else
$COMMAND
fi
}
retry_docker_start_count=0
docker_ok="false"
until [ "${docker_ok}" = "true" ] || [ "${retry_docker_start_count}" -eq "5" ];
do
# Start using sudo if not invoked as root
if [ "$(id -u)" -ne 0 ]; then
sudo /bin/sh -c "${dockerd_start}"
else
eval "${dockerd_start}"
fi
retry_count=0
until [ "${docker_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
do
sleep 1s
set +e
docker info > /dev/null 2>&1 && docker_ok="true"
set -e
retry_count=`expr $retry_count + 1`
done
if [ "${docker_ok}" != "true" ] && [ "${retry_docker_start_count}" != "4" ]; then
echo "(*) Failed to start docker, retrying..."
set +e
sudo_if pkill dockerd
sudo_if pkill containerd
set -e
fi
retry_docker_start_count=`expr $retry_docker_start_count + 1`
done
# Execute whatever commands were passed in (if any). This allows us # Execute whatever commands were passed in (if any). This allows us
# to set this script to ENTRYPOINT while still executing the default CMD. # to set this script to ENTRYPOINT while still executing the default CMD.

View file

@ -1,6 +1,6 @@
{ {
"id": "docker-outside-of-docker", "id": "docker-outside-of-docker",
"version": "1.3.0", "version": "1.3.1",
"name": "Docker (docker-outside-of-docker)", "name": "Docker (docker-outside-of-docker)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
"description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.", "description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",

View file

@ -18,8 +18,8 @@ USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}" INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc" MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy" DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal jammy"
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy" DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="bookworm buster bullseye bionic focal hirsute impish jammy"
set -e set -e
@ -48,21 +48,6 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
apt_get_update() apt_get_update()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
@ -129,8 +114,6 @@ architecture="$(dpkg --print-architecture)"
# Check if distro is supported # Check if distro is supported
if [ "${USE_MOBY}" = "true" ]; then if [ "${USE_MOBY}" = "true" ]; then
# 'get_common_setting' allows attribute to be updated remotely
get_common_setting DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}"
@ -138,7 +121,6 @@ if [ "${USE_MOBY}" = "true" ]; then
fi fi
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'" echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
else else
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution" err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
@ -153,7 +135,6 @@ if [ "${USE_MOBY}" = "true" ]; then
cli_package_name="moby-cli" cli_package_name="moby-cli"
# Import key safely and import Microsoft apt repo # Import key safely and import Microsoft apt repo
get_common_setting MICROSOFT_GPG_KEYS_URI
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
else else

View file

@ -1,4 +1,71 @@
## Configuration examples
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. (The SDK includes all runtimes so this configuration is only useful if you 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 ## OS Support

View file

@ -1,13 +1,13 @@
# Dotnet CLI (dotnet) # Dotnet CLI (dotnet)
Installs the .NET CLI. Provides option of installing sdk or runtime, and option of versions to install. Uses latest version of .NET sdk as defaults to install. 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.
## Example Usage ## Example Usage
```json ```json
"features": { "features": {
"ghcr.io/devcontainers/features/dotnet:1": {} "ghcr.io/devcontainers/features/dotnet:2": {}
} }
``` ```
@ -15,9 +15,10 @@ Installs the .NET CLI. Provides option of installing sdk or runtime, and option
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| version | Select or enter a dotnet CLI version. (Available versions may vary by Linux distribution.) | string | latest | | version | Select or enter a .NET SDK version. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version. | string | latest |
| runtimeOnly | Install just the dotnet runtime if true, and sdk if false. | boolean | false | | additionalVersions | 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. | string | - |
| installUsingApt | If true, it installs using apt instead of the release URL | boolean | true | | dotnetRuntimeVersions | 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. | string | - |
| aspNetCoreRuntimeVersions | 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. | string | - |
## Customizations ## Customizations
@ -25,7 +26,74 @@ Installs the .NET CLI. Provides option of installing sdk or runtime, and option
- `ms-dotnettools.csharp` - `ms-dotnettools.csharp`
## Configuration examples
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. (The SDK includes all runtimes so this configuration is only useful if you 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 ## OS Support

View file

@ -1,35 +1,44 @@
{ {
"id": "dotnet", "id": "dotnet",
"version": "1.1.4", "version": "2.0.2",
"name": "Dotnet CLI", "name": "Dotnet CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/dotnet", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/dotnet",
"description": "Installs the .NET CLI. Provides option of installing sdk or runtime, and option of versions to install. Uses latest version of .NET sdk as defaults to install.", "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.",
"options": { "options": {
"version": { "version": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"latest", "latest",
"7", "lts",
"6", "none",
"3.1" "8.0",
"7.0",
"6.0"
], ],
"default": "latest", "default": "latest",
"description": "Select or enter a dotnet CLI version. (Available versions may vary by Linux distribution.)" "description": "Select or enter a .NET SDK version. Use 'latest' for the latest version, 'lts' for the latest LTS version, 'X.Y' or 'X.Y.Z' for a specific version."
}, },
"runtimeOnly": { "additionalVersions": {
"type": "boolean", "type": "string",
"default": false, "default": "",
"description": "Install just the dotnet runtime if true, and sdk if false." "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."
}, },
"installUsingApt": { "dotnetRuntimeVersions": {
"type": "boolean", "type": "string",
"default": true, "default": "",
"description": "If true, it installs using apt instead of the release URL" "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": { "containerEnv": {
"DOTNET_ROOT": "/usr/local/dotnet/current", "DOTNET_ROOT": "/usr/share/dotnet",
"PATH": "/usr/local/dotnet/current:${PATH}" "PATH": "$PATH:$DOTNET_ROOT:~/.dotnet/tools",
"DOTNET_RUNNING_IN_CONTAINER": "true",
"DOTNET_USE_POLLING_FILE_WATCHER": "true"
}, },
"customizations": { "customizations": {
"vscode": { "vscode": {

View file

@ -4,47 +4,21 @@
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
#------------------------------------------------------------------------------------------------------------- #-------------------------------------------------------------------------------------------------------------
# #
# Docs: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/dotnet.md # Docs: https://github.com/devcontainers/features/tree/main/src/dotnet
# Maintainer: The VS Code and Codespaces Teams # Maintainer: The Dev Container spec maintainers
DOTNET_VERSION="${VERSION:-"latest"}" DOTNET_VERSION="${VERSION:-"latest"}"
DOTNET_RUNTIME_ONLY="${RUNTIMEONLY:-"false"}" ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS}"
OVERRIDE_DEFAULT_VERSION="${OVERRIDEDEFAULTVERSION:-"true"}" DOTNET_RUNTIME_VERSIONS="${DOTNETRUNTIMEVERSIONS}"
INSTALL_USING_APT="${INSTALLUSINGAPT:-"true"}" ASPNETCORE_RUNTIME_VERSIONS="${ASPNETCORERUNTIMEVERSIONS}"
DOTNET_LATEST="7"
DOTNET_LTS="6"
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
UPDATE_RC="${UPDATE_RC:-"true"}"
TARGET_DOTNET_ROOT="${TARGET_DOTNET_ROOT:-"/usr/local/dotnet"}"
ACCESS_GROUP="${ACCESS_GROUP:-"dotnet"}"
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
DOTNET_ARCHIVE_ARCHITECTURES="amd64"
DOTNET_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute jammy"
# Feed URI sourced from the official dotnet-install.sh
# https://github.com/dotnet/install-scripts/blob/1b98b94a6f6d81cc4845eb88e0195fac67caa0a6/src/dotnet-install.sh#L1342-L1343
DOTNET_CDN_FEED_URI="https://dotnetcli.azureedge.net"
# Ubuntu 22.04 and on do not ship with libssl1.1, which is required for versions of .NET < 6.0
DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1="buster bullseye bionic focal hirsute"
# Comma-separated list of dotnet versions to be installed
# alongside DOTNET_VERSION, but not set as default.
ADDITIONAL_VERSIONS=${ADDITIONALVERSIONS:-""}
APT_PREFERENCES_CONTENT="$(cat << 'EOF'
Package: *
Pin: origin "packages.microsoft.com"
Pin-Priority: 1001
EOF
)"
APT_PREFERENCES_FILE_ADDED="false"
APT_PREFERENCES_UPDATED="false"
set -e set -e
# Import trim_whitespace and split_csv
source "scripts/string-helpers.sh"
# Import install_sdk and install_runtime
source "scripts/dotnet-helpers.sh"
# Clean up # Clean up
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
@ -53,72 +27,14 @@ err() {
echo "(!) $*" >&2 echo "(!) $*" >&2
} }
# Ensure the appropriate root user is running the script. apt_get_update() {
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
# Ensure that login shells get the correct path if the user updated the PATH using ENV.
rm -f /etc/profile.d/00-restore-env.sh
echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh
chmod +x /etc/profile.d/00-restore-env.sh
# Determine the appropriate non-root user.
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
USERNAME=""
POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")
for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do
if id -u "${CURRENT_USER}" > /dev/null 2>&1; then
USERNAME="${CURRENT_USER}"
break
fi
done
if [ "${USERNAME}" = "" ]; then
USERNAME=root
fi
elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root
fi
###################
# Helper Functions
###################
# Cleanup temporary directory and associated files when exiting the script.
cleanup() {
EXIT_CODE=$?
set +e
if [[ -n "${TMP_DIR}" ]]; then
echo "Executing cleanup of tmp files"
rm -Rf "${TMP_DIR}"
fi
exit $EXIT_CODE
}
trap cleanup EXIT
# Add dotnet directory to PATH in bashrc/zshrc files if OVERRIDE_DEFAULT_VERSION=true.
updaterc() {
if [ "${UPDATE_RC}" = "true" ]; then
echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..."
if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/bash.bashrc
fi
if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then
echo -e "$1" >> /etc/zsh/zshrc
fi
fi
}
apt_get_update()
{
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
echo "Running apt-get update..." echo "Running apt-get update..."
apt-get update -y apt-get update -y
fi fi
} }
# Check if packages are installed and installs them if not. # Checks if packages are installed and installs them if not
check_packages() { check_packages() {
if ! dpkg -s "$@" > /dev/null 2>&1; then if ! dpkg -s "$@" > /dev/null 2>&1; then
apt_get_update apt_get_update
@ -126,367 +42,77 @@ check_packages() {
fi fi
} }
# Get appropriate architecture name for .NET binaries for the target OS if [ "$(id -u)" -ne 0 ]; then
get_architecture_name_for_target_os() { err 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
local architecture
architecture="$(uname -m)"
case $architecture in
x86_64) architecture="x64";;
aarch64 | armv8*) architecture="arm64";;
*) err "Architecture ${architecture} unsupported"; exit 1 ;;
esac
echo "${architecture}"
}
# Soft version matching that resolves a version for a given package in the *current apt-cache*
# Return value is stored in first argument (the unprocessed version)
apt_cache_package_and_version_soft_match() {
# Version
local version_variable_name="$1"
local requested_version=${!version_variable_name}
# Package Name
local package_variable_name="$2"
local partial_package_name=${!package_variable_name}
local package_name
# Exit on no match?
local exit_on_no_match="${3:-true}"
local major_minor_version
# Ensure we've exported useful variables
. /etc/os-release
local architecture="$(dpkg --print-architecture)"
apt-get update -y
major_minor_version="$(echo "${requested_version}" | cut -d "." --field=1,2)"
package_name="$(apt-cache search "${partial_package_name}-[0-9].[0-9]$" | awk -F" - " '{print $1}' | grep -m 1 "${partial_package_name}-${major_minor_version}")"
dot_escaped="${requested_version//./\\.}"
dot_plus_escaped="${dot_escaped//+/\\+}"
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
version_regex="^(.+:)?${dot_plus_escaped}([\\.\\+ ~:-]|$)"
set +e # Don't exit if finding version fails - handle gracefully
fuzzy_version="$(apt-cache madison ${package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${version_regex}")"
set -e
if [ -z "${fuzzy_version}" ]; then
echo "(!) No full or partial for package \"${partial_package_name}\" (resolved: \"${package_name}\") match found in apt-cache for \"${requested_version}\" on OS ${ID} ${VERSION_CODENAME} (${architecture})."
if $exit_on_no_match; then
echo "Available versions:"
apt-cache madison ${package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
exit 1 # Fail entire script
else
echo "Continuing to fallback method if available"
return 1;
fi
fi
# Globally assign fuzzy_version to this value
# Use this value as the return value of this function
declare -g ${version_variable_name}="=${fuzzy_version}"
echo "${version_variable_name} ${!version_variable_name}"
# Globally assign package to this value
# Use this value as the return value of this function
declare -g ${package_variable_name}="${package_name}"
echo "${package_variable_name} ${!package_variable_name}"
}
# Install .NET CLI using apt-get package installer
install_using_apt() {
local sdk_or_runtime="$1"
local target_dotnet_version="$2"
local use_msft_repo="$3"
if [ "${use_msft_repo}" = "true" ]; then
# Install dependencies
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
# Import key safely and import Microsoft apt repo
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
echo "deb [arch=${architecture} signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/repos/microsoft-${ID}-${VERSION_CODENAME}-prod ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/microsoft.list
[ ! -f /etc/apt/preferences ] && APT_PREFERENCES_FILE_ADDED="true"
APT_PREFERENCES_UPDATED="true"
# See https://github.com/dotnet/core/issues/7699
echo "${APT_PREFERENCES_CONTENT}" >> /etc/apt/preferences
apt-get update -y
fi
# .NET 7 is not a LTS version, so handle latest and LTS versions differently
export APT_DOTNET_VERSION="$target_dotnet_version"
if [ "${APT_DOTNET_VERSION}" = "latest" ]; then
APT_DOTNET_VERSION="${DOTNET_LATEST}.0"
elif [ "${APT_DOTNET_VERSION}" = "lts" ]; then
APT_DOTNET_VERSION="${DOTNET_LTS}.0"
fi
# Sets target_dotnet_version and dotnet_package if matches found.
local base_dotnet_package="dotnet-${sdk_or_runtime}"
export DOTNET_PACKAGE="${base_dotnet_package}"
apt_cache_package_and_version_soft_match APT_DOTNET_VERSION DOTNET_PACKAGE false
if [ "$?" != 0 ] || [ ${DOTNET_PACKAGE} == "${base_dotnet_package}" ]; then
echo "Failed to find requested version."
return 1
fi
if type dotnet > /dev/null 2>&1 && [[ "$(dotnet --version)" == *"${APT_DOTNET_VERSION}"* ]] ; then
echo "dotnet version ${APT_DOTNET_VERSION} is already installed"
return 1
fi
echo "Installing '${DOTNET_PACKAGE}${APT_DOTNET_VERSION}'..."
apt-get install -yq ${DOTNET_PACKAGE}${APT_DOTNET_VERSION}
if [ "$?" != 0 ]; then
echo "Failed to complete apt install of ${DOTNET_PACKAGE}${TARGET_DOTNET_VERSION}"
return 1
fi
# Add symlink for current
CURRENT_DIR="${TARGET_DOTNET_ROOT}/current"
if [[ -d "${CURRENT_DIR}" ]]; then
rm -rf "${CURRENT_DIR}"
fi
mkdir -p "${TARGET_DOTNET_ROOT}"
local dotnet_installed_version="$(dotnet --version)"
# See if its the distro version
if [[ "$(dotnet --info)" == *"Base Path: /usr/lib/dotnet/${DOTNET_SDK_OR_RUNTIME}/${dotnet_installed_version}"* ]]; then
ln -s "/usr/lib/dotnet" "${CURRENT_DIR}"
else
# Location used by MS repo versions
ln -s "/usr/share/dotnet" "${CURRENT_DIR}"
fi
}
# Find and extract .NET binary download details based on user-requested version
# args:
# sdk_or_runtime $1
# dotnet_version_to_download $2
# exports:
# DOTNET_DOWNLOAD_URL
# DOTNET_DOWNLOAD_HASH
# DOTNET_DOWNLOAD_NAME
get_full_version_details() {
local sdk_or_runtime="$1"
local architecture
local dotnet_channel_version
local dotnet_releases_url
local dotnet_releases_json
local dotnet_latest_version
local dotnet_download_details
export DOTNET_DOWNLOAD_VERSION="$2"
export DOTNET_DOWNLOAD_URL
export DOTNET_DOWNLOAD_HASH
export DOTNET_DOWNLOAD_NAME
# Set architecture variable to current user's architecture (x64 or ARM64).
architecture="$(get_architecture_name_for_target_os)"
# Set VERSION to empty string to ensure jq includes all .NET versions in reverse sort below
if [ "${DOTNET_DOWNLOAD_VERSION}" = "latest" ]; then
DOTNET_DOWNLOAD_VERSION=""
fi
dotnet_patchless_version="$(echo "${DOTNET_DOWNLOAD_VERSION}" | cut -d "." --field=1,2)"
set +e
dotnet_channel_version="$(curl -s "${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/releases-index.json" | jq -r --arg channel_version "${dotnet_patchless_version}" '[."releases-index"[]] | sort_by(."channel-version") | reverse | map( select(."channel-version" | startswith($channel_version))) | first | ."channel-version"')"
set -e
# Construct the releases URL using the official channel-version if one was found. Otherwise make a best-effort using the user input.
if [ -n "${dotnet_channel_version}" ] && [ "${dotnet_channel_version}" != "null" ]; then
dotnet_releases_url="${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/${dotnet_channel_version}/releases.json"
else
dotnet_releases_url="${DOTNET_CDN_FEED_URI}/dotnet/release-metadata/${dotnet_patchless_version}/releases.json"
fi
set +e
dotnet_releases_json="$(curl -s "${dotnet_releases_url}")"
set -e
if [ -n "${dotnet_releases_json}" ] && [[ ! "${dotnet_releases_json}" = *"Error"* ]]; then
dotnet_latest_version="$(echo "${dotnet_releases_json}" | jq -r --arg sdk_or_runtime "${sdk_or_runtime}" '."latest-\($sdk_or_runtime)"')"
# If user-specified version has 2 or more dots, use it as is. Otherwise use latest version.
if [ "$(echo "${DOTNET_DOWNLOAD_VERSION}" | grep -o "\." | wc -l)" -lt "2" ]; then
DOTNET_DOWNLOAD_VERSION="${dotnet_latest_version}"
fi
dotnet_download_details="$(echo "${dotnet_releases_json}" | jq -r --arg sdk_or_runtime "${sdk_or_runtime}" --arg dotnet_version "${DOTNET_DOWNLOAD_VERSION}" --arg arch "${architecture}" '.releases[]."\($sdk_or_runtime)" | select(.version==$dotnet_version) | .files[] | select(.name=="dotnet-\($sdk_or_runtime)-linux-\($arch).tar.gz")')"
if [ -n "${dotnet_download_details}" ]; then
echo "Found .NET binary version ${DOTNET_DOWNLOAD_VERSION}"
DOTNET_DOWNLOAD_URL="$(echo "${dotnet_download_details}" | jq -r '.url')"
DOTNET_DOWNLOAD_HASH="$(echo "${dotnet_download_details}" | jq -r '.hash')"
DOTNET_DOWNLOAD_NAME="$(echo "${dotnet_download_details}" | jq -r '.name')"
else
err "Unable to find .NET binary for version ${DOTNET_DOWNLOAD_VERSION}"
exit 1
fi
else
err "Unable to find .NET release details for version ${DOTNET_DOWNLOAD_VERSION} at ${dotnet_releases_url}"
exit 1
fi
}
# Install .NET CLI using the .NET releases url
install_using_dotnet_releases_url() {
local sdk_or_runtime="$1"
local version="$2"
# Check listed package dependencies and install them if they are not already installed.
# NOTE: icu-devtools is a small package with similar dependencies to .NET.
# It will install the appropriate dependencies based on the OS:
# - libgcc-s1 OR libgcc1 depending on OS
# - the latest libicuXX depending on OS (eg libicu57 for stretch)
# - also installs libc6 and libstdc++6 which are required by .NET
check_packages curl ca-certificates tar jq icu-devtools libgssapi-krb5-2 zlib1g
# Starting with Ubuntu 22.04 (jammy), libssl1.1 does not ship with the OS anymore.
if [[ "${DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1}" = *"${VERSION_CODENAME}"* ]]; then
check_packages libssl1.1
else
check_packages libssl3
fi
get_full_version_details "${sdk_or_runtime}" "${version}"
DOTNET_INSTALL_PATH="${TARGET_DOTNET_ROOT}/${DOTNET_DOWNLOAD_VERSION}"
if [ -d "${DOTNET_INSTALL_PATH}" ]; then
echo "(!) Dotnet version ${DOTNET_DOWNLOAD_VERSION} already exists."
exit 1
fi
# exports DOTNET_DOWNLOAD_URL, DOTNET_DOWNLOAD_HASH, DOTNET_DOWNLOAD_NAME
echo "DOWNLOAD LINK: ${DOTNET_DOWNLOAD_URL}"
# Setup the access group and add the user to it.
umask 0002
if ! cat /etc/group | grep -e "^${ACCESS_GROUP}:" > /dev/null 2>&1; then
groupadd -r "${ACCESS_GROUP}"
fi
usermod -a -G "${ACCESS_GROUP}" "${USERNAME}"
# Download the .NET binaries.
echo "DOWNLOADING BINARY..."
TMP_DIR="/tmp/dotnetinstall"
mkdir -p "${TMP_DIR}"
curl -sSL "${DOTNET_DOWNLOAD_URL}" -o "${TMP_DIR}/${DOTNET_DOWNLOAD_NAME}"
# Get checksum from .NET CLI blob storage using the runtime version and
# run validation (sha512sum) of checksum against the expected checksum hash.
echo "VERIFY CHECKSUM"
cd "${TMP_DIR}"
echo "${DOTNET_DOWNLOAD_HASH} *${DOTNET_DOWNLOAD_NAME}" | sha512sum -c -
# Extract binaries and add to path.
mkdir -p "${DOTNET_INSTALL_PATH}"
echo "Extract Binary to ${DOTNET_INSTALL_PATH}"
tar -xzf "${TMP_DIR}/${DOTNET_DOWNLOAD_NAME}" -C "${DOTNET_INSTALL_PATH}" --strip-components=1
CURRENT_DIR="${TARGET_DOTNET_ROOT}/current"
if [[ ! -d "${CURRENT_DIR}" ]]; then
ln -s "${DOTNET_INSTALL_PATH}" "${CURRENT_DIR}"
fi
# Give write permissions to the user.
chown -R "${USERNAME}:${USERNAME}" "${CURRENT_DIR}"
chmod g+r+w+s "${CURRENT_DIR}"
chmod -R g+r+w "${CURRENT_DIR}"
if [[ "${OVERRIDE_DEFAULT_VERSION}" = "true" ]]; then
if [[ $(ls -l ${CURRENT_DIR}) != *"-> ${DOTNET_INSTALL_PATH}"* ]] ; then
rm "${CURRENT_DIR}"
ln -s "${DOTNET_INSTALL_PATH}" "${CURRENT_DIR}"
fi
fi
updaterc "if [[ \"\${PATH}\" != *\"${CURRENT_DIR}\"* ]]; then export PATH=${CURRENT_DIR}:\${PATH}; fi"
}
###########################
# Start .NET installation
###########################
export DEBIAN_FRONTEND=noninteractive
. /etc/os-release
architecture="$(dpkg --print-architecture)"
# Dotnet 3.1 and 5 are not supported on Ubuntu 22.04 (jammy)+,
# due to lack of libssl3.0 support.
# See: https://github.com/microsoft/vscode-dev-containers/issues/1458#issuecomment-1135077775
# NOTE: This will only guard against installation of the dotnet versions we propose via 'features'.
# The user can attempt to install any other version at their own risk.
if [[ "${DOTNET_VERSION}" = "3"* ]] || [[ "${DOTNET_VERSION}" = "5"* ]]; then
if [[ ! "${DOTNET_VERSION_CODENAMES_REQUIRE_OLDER_LIBSSL_1}" = *"${VERSION_CODENAME}"* ]]; then
err "Dotnet ${DOTNET_VERSION} is not supported on Ubuntu ${VERSION_CODENAME} due to a change in the 'libssl' dependency across distributions.\n Please upgrade your version of dotnet, or downgrade your OS version."
exit 1
fi
fi
# Determine if the user wants to download .NET Runtime only, or .NET SDK & Runtime
# and set the appropriate variables.
if [ "${DOTNET_RUNTIME_ONLY}" = "true" ]; then
DOTNET_SDK_OR_RUNTIME="runtime"
elif [ "${DOTNET_RUNTIME_ONLY}" = "false" ]; then
DOTNET_SDK_OR_RUNTIME="sdk"
else
err "Expected true for installing dotnet Runtime only or false for installing SDK and Runtime. Received ${DOTNET_RUNTIME_ONLY}."
exit 1 exit 1
fi fi
# Install the .NET CLI # For our own convenience, combine DOTNET_VERSION and ADDITIONAL_VERSIONS into a single 'versions' array
echo "(*) Installing .NET CLI..." versions=()
CHANGE_OWNERSHIP="false" # The version can be set to 'none' for runtime-only installations, then the array will just remain empty
if [[ "${DOTNET_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${DOTNET_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]] && [[ "${INSTALL_USING_APT}" = "true" ]]; then # Ensure there are no leading or trailing spaces that can break regex pattern matching
echo "Detected ${VERSION_CODENAME} on ${architecture}. Attempting to install dotnet from apt" if [ "$DOTNET_VERSION" != "none" ]; then
versions+=("$(trim_whitespace "$DOTNET_VERSION")")
for additional_version in $(split_csv "$ADDITIONAL_VERSIONS"); do
versions+=("$additional_version")
done
fi
install_using_apt "${DOTNET_SDK_OR_RUNTIME}" "${DOTNET_VERSION}" false || install_using_apt "${DOTNET_SDK_OR_RUNTIME}" "${DOTNET_VERSION}" true dotnetRuntimeVersions=()
if [ "$?" != 0 ]; then for dotnetRuntimeVersion in $(split_csv "$DOTNET_RUNTIME_VERSIONS"); do
echo "Could not install requested version from apt on current distribution." 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
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 exit 1
fi fi
else done
if [[ "${INSTALL_USING_APT}" = "false" ]]; then
echo "Installing dotnet from releases url" for version in "${dotnetRuntimeVersions[@]}"; do
else if [[ "$version" =~ ^[0-9]+$ ]]; then
echo "Attempting to install dotnet from releases url" 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 fi
install_using_dotnet_releases_url "${DOTNET_SDK_OR_RUNTIME}" "${DOTNET_VERSION}" done
CHANGE_OWNERSHIP="true"
fi
# Additional dotnet versions to be installed but not be set as default. for version in "${aspNetCoreRuntimeVersions[@]}"; do
if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then if [[ "$version" =~ ^[0-9]+$ ]]; then
OLDIFS=$IFS 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."
IFS="," exit 1
read -a additional_versions <<< "$ADDITIONAL_VERSIONS"
for version in "${additional_versions[@]}"; do
OVERRIDE_DEFAULT_VERSION="false"
install_using_dotnet_releases_url "${DOTNET_SDK_OR_RUNTIME}" "${version}"
done
IFS=$OLDIFS
fi
if [ "${CHANGE_OWNERSHIP}" = "true" ]; then
if ! cat /etc/group | grep -e "^dotnet:" > /dev/null 2>&1; then
groupadd -r dotnet
fi fi
usermod -a -G dotnet "${USERNAME}" done
chown -R "${USERNAME}:dotnet" "${TARGET_DOTNET_ROOT}" # Install .NET versions and dependencies
chmod -R g+r+w "${TARGET_DOTNET_ROOT}" # icu-devtools includes dependencies for .NET
find "${TARGET_DOTNET_ROOT}" -type d -print0 | xargs -n 1 -0 chmod g+s check_packages wget ca-certificates icu-devtools
fi
for version in "${versions[@]}"; do
install_sdk "$version"
done
for version in "${dotnetRuntimeVersions[@]}"; do
install_runtime "dotnet" "$version"
done
for version in "${aspNetCoreRuntimeVersions[@]}"; do
install_runtime "aspnetcore" "$version"
done
# Clean up # Clean up
if [ "${APT_PREFERENCES_UPDATED}" = "true" ]; then
if [ "${APT_PREFERENCES_FILE_ADDED}" = "true" ]; then
rm -f /etc/apt/preferences
else
head -n -3 /etc/apt/preferences > /tmp/preferences-temp && mv /tmp/preferences-temp /etc/apt/preferences
fi
fi
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
rm -rf scripts
echo "Done!" echo "Done!"

View file

@ -0,0 +1,119 @@
#!/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
DOTNET_SCRIPTS=$(dirname "${BASH_SOURCE[0]}")
DOTNET_INSTALL_SCRIPT="$DOTNET_SCRIPTS/vendor/dotnet-install.sh"
DOTNET_INSTALL_DIR='/usr/share/dotnet'
# Prints the latest dotnet version in the specified channel
# Usage: fetch_latest_version_in_channel <channel> [<runtime>]
# Example: fetch_latest_version_in_channel "LTS"
# Example: fetch_latest_version_in_channel "6.0" "dotnet"
# Example: fetch_latest_version_in_channel "6.0" "aspnetcore"
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"
elif [ "$runtime" = "aspnetcore" ]; then
wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
else
wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
fi
}
# Prints the latest dotnet version
# Usage: fetch_latest_version [<runtime>]
# Example: fetch_latest_version
# Example: fetch_latest_version "dotnet"
# Example: fetch_latest_version "aspnetcore"
fetch_latest_version() {
local runtime="$1"
local sts_version
local lts_version
sts_version=$(fetch_latest_version_in_channel "STS" "$runtime")
lts_version=$(fetch_latest_version_in_channel "LTS" "$runtime")
if [[ "$sts_version" > "$lts_version" ]]; then
echo "$sts_version"
else
echo "$lts_version"
fi
}
# Installs a version of the .NET SDK
# Usage: install_sdk <version>
install_sdk() {
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_version)
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.413' or '8.0.100-rc.2.23425.18'
version="$inputVersion"
fi
# Currently this script does not make it possible to qualify the version, 'GA' is always implied
echo "Executing $DOTNET_INSTALL_SCRIPT --version $version --channel $channel --install-dir $DOTNET_INSTALL_DIR --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--version "$version" \
--channel "$channel" \
--install-dir "$DOTNET_INSTALL_DIR" \
--no-path
}
# Installs a version of the .NET Runtime
# Usage: install_runtime <runtime> <version>
install_runtime() {
local runtime="$1"
local inputVersion="$2"
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_version "$runtime")
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-preview.7.23375.6'
version="$inputVersion"
fi
echo "Executing $DOTNET_INSTALL_SCRIPT --runtime $runtime --version $version --channel $channel --install-dir $DOTNET_INSTALL_DIR --no-path"
"$DOTNET_INSTALL_SCRIPT" \
--runtime "$runtime" \
--version "$version" \
--channel "$channel" \
--install-dir "$DOTNET_INSTALL_DIR" \
--no-path
}

View file

@ -0,0 +1,16 @@
#!/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
#
# Run this script to replace dotnet-install.sh with the latest and greatest available version
#
DOTNET_SCRIPTS=$(dirname "${BASH_SOURCE[0]}")
DOTNET_INSTALL_SCRIPT="$DOTNET_SCRIPTS/vendor/dotnet-install.sh"
wget https://dot.net/v1/dotnet-install.sh -O "$DOTNET_INSTALL_SCRIPT"
chmod +x "$DOTNET_INSTALL_SCRIPT"

View file

@ -0,0 +1,42 @@
#!/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
# Removes leading and trailing whitespace from an input string
# Usage: trim_whitespace <text>
trim_whitespace() {
text="$1"
# Remove leading spaces
while [ "${text:0:1}" == " " ]; do
text="${text:1}"
done
# Remove trailing spaces
while [ "${text: -1}" == " " ]; do
text="${text:0:-1}"
done
echo "$text"
}
# Splits comma-separated values into an array while ignoring empty entries
# Usage: split_csv <comma-separated-values>
split_csv() {
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"
echo "${values[@]}"
}

27
src/dotnet/scripts/vendor/README.md vendored Normal file
View file

@ -0,0 +1,27 @@
### **IMPORTANT NOTE**
Scripts in this directory are sourced externally and not maintained by the Dev Container spec maintainers. Do not make changes directly as they might be overwritten at any moment.
## dotnet-install.sh
`dotnet-install.sh` is a copy of <https://dot.net/v1/dotnet-install.sh>. ([Script reference](https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script))
Quick options reminder for `dotnet-install.sh`:
- `--version`: `"latest"` (default) or an exact version in the form A.B.C like `"6.0.413"`
- `--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"`, `"preview"` or `"GA"`
- 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
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.413
```

1835
src/dotnet/scripts/vendor/dotnet-install.sh vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"id": "git-lfs", "id": "git-lfs",
"version": "1.1.0", "version": "1.1.1",
"name": "Git Large File Support (LFS)", "name": "Git Large File Support (LFS)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/git-lfs", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/git-lfs",
"description": "Installs Git Large File Support (Git LFS) along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like git and curl.", "description": "Installs Git Large File Support (Git LFS) along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like git and curl.",

View file

@ -15,6 +15,7 @@ GIT_LFS_ARCHIVE_ARCHITECTURES="amd64 arm64"
GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy" GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal jammy"
GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889" GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -1,6 +1,6 @@
{ {
"id": "git", "id": "git",
"version": "1.1.5", "version": "1.1.6",
"name": "Git (from source)", "name": "Git (from source)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/git", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/git",
"description": "Install an up-to-date version of Git, built from source as needed. Useful for when you want the latest and greatest features. Auto-detects latest stable version and installs needed dependencies.", "description": "Install an up-to-date version of Git, built from source as needed. Useful for when you want the latest and greatest features. Auto-detects latest stable version and installs needed dependencies.",

View file

@ -12,6 +12,7 @@ USE_PPA_IF_AVAILABLE=${PPA}
GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24 GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -1,6 +1,6 @@
{ {
"id": "github-cli", "id": "github-cli",
"version": "1.0.10", "version": "1.0.11",
"name": "GitHub CLI", "name": "GitHub CLI",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/github-cli",
"description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.", "description": "Installs the GitHub CLI. Auto-detects latest version and installs needed dependencies.",

View file

@ -12,6 +12,7 @@ INSTALL_DIRECTLY_FROM_GITHUB_RELEASE=${INSTALLDIRECTLYFROMGITHUBRELEASE:-"true"}
GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059 GITHUB_CLI_ARCHIVE_GPG_KEY=23F3D4EA75716059
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -1,6 +1,6 @@
{ {
"id": "go", "id": "go",
"version": "1.2.0", "version": "1.2.2",
"name": "Go", "name": "Go",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/go", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/go",
"description": "Installs Go and common Go utilities. Auto-detects latest version and installs needed dependencies.", "description": "Installs Go and common Go utilities. Auto-detects latest version and installs needed dependencies.",
@ -10,8 +10,8 @@
"proposals": [ "proposals": [
"latest", "latest",
"none", "none",
"1.19", "1.21",
"1.18" "1.20"
], ],
"default": "latest", "default": "latest",
"description": "Select or enter a Go version to install" "description": "Select or enter a Go version to install"

View file

@ -84,21 +84,6 @@ find_version_from_git_tags() {
echo "${variable_name}=${!variable_name}" echo "${variable_name}=${!variable_name}"
} }
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
apt_get_update() apt_get_update()
{ {
if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then
@ -148,7 +133,6 @@ if [[ "${TARGET_GO_VERSION}" != "none" ]] && [[ "$(go version)" != *"${TARGET_GO
export GNUPGHOME="/tmp/tmp-gnupg" export GNUPGHOME="/tmp/tmp-gnupg"
mkdir -p ${GNUPGHOME} mkdir -p ${GNUPGHOME}
chmod 700 ${GNUPGHOME} chmod 700 ${GNUPGHOME}
get_common_setting GO_GPG_KEY_URI
curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}" curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}"
gpg -q --import /tmp/tmp-gnupg/golang_key gpg -q --import /tmp/tmp-gnupg/golang_key
echo "Downloading Go ${TARGET_GO_VERSION}..." echo "Downloading Go ${TARGET_GO_VERSION}..."

View file

@ -1,6 +1,6 @@
{ {
"id": "java", "id": "java",
"version": "1.2.1", "version": "1.2.2",
"name": "Java (via SDKMAN!)", "name": "Java (via SDKMAN!)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/java", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/java",
"description": "Installs Java, SDKMAN! (if not installed), and needed dependencies.", "description": "Installs Java, SDKMAN! (if not installed), and needed dependencies.",

View file

@ -111,13 +111,13 @@ sdk_install() {
elif echo "${requested_version}" | grep -oE "${full_version_check}" > /dev/null 2>&1; then elif echo "${requested_version}" | grep -oE "${full_version_check}" > /dev/null 2>&1; then
echo "${requested_version}" echo "${requested_version}"
else else
local regex="${prefix}\\K[0-9]+\\.[0-9]+\\.[0-9]+${suffix}" local regex="${prefix}\\K[0-9]+\\.?[0-9]*\\.?[0-9]*${suffix}"
local version_list=$(su ${USERNAME} -c ". \${SDKMAN_DIR}/bin/sdkman-init.sh && sdk list ${install_type} 2>&1 | grep -oP \"${regex}\" | tr -d ' ' | sort -rV") local version_list=$(su ${USERNAME} -c ". \${SDKMAN_DIR}/bin/sdkman-init.sh && sdk list ${install_type} 2>&1 | grep -oP \"${regex}\" | tr -d ' ' | sort -rV")
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ]; then if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ]; then
requested_version="$(echo "${version_list}" | head -n 1)" requested_version="$(echo "${version_list}" | head -n 1)"
else else
set +e set +e
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|-|$)")"
set -e set -e
fi fi
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then

View file

@ -1,6 +1,6 @@
{ {
"id": "kubectl-helm-minikube", "id": "kubectl-helm-minikube",
"version": "1.1.4", "version": "1.1.5",
"name": "Kubectl, Helm, and Minikube", "name": "Kubectl, Helm, and Minikube",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/kubectl-helm-minikube", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/kubectl-helm-minikube",
"description": "Installs latest version of kubectl, Helm, and optionally minikube. Auto-detects latest versions and installs needed dependencies.", "description": "Installs latest version of kubectl, Helm, and optionally minikube. Auto-detects latest versions and installs needed dependencies.",

View file

@ -23,6 +23,7 @@ USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
HELM_GPG_KEYS_URI="https://raw.githubusercontent.com/helm/helm/main/KEYS" HELM_GPG_KEYS_URI="https://raw.githubusercontent.com/helm/helm/main/KEYS"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -18,7 +18,7 @@ Installs Node.js, nvm, yarn, pnpm, and needed dependencies.
| version | Select or enter a Node.js version to install | string | lts | | version | Select or enter a Node.js version to install | string | lts |
| nodeGypDependencies | Install dependencies to compile native node modules (node-gyp)? | boolean | true | | nodeGypDependencies | Install dependencies to compile native node modules (node-gyp)? | boolean | true |
| nvmInstallPath | The path where NVM will be installed. | string | /usr/local/share/nvm | | nvmInstallPath | The path where NVM will be installed. | string | /usr/local/share/nvm |
| nvmVersion | Version of NVM to install. | string | 0.39.2 | | nvmVersion | Version of NVM to install. | string | latest |
## Customizations ## Customizations

View file

@ -1,6 +1,6 @@
{ {
"id": "node", "id": "node",
"version": "1.2.1", "version": "1.3.1",
"name": "Node.js (via nvm), yarn and pnpm", "name": "Node.js (via nvm), yarn and pnpm",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/node", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/node",
"description": "Installs Node.js, nvm, yarn, pnpm, and needed dependencies.", "description": "Installs Node.js, nvm, yarn, pnpm, and needed dependencies.",
@ -30,7 +30,11 @@
}, },
"nvmVersion": { "nvmVersion": {
"type": "string", "type": "string",
"default": "0.39.2", "proposals": [
"latest",
"0.39"
],
"default": "latest",
"description": "Version of NVM to install." "description": "Version of NVM to install."
} }
}, },

View file

@ -8,7 +8,7 @@
# Maintainer: The Dev Container spec maintainers # Maintainer: The Dev Container spec maintainers
export NODE_VERSION="${VERSION:-"lts"}" export NODE_VERSION="${VERSION:-"lts"}"
export NVM_VERSION="${NVMVERSION:-"0.39.2"}" export NVM_VERSION="${NVMVERSION:-"latest"}"
export NVM_DIR="${NVMINSTALLPATH:-"/usr/local/share/nvm"}" export NVM_DIR="${NVMINSTALLPATH:-"/usr/local/share/nvm"}"
INSTALL_TOOLS_FOR_NODE_GYP="${NODEGYPDEPENDENCIES:-true}" INSTALL_TOOLS_FOR_NODE_GYP="${NODEGYPDEPENDENCIES:-true}"
@ -78,6 +78,40 @@ check_packages() {
fi fi
} }
# Figure out correct version of a three part version number is not passed
find_version_from_git_tags() {
local variable_name=$1
local requested_version=${!variable_name}
if [ "${requested_version}" = "none" ]; then return; fi
local repository=$2
local prefix=${3:-"tags/v"}
local separator=${4:-"."}
local last_part_optional=${5:-"false"}
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
local escaped_separator=${separator//./\\.}
local last_part
if [ "${last_part_optional}" = "true" ]; then
last_part="(${escaped_separator}[0-9]+)?"
else
last_part="${escaped_separator}[0-9]+"
fi
local regex="${prefix}\\K[0-9]+${escaped_separator}[0-9]+${last_part}$"
local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)"
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)"
else
set +e
declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
set -e
fi
fi
if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then
echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
exit 1
fi
echo "${variable_name}=${!variable_name}"
}
# Ensure apt is in non-interactive to avoid prompts # Ensure apt is in non-interactive to avoid prompts
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
@ -92,6 +126,10 @@ fi
# Install dependencies # Install dependencies
check_packages apt-transport-https curl ca-certificates tar gnupg2 dirmngr check_packages apt-transport-https curl ca-certificates tar gnupg2 dirmngr
if ! type git > /dev/null 2>&1; then
check_packages git
fi
# Install yarn # Install yarn
if type yarn > /dev/null 2>&1; then if type yarn > /dev/null 2>&1; then
echo "Yarn already installed." echo "Yarn already installed."
@ -112,6 +150,8 @@ elif [ "${NODE_VERSION}" = "latest" ]; then
export NODE_VERSION="node" export NODE_VERSION="node"
fi fi
find_version_from_git_tags NVM_VERSION "https://github.com/nvm-sh/nvm"
# Install snipppet that we will run as the user # Install snipppet that we will run as the user
nvm_install_snippet="$(cat << EOF nvm_install_snippet="$(cat << EOF
set -e set -e
@ -188,6 +228,9 @@ if type pnpm > /dev/null 2>&1; then
echo "pnpm already installed." echo "pnpm already installed."
else else
if type npm > /dev/null 2>&1; then if type npm > /dev/null 2>&1; then
[ ! -z "$http_proxy" ] && npm set proxy="$http_proxy"
[ ! -z "$https_proxy" ] && npm set https-proxy="$https_proxy"
[ ! -z "$no_proxy" ] && npm set noproxy="$no_proxy"
npm install -g pnpm npm install -g pnpm
else else
echo "Skip installing pnpm because npm is missing" echo "Skip installing pnpm because npm is missing"

View file

@ -16,7 +16,9 @@ Installs shared libraries for NVIDIA CUDA.
| Options Id | Description | Type | Default Value | | Options Id | Description | Type | Default Value |
|-----|-----|-----|-----| |-----|-----|-----|-----|
| installCudnn | Additionally install CUDA Deep Neural Network (cuDNN) shared library | boolean | false | | installCudnn | Additionally install CUDA Deep Neural Network (cuDNN) shared library | boolean | false |
| installCudnnDev | Additionally install CUDA Deep Neural Network (cuDNN) development libraries and headers | boolean | false |
| installNvtx | Additionally install NVIDIA Tools Extension (NVTX) | boolean | false | | installNvtx | Additionally install NVIDIA Tools Extension (NVTX) | boolean | false |
| installToolkit | Additionally install NVIDIA CUDA Toolkit | boolean | false |
| cudaVersion | Version of CUDA to install | string | 11.8 | | cudaVersion | Version of CUDA to install | string | 11.8 |
| cudnnVersion | Version of cuDNN to install | string | 8.6.0.163 | | cudnnVersion | Version of cuDNN to install | string | 8.6.0.163 |

View file

@ -1,6 +1,6 @@
{ {
"id": "nvidia-cuda", "id": "nvidia-cuda",
"version": "1.0.7", "version": "1.1.0",
"name": "NVIDIA CUDA", "name": "NVIDIA CUDA",
"description": "Installs shared libraries for NVIDIA CUDA.", "description": "Installs shared libraries for NVIDIA CUDA.",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/nvidia-cuda", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/nvidia-cuda",
@ -10,14 +10,27 @@
"default": false, "default": false,
"description": "Additionally install CUDA Deep Neural Network (cuDNN) shared library" "description": "Additionally install CUDA Deep Neural Network (cuDNN) shared library"
}, },
"installCudnnDev": {
"type": "boolean",
"default": false,
"description": "Additionally install CUDA Deep Neural Network (cuDNN) development libraries and headers"
},
"installNvtx": { "installNvtx": {
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Additionally install NVIDIA Tools Extension (NVTX)" "description": "Additionally install NVIDIA Tools Extension (NVTX)"
}, },
"installToolkit": {
"type": "boolean",
"default": false,
"description": "Additionally install NVIDIA CUDA Toolkit"
},
"cudaVersion": { "cudaVersion": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"12.2",
"12.1",
"12.0",
"11.8", "11.8",
"11.7", "11.7",
"11.6", "11.6",
@ -32,6 +45,15 @@
"cudnnVersion": { "cudnnVersion": {
"type": "string", "type": "string",
"proposals": [ "proposals": [
"8.9.5.29",
"8.9.4.25",
"8.9.3.28",
"8.9.2.26",
"8.9.1.23",
"8.9.0.131",
"8.8.1.3",
"8.8.0.121",
"8.7.0.84",
"8.6.0.163", "8.6.0.163",
"8.5.0.96", "8.5.0.96",
"8.4.1.50", "8.4.1.50",

View file

@ -6,7 +6,9 @@ set -e
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
INSTALL_CUDNN=${INSTALLCUDNN} INSTALL_CUDNN=${INSTALLCUDNN}
INSTALL_CUDNNDEV=${INSTALLCUDNNDEV}
INSTALL_NVTX=${INSTALLNVTX} INSTALL_NVTX=${INSTALLNVTX}
INSTALL_TOOLKIT=${INSTALLTOOLKIT}
CUDA_VERSION=${CUDAVERSION} CUDA_VERSION=${CUDAVERSION}
CUDNN_VERSION=${CUDNNVERSION} CUDNN_VERSION=${CUDNNVERSION}
@ -47,6 +49,7 @@ apt-get update -yq
# Ensure that the requested version of CUDA is available # Ensure that the requested version of CUDA is available
cuda_pkg="cuda-libraries-${CUDA_VERSION/./-}" cuda_pkg="cuda-libraries-${CUDA_VERSION/./-}"
nvtx_pkg="cuda-nvtx-${CUDA_VERSION/./-}" nvtx_pkg="cuda-nvtx-${CUDA_VERSION/./-}"
toolkit_pkg="cuda-toolkit-${CUDA_VERSION/./-}"
if ! apt-cache show "$cuda_pkg"; then if ! apt-cache show "$cuda_pkg"; then
echo "The requested version of CUDA is not available: CUDA $CUDA_VERSION" echo "The requested version of CUDA is not available: CUDA $CUDA_VERSION"
exit 1 exit 1
@ -67,11 +70,28 @@ if [ "$INSTALL_CUDNN" = "true" ]; then
apt-get install -yq "$cudnn_pkg_version" apt-get install -yq "$cudnn_pkg_version"
fi fi
if [ "$INSTALL_CUDNNDEV" = "true" ]; then
# Ensure that the requested version of cuDNN development package is available AND compatible
cudnn_dev_pkg_version="libcudnn8-dev=${CUDNN_VERSION}-1+cuda${CUDA_VERSION}"
if ! apt-cache show "$cudnn_dev_pkg_version"; then
echo "The requested version of cuDNN development package is not available: cuDNN $CUDNN_VERSION for CUDA $CUDA_VERSION"
exit 1
fi
echo "Installing cuDNN dev libraries..."
apt-get install -yq "$cudnn_dev_pkg_version"
fi
if [ "$INSTALL_NVTX" = "true" ]; then if [ "$INSTALL_NVTX" = "true" ]; then
echo "Installing NVTX..." echo "Installing NVTX..."
apt-get install -yq "$nvtx_pkg" apt-get install -yq "$nvtx_pkg"
fi fi
if [ "$INSTALL_TOOLKIT" = "true" ]; then
echo "Installing CUDA Toolkit..."
apt-get install -yq "$toolkit_pkg"
fi
# Clean up # Clean up
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*

View file

@ -18,6 +18,12 @@ Installs PowerShell along with needed dependencies. Useful for base Dockerfiles
| version | Select or enter a version of PowerShell. | string | latest | | version | Select or enter a version of PowerShell. | string | latest |
| modules | Optional comma separated list of PowerShell modules to install. | string | - | | modules | Optional comma separated list of PowerShell modules to install. | string | - |
## Customizations
### VS Code Extensions
- `ms-vscode.powershell`
## OS Support ## OS Support

View file

@ -1,6 +1,6 @@
{ {
"id": "powershell", "id": "powershell",
"version": "1.1.0", "version": "1.2.0",
"name": "PowerShell", "name": "PowerShell",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/powershell",
"description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.", "description": "Installs PowerShell along with needed dependencies. Useful for base Dockerfiles that often are missing required install dependencies like gpg.",
@ -19,8 +19,15 @@
"type": "string", "type": "string",
"default": "", "default": "",
"description": "Optional comma separated list of PowerShell modules to install." "description": "Optional comma separated list of PowerShell modules to install."
} }
}, },
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.powershell"
]
}
},
"installsAfter": [ "installsAfter": [
"ghcr.io/devcontainers/features/common-utils" "ghcr.io/devcontainers/features/common-utils"
] ]

View file

@ -19,6 +19,7 @@ MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
POWERSHELL_ARCHIVE_ARCHITECTURES="amd64" POWERSHELL_ARCHIVE_ARCHITECTURES="amd64"
POWERSHELL_ARCHIVE_VERSION_CODENAMES="stretch buster bionic focal bullseye jammy" POWERSHELL_ARCHIVE_VERSION_CODENAMES="stretch buster bionic focal bullseye jammy"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -1,6 +1,6 @@
{ {
"id": "python", "id": "python",
"version": "1.1.0", "version": "1.3.1",
"name": "Python", "name": "Python",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/python", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/python",
"description": "Installs the provided version of Python, as well as PIPX, and other common Python utilities. JupyterLab is conditionally installed with the python feature. Note: May require source code compilation.", "description": "Installs the provided version of Python, as well as PIPX, and other common Python utilities. JupyterLab is conditionally installed with the python feature. Note: May require source code compilation.",
@ -11,6 +11,7 @@
"latest", "latest",
"os-provided", "os-provided",
"none", "none",
"3.12",
"3.11", "3.11",
"3.10", "3.10",
"3.9", "3.9",
@ -65,15 +66,7 @@
"ms-python.vscode-pylance" "ms-python.vscode-pylance"
], ],
"settings": { "settings": {
"python.defaultInterpreterPath": "/usr/local/python/current/bin/python", "python.defaultInterpreterPath": "/usr/local/python/current/bin/python"
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
"python.linting.flake8Enabled": false,
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
"python.linting.mypyEnabled": false,
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint",
"python.linting.pylintEnabled": false
} }
} }
}, },

View file

@ -27,8 +27,9 @@ CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN="${CONFIGUREJUPYTERLABALLOWORIGIN:-""}"
ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS:-""}" ADDITIONAL_VERSIONS="${ADDITIONALVERSIONS:-""}"
DEFAULT_UTILS=("pylint" "flake8" "autopep8" "black" "yapf" "mypy" "pydocstyle" "pycodestyle" "bandit" "pipenv" "virtualenv" "pytest") DEFAULT_UTILS=("pylint" "flake8" "autopep8" "black" "yapf" "mypy" "pydocstyle" "pycodestyle" "bandit" "pipenv" "virtualenv" "pytest")
PYTHON_SOURCE_GPG_KEYS="64E628F8D684696D B26995E310250568 2D347EA6AA65421D FB9921286F5E1540 3A5CA953F73C700D 04C367C218ADD4FF 0EDDC5F26A45C816 6AF053F07D9DC8D2 C9BE28DEE6DF025C 126EB563A74B06BF D9866941EA5BBD71 ED9D77D5" PYTHON_SOURCE_GPG_KEYS="64E628F8D684696D B26995E310250568 2D347EA6AA65421D FB9921286F5E1540 3A5CA953F73C700D 04C367C218ADD4FF 0EDDC5F26A45C816 6AF053F07D9DC8D2 C9BE28DEE6DF025C 126EB563A74B06BF D9866941EA5BBD71 ED9D77D5 A821E680E5FA6305"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"
@ -304,25 +305,31 @@ sudo_if() {
if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then
su - "$USERNAME" -c "$COMMAND" su - "$USERNAME" -c "$COMMAND"
else else
"$COMMAND" $COMMAND
fi fi
} }
install_user_package() { install_user_package() {
PACKAGE="$1" INSTALL_UNDER_ROOT="$1"
sudo_if "${PYTHON_SRC}" -m pip install --user --upgrade --no-cache-dir "$PACKAGE" PACKAGE="$2"
if [ "$INSTALL_UNDER_ROOT" = true ]; then
sudo_if "${PYTHON_SRC}" -m pip install --upgrade --no-cache-dir "$PACKAGE"
else
sudo_if "${PYTHON_SRC}" -m pip install --user --upgrade --no-cache-dir "$PACKAGE"
fi
} }
add_user_jupyter_config() { add_user_jupyter_config() {
CONFIG_DIR="/home/$USERNAME/.jupyter" CONFIG_DIR="$1"
CONFIG_FILE="$CONFIG_DIR/jupyter_server_config.py" CONFIG_FILE="$2"
# Make sure the config file exists or create it with proper permissions # Make sure the config file exists or create it with proper permissions
test -d "$CONFIG_DIR" || sudo_if mkdir "$CONFIG_DIR" test -d "$CONFIG_DIR" || sudo_if mkdir "$CONFIG_DIR"
test -f "$CONFIG_FILE" || sudo_if touch "$CONFIG_FILE" test -f "$CONFIG_FILE" || sudo_if touch "$CONFIG_FILE"
# Don't write the same config more than once # Don't write the same config more than once
grep -q "$1" "$CONFIG_FILE" || echo "$1" >> "$CONFIG_FILE" grep -q "$3" "$CONFIG_FILE" || echo "$3" >> "$CONFIG_FILE"
} }
install_python() { install_python() {
@ -461,13 +468,26 @@ if [ "${INSTALL_JUPYTERLAB}" = "true" ]; then
exit 1 exit 1
fi fi
install_user_package jupyterlab INSTALL_UNDER_ROOT=true
install_user_package jupyterlab-git if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then
INSTALL_UNDER_ROOT=false
fi
install_user_package $INSTALL_UNDER_ROOT jupyterlab
install_user_package $INSTALL_UNDER_ROOT jupyterlab-git
# Configure JupyterLab if needed # Configure JupyterLab if needed
if [ -n "${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}" ]; then if [ -n "${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}" ]; then
add_user_jupyter_config "c.ServerApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'" # Resolve config directory
add_user_jupyter_config "c.NotebookApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'" CONFIG_DIR="/root/.jupyter"
if [ "$INSTALL_UNDER_ROOT" = false ]; then
CONFIG_DIR="/home/$USERNAME/.jupyter"
fi
CONFIG_FILE="$CONFIG_DIR/jupyter_server_config.py"
add_user_jupyter_config $CONFIG_DIR $CONFIG_FILE "c.ServerApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'"
add_user_jupyter_config $CONFIG_DIR $CONFIG_FILE "c.NotebookApp.allow_origin = '${CONFIGURE_JUPYTERLAB_ALLOW_ORIGIN}'"
fi fi
fi fi

View file

@ -21,7 +21,6 @@ Installs Ruby, rvm, rbenv, common Ruby utilities, and needed dependencies.
### VS Code Extensions ### VS Code Extensions
- `rebornix.Ruby`
- `shopify.ruby-lsp` - `shopify.ruby-lsp`

View file

@ -1,6 +1,6 @@
{ {
"id": "ruby", "id": "ruby",
"version": "1.1.1", "version": "1.2.0",
"name": "Ruby (via rvm)", "name": "Ruby (via rvm)",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/ruby", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/ruby",
"description": "Installs Ruby, rvm, rbenv, common Ruby utilities, and needed dependencies.", "description": "Installs Ruby, rvm, rbenv, common Ruby utilities, and needed dependencies.",
@ -21,7 +21,6 @@
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [
"rebornix.Ruby",
"shopify.ruby-lsp" "shopify.ruby-lsp"
] ]
} }

View file

@ -24,6 +24,7 @@ DEFAULT_GEMS="rake"
RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB" RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com
keyserver hkp://keyserver.ubuntu.com:80
keyserver hkps://keys.openpgp.org keyserver hkps://keys.openpgp.org
keyserver hkp://keyserver.pgp.com" keyserver hkp://keyserver.pgp.com"

View file

@ -23,7 +23,6 @@ Installs Rust, common Rust utilities, and their required dependencies
### VS Code Extensions ### VS Code Extensions
- `vadimcn.vscode-lldb` - `vadimcn.vscode-lldb`
- `mutantdino.resourcemonitor`
- `rust-lang.rust-analyzer` - `rust-lang.rust-analyzer`
- `tamasfe.even-better-toml` - `tamasfe.even-better-toml`
- `serayuzgur.crates` - `serayuzgur.crates`

View file

@ -1,6 +1,6 @@
{ {
"id": "rust", "id": "rust",
"version": "1.1.0", "version": "1.1.1",
"name": "Rust", "name": "Rust",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/rust", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/rust",
"description": "Installs Rust, common Rust utilities, and their required dependencies", "description": "Installs Rust, common Rust utilities, and their required dependencies",

View file

@ -48,21 +48,6 @@ elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then
USERNAME=root USERNAME=root
fi fi
# Get central common setting
get_common_setting() {
if [ "${common_settings_file_loaded}" != "true" ]; then
curl -sfL "https://aka.ms/vscode-dev-containers/script-library/settings.env" 2>/dev/null -o /tmp/vsdc-settings.env || echo "Could not download settings file. Skipping."
common_settings_file_loaded=true
fi
if [ -f "/tmp/vsdc-settings.env" ]; then
local multi_line=""
if [ "$2" = "true" ]; then multi_line="-z"; fi
local result="$(grep ${multi_line} -oP "$1=\"?\K[^\"]+" /tmp/vsdc-settings.env | tr -d '\0')"
if [ ! -z "${result}" ]; then declare -g $1="${result}"; fi
fi
echo "$1=${!1}"
}
# Figure out correct version of a three part version number is not passed # Figure out correct version of a three part version number is not passed
find_version_from_git_tags() { find_version_from_git_tags() {
local variable_name=$1 local variable_name=$1

View file

@ -1,5 +1,9 @@
## Licensing
On August 10, 2023, HashiCorp announced a change of license for its products, including Terraform. After ~9 years of Terraform being open source under the MPL v2 license, it was to move under a non-open source BSL v1.1 license, starting from the next (1.6) version. See https://github.com/hashicorp/terraform/blob/main/LICENSE
## OS Support ## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.

View file

@ -32,6 +32,10 @@ Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects la
## Licensing
On August 10, 2023, HashiCorp announced a change of license for its products, including Terraform. After ~9 years of Terraform being open source under the MPL v2 license, it was to move under a non-open source BSL v1.1 license, starting from the next (1.6) version. See https://github.com/hashicorp/terraform/blob/main/LICENSE
## OS Support ## OS Support
This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed.

View file

@ -1,6 +1,6 @@
{ {
"id": "terraform", "id": "terraform",
"version": "1.3.4", "version": "1.3.5",
"name": "Terraform, tflint, and TFGrunt", "name": "Terraform, tflint, and TFGrunt",
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/terraform",
"description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.", "description": "Installs the Terraform CLI and optionally TFLint and Terragrunt. Auto-detects latest version and installs needed dependencies.",

View file

@ -77,6 +77,25 @@ receive_gpg_keys() {
sleep 10s sleep 10s
fi fi
done done
# If all attempts fail, try getting the keyserver IP address and explicitly passing it to gpg
if [ "${gpg_ok}" = "false" ]; then
retry_count=0;
echo "(*) Resolving GPG keyserver IP address..."
local keyserver_ip_address=$( dig +short keyserver.ubuntu.com | head -n1 )
echo "(*) GPG keyserver IP address $keyserver_ip_address"
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "3" ];
do
echo "(*) Downloading GPG key..."
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys --keyserver ${keyserver_ip_address}) 2>&1 && gpg_ok="true"
if [ "${gpg_ok}" != "true" ]; then
echo "(*) Failed getting key, retring in 10s..."
(( retry_count++ ))
sleep 10s
fi
done
fi
set -e set -e
if [ "${gpg_ok}" = "false" ]; then if [ "${gpg_ok}" = "false" ]; then
echo "(!) Failed to get gpg key." echo "(!) Failed to get gpg key."
@ -183,7 +202,7 @@ ensure_cosign() {
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
# Install dependencies if missing # Install dependencies if missing
check_packages curl ca-certificates gnupg2 dirmngr coreutils unzip check_packages curl ca-certificates gnupg2 dirmngr coreutils unzip dnsutils
if ! type git > /dev/null 2>&1; then if ! type git > /dev/null 2>&1; then
check_packages git check_packages git
fi fi

View file

@ -9,6 +9,9 @@ source dev-container-features-test-lib
check "default-shell-is-zsh" bash -c "getent passwd $(whoami) | awk -F: '{ print $7 }' | grep '/bin/zsh'" check "default-shell-is-zsh" bash -c "getent passwd $(whoami) | awk -F: '{ print $7 }' | grep '/bin/zsh'"
# check it overrides the ~/.zshrc with default dev containers template # check it overrides the ~/.zshrc with default dev containers template
check "default-zshrc-is-dev-container-template" bash -c "cat ~/.zshrc | grep ZSH_THEME | grep devcontainers" check "default-zshrc-is-dev-container-template" bash -c "cat ~/.zshrc | grep ZSH_THEME | grep devcontainers"
check "zsh-path-contains-local-bin" zsh -l -c "echo $PATH | grep '/home/devcontainer/.local/bin'"
check "Ensure .zprofile is owned by remoteUser" bash -c "stat -c '%U' /home/devcontainer/.zprofile | grep devcontainer"
# Report result # Report result
reportResults reportResults

View file

@ -0,0 +1,12 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
# Definition specific tests
check "default-zsh-with-no-zshrc" bash -c "[ ! -e ~/.zshrc ]"
# Report result
reportResults

View file

@ -4,12 +4,11 @@ set -e
# Optional: Import test library # Optional: Import test library
source dev-container-features-test-lib source dev-container-features-test-lib
# Definition specific tests # Definition specific tests
function file_not_overridden() { function file_not_overridden() {
cat ~/.zshrc | grep 'alias fnomockalias=' | grep testingmock cat $1 | grep 'alias fnomockalias=' | grep testingmock
} }
check "default-zsh-with-no-zshrc" file_not_overridden check "default-zsh-with-no-zshrc" file_not_overridden /home/devcontainer/.zshrc
# Report result # Report result
reportResults reportResults

View file

@ -0,0 +1,13 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
# Definition specific tests
check "user is customUser" grep customUser <(whoami)
check "home is /customHome" grep "/customHome" <(getent passwd customUser | cut -d: -f6)
# Report result
reportResults

View file

@ -0,0 +1,4 @@
FROM ubuntu:focal
RUN groupadd customUser -g 30000 && \
useradd customUser -u 30000 -g 30000 --create-home --home-dir /customHome

View file

@ -0,0 +1,13 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
# Definition specific tests
check "user is customUser" grep customUser <(whoami)
check "home is /home/customUser" grep "/home/customUser" <(getent passwd customUser | cut -d: -f6)
# Report result
reportResults

View file

@ -108,17 +108,29 @@
} }
}, },
"configure_zsh_as_default_shell": { "configure_zsh_as_default_shell": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu", "image": "ubuntu",
"features": { "features": {
"common-utils": { "common-utils": {
"installZsh": true,
"configureZshAsDefaultShell": true "configureZshAsDefaultShell": true
} }
},
"remoteUser": "devcontainer"
},
"configure_zsh_no_template_second_step": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"postCreateCommand": "echo alias fnomockalias=testingmock >> /home/devcontainer/.zshrc",
"remoteUser": "devcontainer",
"features": {
"common-utils": {
"installZsh": true,
"installOhMyZshConfig": false
}
} }
}, },
"configure_zsh_no_template": { "configure_zsh_no_template_first_step": {
"image": "mcr.microsoft.com/devcontainers/base:ubuntu", "image": "debian:bullseye",
"postCreateCommand": "echo alias fnomockalias=testingmock >> /root/.zshrc", "remoteUser": "devcontainer",
"remoteUser": "root",
"features": { "features": {
"common-utils": { "common-utils": {
"installZsh": true, "installZsh": true,
@ -173,5 +185,21 @@
"configureZshAsDefaultShell": true "configureZshAsDefaultShell": true
} }
} }
},
"devcontainer-custom-home": {
"build": {
"dockerfile": "Dockerfile"
},
"remoteUser": "customUser",
"features": {
"common-utils": {}
}
},
"devcontainer-custom-user-default-home": {
"image": "mcr.microsoft.com/devcontainers/base:alpine",
"remoteUser": "customUser",
"features": {
"common-utils": {}
}
} }
} }

View file

@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
# Feature specific tests
check "version" docker --version
check "docker-ps" bash -c "docker ps"
check "docker-init-exists" bash -c "ls /usr/local/share/docker-init.sh"
check "log-exists" bash -c "ls /tmp/dockerd.log"
check "log-for-completion" bash -c "cat /tmp/dockerd.log | grep 'Daemon has completed initialization'"
check "log-contents" bash -c "cat /tmp/dockerd.log | grep 'API listen on /var/run/docker.sock'"
# Report result
reportResults

View file

@ -67,5 +67,14 @@
} }
}, },
"remoteUser": "node" "remoteUser": "node"
},
// DO NOT REMOVE: This scenario is used by the docker-in-docker-stress-test workflow
"docker_with_on_create_command": {
"image": "mcr.microsoft.com/devcontainers/base:debian",
"features": {
"docker-in-docker": {}
},
"remoteUser": "vscode",
"onCreateCommand": "docker ps && sleep 5s && docker ps"
} }
} }

View file

@ -8,10 +8,10 @@ source dev-container-features-test-lib
# Feature specific tests # Feature specific tests
check "version" docker --version check "version" docker --version
check "docker-init-exists" bash -c "ls /usr/local/share/docker-init.sh" check "docker-init-exists" bash -c "ls /usr/local/share/docker-init.sh"
check "docker-ps" bash -c "docker ps"
check "log-exists" bash -c "ls /tmp/dockerd.log" check "log-exists" bash -c "ls /tmp/dockerd.log"
check "log-for-completion" bash -c "cat /tmp/dockerd.log | grep 'Daemon has completed initialization'" check "log-for-completion" bash -c "cat /tmp/dockerd.log | grep 'Daemon has completed initialization'"
check "log-contents" bash -c "cat /tmp/dockerd.log | grep 'API listen on /var/run/docker.sock'" check "log-contents" bash -c "cat /tmp/dockerd.log | grep 'API listen on /var/run/docker.sock'"
check "docker-ps" bash -c "docker ps"
# Report result # Report result
reportResults reportResults

View file

@ -0,0 +1,4 @@
#!/bin/bash
export DOTNET_NOLOGO=true
export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
export DOTNET_GENERATE_ASPNET_CERTIFICATE=false

View file

@ -0,0 +1,71 @@
#!/bin/bash
# Prints the latest dotnet version in the specified channel
# Usage: fetch_latest_version_in_channel <channel> [<runtime>]
# Example: fetch_latest_version_in_channel "LTS"
# Example: fetch_latest_version_in_channel "6.0" "dotnet"
# Example: fetch_latest_version_in_channel "6.0" "aspnetcore"
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"
elif [ "$runtime" = "aspnetcore" ]; then
wget -qO- "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$channel/latest.version"
else
wget -qO- "https://dotnetcli.azureedge.net/dotnet/Sdk/$channel/latest.version"
fi
}
# Prints the latest dotnet version
# Usage: fetch_latest_version [<runtime>]
# Example: fetch_latest_version
# Example: fetch_latest_version "dotnet"
# Example: fetch_latest_version "aspnetcore"
fetch_latest_version() {
local runtime="$1"
local sts_version
local lts_version
sts_version=$(fetch_latest_version_in_channel "STS" "$runtime")
lts_version=$(fetch_latest_version_in_channel "LTS" "$runtime")
if [[ "$sts_version" > "$lts_version" ]]; then
echo "$sts_version"
else
echo "$lts_version"
fi
}
# Asserts that the specified .NET SDK version is installed
# Returns a non-zero exit code if the check fails
# Usage: is_dotnet_sdk_version_installed <version>
# Example: is_dotnet_sdk_version_installed "6.0"
# Example: is_dotnet_sdk_version_installed "6.0.412"
is_dotnet_sdk_version_installed() {
local expected="$1"
dotnet --list-sdks | grep --fixed-strings --silent "$expected"
return $?
}
# Asserts that the specified .NET Runtime version is installed
# Returns a non-zero exit code if the check fails
# Usage: is_dotnet_runtime_version_installed <version>
# Example: is_dotnet_runtime_version_installed "6.0"
# Example: is_dotnet_runtime_version_installed "6.0.412"
is_dotnet_runtime_version_installed() {
local expected="$1"
dotnet --list-runtimes | grep --fixed-strings --silent "Microsoft.NETCore.App $expected"
return $?
}
# Asserts that the specified ASP.NET Core Runtime version is installed
# Returns a non-zero exit code if the check fails
# Usage: is_aspnetcore_runtime_version_installed <version>
# Example: is_aspnetcore_runtime_version_installed "6.0"
# Example: is_aspnetcore_runtime_version_installed "6.0.412"
is_aspnetcore_runtime_version_installed() {
local expected="$1"
dotnet --list-runtimes | grep --fixed-strings --silent "Microsoft.AspNetCore.App $expected"
return $?
}

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet version 6.0.301 installed as default" bash -c "dotnet --version | grep 6.0.301"
check "dotnet version 5.0 installed" bash -c "ls -l /usr/local/dotnet | grep 5.0"
check "dotnet version 3.1.420 installed" bash -c "ls -l /usr/local/dotnet | grep 3.1.420"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -0,0 +1,27 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
expected=$(fetch_latest_version "aspnetcore")
check "Latest ASP.NET Core Runtime version installed" \
is_aspnetcore_runtime_version_installed "$expected"
# Expect this check to fail in November 2023 when .NET 8.0 becomes GA
check "It is a flavor of .NET 7.0" \
is_aspnetcore_runtime_version_installed "7.0"
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 3 is installed" bash -c "dotnet --list-sdks | grep '3\.[0-9]*\.[0-9]*'"
check "dotnet version 3 installed" bash -c "ls -l /usr/share/dotnet/sdk | grep '3\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 6 is installed" bash -c "dotnet --list-sdks | grep '6\.[0-9]*\.[0-9]*'"
check "dotnet version 6 installed" bash -c "ls -l /usr/share/dotnet/sdk | grep '6\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 6 is installed" bash -c "dotnet --list-sdks | grep '6\.[0-9]*\.[0-9]*'"
check "dotnet version 6 installed" bash -c "ls -l /usr/share/dotnet/sdk | grep '6\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 6 is installed" bash -c "dotnet --list-sdks | grep '6\.[0-9]*\.[0-9]*'"
check "dotnet version 6 installed" bash -c "ls -l /usr/lib/dotnet/sdk | grep '6\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 7 is installed" bash -c "dotnet --list-sdks | grep '7\.[0-9]*\.[0-9]*'"
check "dotnet version 7 installed" bash -c "ls -l /usr/share/dotnet/sdk | grep '7\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 7 is installed" bash -c "dotnet --list-sdks | grep '7\.[0-9]*\.[0-9]*'"
check "dotnet version 7 installed" bash -c "ls -l /usr/lib/dotnet/sdk | grep '7\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -0,0 +1,24 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
check ".NET SDK 8.0.100-preview.6.23330.14 installed" \
is_dotnet_sdk_version_installed "8.0.100-preview.6.23330.14"
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

View file

@ -0,0 +1,25 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
# From https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools
check "Install a .NET global tool" \
dotnet tool install --global dotnetsay
check "Run the tool" \
dotnetsay "$(dotnet --info)"
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 7 is installed" bash -c "dotnet --list-sdks | grep '7\.[0-9]*\.[0-9]*'"
check "dotnet version 7 installed" bash -c "ls -l /usr/lib/dotnet/sdk | grep '7\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

View file

@ -0,0 +1,30 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
expected=$(fetch_latest_version)
check "Latest .NET SDK version installed" \
is_dotnet_sdk_version_installed "$expected"
# Expect this check to fail in November 2023 when .NET 8.0 becomes GA
check "It is a flavor of .NET 7.0" \
is_dotnet_sdk_version_installed "7.0"
check "Build and run example project" \
dotnet run --project projects/net7.0
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -2,16 +2,25 @@
set -e set -e
# Optional: Import test library # Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks # Feature-specific tests
check "some major version of dotnet 6 is installed" bash -c "dotnet --list-sdks | grep '6\.[0-9]*\.[0-9]*'" # The 'check' command comes from the dev-container-features-test-lib. Syntax is...
check "dotnet version 6 installed" bash -c "ls -l /usr/share/dotnet/sdk | grep '6\.[0-9]*\.[0-9]*'" # check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
# Verify current symlink exists and works expected=$(fetch_latest_version_in_channel "LTS")
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result check "Latest LTS version installed" \
is_dotnet_sdk_version_installed "$expected"
check "Build and run example project" \
dotnet run --project projects/net6.0
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults reportResults

View file

@ -0,0 +1,30 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
check ".NET SDK 8.0 installed" \
is_dotnet_sdk_version_installed "8.0"
check ".NET SDK 7.0 installed" \
is_dotnet_sdk_version_installed "7.0"
check ".NET SDK 6.0 installed" \
is_dotnet_sdk_version_installed "6.0"
check "Build example class library" \
dotnet build projects/multitargeting
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -0,0 +1,27 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
expected=$(fetch_latest_version "dotnet")
check "Latest .NET Runtime version installed" \
is_dotnet_runtime_version_installed "$expected"
# Expect this check to fail in November 2023 when .NET 8.0 becomes GA
check "It is a flavor of .NET 7.0" \
is_dotnet_runtime_version_installed "7.0"
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -0,0 +1,26 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
expected=$(fetch_latest_version_in_channel "3.1")
check ".NET Core SDK 3.1 installed" \
is_dotnet_sdk_version_installed "$expected"
check "Build and run example 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

View file

@ -0,0 +1,24 @@
#!/bin/bash
set -e
# Optional: Import test library bundled with the devcontainer CLI
# See https://github.com/devcontainers/cli/blob/HEAD/docs/features/test.md#dev-container-features-test-lib
# Provides the 'check' and 'reportResults' commands.
source dev-container-features-test-lib
# Feature-specific tests
# The 'check' command comes from the dev-container-features-test-lib. Syntax is...
# check <LABEL> <cmd> [args...]
source dotnet_env.sh
source dotnet_helpers.sh
check ".NET SDK 5.0.3xx installed" \
is_dotnet_sdk_version_installed "5.0.3"
check "Build and run example project" \
dotnet run --project projects/net5.0
# Report results
# If any of the checks above exited with a non-zero exit code, the test will fail.
reportResults

View file

@ -1,17 +0,0 @@
#!/bin/bash
set -e
# Optional: Import test library
source dev-container-features-test-lib
check "dotnet sdks" dotnet --list-sdks
check "some major version of dotnet 7 is installed" bash -c "dotnet --list-sdks | grep '7\.[0-9]*\.[0-9]*'"
check "dotnet version 7 installed" bash -c "ls -l /usr/local/dotnet | grep '7\.[0-9]*\.[0-9]*'"
# Verify current symlink exists and works
check "current link dotnet" /usr/local/dotnet/current/dotnet --info
check "current link sdk" ls -l /usr/local/dotnet/current/sdk
# Report result
reportResults

2
test/dotnet/projects/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
bin
obj

View file

@ -0,0 +1,4 @@
public class Class1
{
}

View file

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
string json = @"{
""Name"": ""Inception"",
""ReleaseDate"": ""2010-07-08T00:00:00"",
""Genres"": [
""Action"",
""Thriller""
]
}";
Movie? m = JsonConvert.DeserializeObject<Movie>(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
{
public string Name { get; set; } = "Default Name";
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; } = new List<string>();
}

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,30 @@
using Newtonsoft.Json;
string json = @"{
""Name"": ""Inception"",
""ReleaseDate"": ""2010-07-08T00:00:00"",
""Genres"": [
""Action"",
""Thriller""
]
}";
Movie? m = JsonConvert.DeserializeObject<Movie>(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
{
public string Name { get; set; } = "Default Name";
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; } = new List<string>();
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,30 @@
using Newtonsoft.Json;
string json = @"{
""Name"": ""Inception"",
""ReleaseDate"": ""2010-07-08T00:00:00"",
""Genres"": [
""Action"",
""Thriller""
]
}";
Movie? m = JsonConvert.DeserializeObject<Movie>(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
{
public string Name { get; set; } = "Default Name";
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; } = new List<string>();
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,32 @@
using Newtonsoft.Json;
string json = """
{
"Name": "Inception",
"ReleaseDate": "2010-07-08T00:00:00",
"Genres": [
"Action",
"Thriller"
]
}
""";
Movie? m = JsonConvert.DeserializeObject<Movie>(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
{
public string Name { get; set; } = "Default Name";
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; } = new List<string>();
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
public static void Main()
{
string json = @"{
""Name"": ""Inception"",
""ReleaseDate"": ""2010-07-08T00:00:00"",
""Genres"": [
""Action"",
""Thriller""
]
}";
Movie? m = JsonConvert.DeserializeObject<Movie>(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
{
public string Name { get; set; } = "Default Name";
public DateTime ReleaseDate { get; set; }
public List<string> Genres { get; set; } = new List<string>();
}

View file

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show more