parent
8759124c53
commit
9efe4b3c4a
54 changed files with 6530 additions and 0 deletions
16
collection/anaconda/feature.json
Normal file
16
collection/anaconda/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "anaconda",
|
||||
"name": "Anaconda",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter an anaconda version."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
99
collection/anaconda/install.sh
Normal file
99
collection/anaconda/install.sh
Normal file
|
@ -0,0 +1,99 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/anaconda.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./anaconda-debian.sh [Conda version] [CONDA_DIR] [Non-root user] [Add rc files flag]
|
||||
|
||||
VERSION=${1:-"latest"}
|
||||
export CONDA_DIR=${2:-"/usr/local/conda"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
|
||||
set -eux
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
architecture="$(uname -m)"
|
||||
if [ "${architecture}" != "x86_64" ]; then
|
||||
echo "(!) Architecture $architecture unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt-get update
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Install Conda if it's missing
|
||||
if ! conda --version &> /dev/null ; then
|
||||
# Install dependencies
|
||||
check_packages wget ca-certificates
|
||||
|
||||
mkdir -p $CONDA_DIR
|
||||
chown ${USERNAME}:root $CONDA_DIR
|
||||
echo "Installing Anaconda..."
|
||||
|
||||
CONDA_VERSION=$VERSION
|
||||
if [ "${VERSION}" = "latest" ] || [ "${VERSION}" = "lts" ]; then
|
||||
CONDA_VERSION="2021.11"
|
||||
fi
|
||||
|
||||
su --login -c "wget -q https://repo.anaconda.com/archive/Anaconda3-${CONDA_VERSION}-Linux-x86_64.sh -O /tmp/anaconda-install.sh \
|
||||
&& /bin/bash /tmp/anaconda-install.sh -u -b -p ${CONDA_DIR}" ${USERNAME} 2>&1
|
||||
|
||||
if [ "${VERSION}" = "latest" ] || [ "${VERSION}" = "lts" ]; then
|
||||
PATH=$PATH:${CONDA_DIR}/bin
|
||||
conda update -y conda
|
||||
fi
|
||||
|
||||
rm /tmp/anaconda-install.sh
|
||||
updaterc "export CONDA_DIR=${CONDA_DIR}/bin"
|
||||
fi
|
||||
|
||||
echo "Done!"
|
21
collection/aws-cli/feature.json
Normal file
21
collection/aws-cli/feature.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "aws-cli",
|
||||
"name": "AWS CLI",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": [
|
||||
"latest"
|
||||
],
|
||||
"default": "latest",
|
||||
"description": "Select or enter an AWS CLI version. (Available versions here: https://github.com/aws/aws-cli/blob/v2/CHANGELOG.rst)"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"AmazonWebServices.aws-toolkit-vscode"
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
142
collection/aws-cli/install.sh
Normal file
142
collection/aws-cli/install.sh
Normal file
|
@ -0,0 +1,142 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/awscli.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./awscli-debian.sh [AWS CLI version]
|
||||
|
||||
set -e
|
||||
|
||||
AWSCLI_VERSION=${1:-"latest"}
|
||||
AWSCLI_GPG_KEY=FB5DB77FD5C118B80511ADA8A6310ACC4672475C
|
||||
AWSCLI_GPG_KEY_MATERIAL="-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
|
||||
ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
|
||||
PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
|
||||
TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
|
||||
gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
|
||||
C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
|
||||
94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
|
||||
lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
|
||||
fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
|
||||
EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
|
||||
XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
|
||||
tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4WIQT7
|
||||
Xbd/1cEYuAURraimMQrMRnJHXAUCXYKvtQIbAwUJB4TOAAULCQgHAgYVCgkICwIE
|
||||
FgIDAQIeAQIXgAAKCRCmMQrMRnJHXJIXEAChLUIkg80uPUkGjE3jejvQSA1aWuAM
|
||||
yzy6fdpdlRUz6M6nmsUhOExjVIvibEJpzK5mhuSZ4lb0vJ2ZUPgCv4zs2nBd7BGJ
|
||||
MxKiWgBReGvTdqZ0SzyYH4PYCJSE732x/Fw9hfnh1dMTXNcrQXzwOmmFNNegG0Ox
|
||||
au+VnpcR5Kz3smiTrIwZbRudo1ijhCYPQ7t5CMp9kjC6bObvy1hSIg2xNbMAN/Do
|
||||
ikebAl36uA6Y/Uczjj3GxZW4ZWeFirMidKbtqvUz2y0UFszobjiBSqZZHCreC34B
|
||||
hw9bFNpuWC/0SrXgohdsc6vK50pDGdV5kM2qo9tMQ/izsAwTh/d/GzZv8H4lV9eO
|
||||
tEis+EpR497PaxKKh9tJf0N6Q1YLRHof5xePZtOIlS3gfvsH5hXA3HJ9yIxb8T0H
|
||||
QYmVr3aIUes20i6meI3fuV36VFupwfrTKaL7VXnsrK2fq5cRvyJLNzXucg0WAjPF
|
||||
RrAGLzY7nP1xeg1a0aeP+pdsqjqlPJom8OCWc1+6DWbg0jsC74WoesAqgBItODMB
|
||||
rsal1y/q+bPzpsnWjzHV8+1/EtZmSc8ZUGSJOPkfC7hObnfkl18h+1QtKTjZme4d
|
||||
H17gsBJr+opwJw/Zio2LMjQBOqlm3K1A4zFTh7wBC7He6KPQea1p2XAMgtvATtNe
|
||||
YLZATHZKTJyiqA==
|
||||
=vYOk
|
||||
-----END PGP PUBLIC KEY BLOCK-----"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
check_packages curl ca-certificates gnupg2 dirmngr unzip
|
||||
|
||||
verify_aws_cli_gpg_signature() {
|
||||
local filePath=$1
|
||||
local sigFilePath=$2
|
||||
|
||||
get_common_setting AWSCLI_GPG_KEY
|
||||
get_common_setting AWSCLI_GPG_KEY_MATERIAL true
|
||||
local awsGpgKeyring=aws-cli-public-key.gpg
|
||||
|
||||
echo "${AWSCLI_GPG_KEY_MATERIAL}" | gpg --dearmor > "./${awsGpgKeyring}"
|
||||
gpg --batch --quiet --no-default-keyring --keyring "./${awsGpgKeyring}" --verify "${sigFilePath}" "${filePath}"
|
||||
local status=$?
|
||||
|
||||
rm "./${awsGpgKeyring}"
|
||||
|
||||
return ${status}
|
||||
}
|
||||
|
||||
install() {
|
||||
local scriptZipFile=awscli.zip
|
||||
local scriptSigFile=awscli.sig
|
||||
|
||||
# See Linux install docs at https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html
|
||||
if [ "${AWSCLI_VERSION}" != "latest" ]; then
|
||||
local versionStr=-${AWSCLI_VERSION}
|
||||
fi
|
||||
architecture=$(dpkg --print-architecture)
|
||||
case "${architecture}" in
|
||||
amd64) architectureStr=x86_64 ;;
|
||||
arm64) architectureStr=aarch64 ;;
|
||||
*)
|
||||
echo "AWS CLI does not support machine architecture '$architecture'. Please use an x86-64 or ARM64 machine."
|
||||
exit 1
|
||||
esac
|
||||
local scriptUrl=https://awscli.amazonaws.com/awscli-exe-linux-${architectureStr}${versionStr}.zip
|
||||
curl "${scriptUrl}" -o "${scriptZipFile}"
|
||||
curl "${scriptUrl}.sig" -o "${scriptSigFile}"
|
||||
|
||||
verify_aws_cli_gpg_signature "$scriptZipFile" "$scriptSigFile"
|
||||
if (( $? > 0 )); then
|
||||
echo "Could not verify GPG signature of AWS CLI install script. Make sure you provided a valid version."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unzip "${scriptZipFile}"
|
||||
./aws/install
|
||||
|
||||
rm -rf ./aws
|
||||
}
|
||||
|
||||
echo "(*) Installing AWS CLI..."
|
||||
|
||||
install
|
||||
|
||||
echo "Done!"
|
17
collection/azure-cli/feature.json
Normal file
17
collection/azure-cli/feature.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "azure-cli",
|
||||
"name": "Azure CLI",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter an Azure CLI version. (Available versions may vary by Linux distribution.)"
|
||||
}
|
||||
},
|
||||
"extensions": ["ms-vscode.azurecli"],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
186
collection/azure-cli/install.sh
Normal file
186
collection/azure-cli/install.sh
Normal file
|
@ -0,0 +1,186 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/azcli.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./azcli-debian.sh
|
||||
|
||||
set -e
|
||||
|
||||
AZ_VERSION=${1:-"latest"}
|
||||
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
|
||||
AZCLI_ARCHIVE_ARCHITECTURES="amd64"
|
||||
AZCLI_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# 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_version_soft_match() {
|
||||
|
||||
# Version
|
||||
local variable_name="$1"
|
||||
local requested_version=${!variable_name}
|
||||
# Package Name
|
||||
local package_name="$2"
|
||||
# Exit on no match?
|
||||
local exit_on_no_match="${3:-true}"
|
||||
|
||||
# Ensure we've exported useful variables
|
||||
. /etc/os-release
|
||||
local architecture="$(dpkg --print-architecture)"
|
||||
|
||||
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 \"${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 ${variable_name}="=${fuzzy_version}"
|
||||
echo "${variable_name} ${!variable_name}"
|
||||
}
|
||||
|
||||
install_using_apt() {
|
||||
# Install dependencies
|
||||
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
|
||||
get_common_setting MICROSOFT_GPG_KEYS_URI
|
||||
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
|
||||
apt-get update
|
||||
|
||||
if [ "${AZ_VERSION}" = "latest" ] || [ "${AZ_VERSION}" = "lts" ] || [ "${AZ_VERSION}" = "stable" ]; then
|
||||
# Empty, meaning grab the "latest" in the apt repo
|
||||
AZ_VERSION=""
|
||||
else
|
||||
# Sets AZ_VERSION to our desired version, if match found.
|
||||
apt_cache_version_soft_match AZ_VERSION "azure-cli" false
|
||||
if [ "$?" != 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! (apt-get install -yq azure-cli${AZ_VERSION}); then
|
||||
rm -f /etc/apt/sources.list.d/azure-cli.list
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
install_using_pip() {
|
||||
echo "(*) No pre-built binaries available in apt-cache. Installing via pip3."
|
||||
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
|
||||
fi
|
||||
export PIPX_HOME=/usr/local/pipx
|
||||
mkdir -p ${PIPX_HOME}
|
||||
export PIPX_BIN_DIR=/usr/local/bin
|
||||
export PYTHONUSERBASE=/tmp/pip-tmp
|
||||
export PIP_CACHE_DIR=/tmp/pip-tmp/cache
|
||||
pipx_bin=pipx
|
||||
if ! type pipx > /dev/null 2>&1; then
|
||||
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
|
||||
pipx_bin=/tmp/pip-tmp/bin/pipx
|
||||
fi
|
||||
|
||||
if [ "${AZ_VERSION}" = "latest" ] || [ "${AZ_VERSION}" = "lts" ] || [ "${AZ_VERSION}" = "stable" ]; then
|
||||
# Empty, meaning grab the "latest" in the apt repo
|
||||
ver=""
|
||||
else
|
||||
ver="==${AZ_VERSION}"
|
||||
fi
|
||||
|
||||
set +e
|
||||
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' -f azure-cli${ver}
|
||||
|
||||
# Fail gracefully
|
||||
if [ "$?" != 0 ]; then
|
||||
echo "Could not install azure-cli${ver} via pip"
|
||||
rm -rf /tmp/pip-tmp
|
||||
return 1
|
||||
fi
|
||||
set -e
|
||||
}
|
||||
|
||||
# See if we're on x86_64 and if so, install via apt-get, otherwise use pip3
|
||||
echo "(*) Installing Azure CLI..."
|
||||
. /etc/os-release
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
if [[ "${AZCLI_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${AZCLI_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]]; then
|
||||
install_using_apt || use_pip="true"
|
||||
else
|
||||
use_pip="true"
|
||||
fi
|
||||
|
||||
if [ "${use_pip}" = "true" ]; then
|
||||
install_using_pip
|
||||
|
||||
if [ "$?" != 0 ]; then
|
||||
echo "Please provide a valid version for your distribution ${ID} ${VERSION_CODENAME} (${architecture})."
|
||||
echo
|
||||
echo "Valid versions in current apt-cache"
|
||||
apt-cache madison azure-cli | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Done!"
|
52
collection/common/feature.json
Normal file
52
collection/common/feature.json
Normal file
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"id": "common",
|
||||
"name": "common",
|
||||
"description": "common",
|
||||
"options": {
|
||||
"installZsh": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install ZSH?"
|
||||
},
|
||||
"installOhMyZsh": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install Oh My Zsh!?"
|
||||
},
|
||||
"upgradePackages": {
|
||||
"type":"boolean",
|
||||
"default": true,
|
||||
"description": "Upgrade OS packages?"
|
||||
},
|
||||
"username": {
|
||||
"type":"string",
|
||||
"proposals": ["vscode", "codespace", "none", "automatic"],
|
||||
"default": "automatic",
|
||||
"description": "Enter name of non-root user to configure or none to skip"
|
||||
},
|
||||
"uid": {
|
||||
"type":"string",
|
||||
"proposals": ["1000", "automatic"],
|
||||
"default": "automatic",
|
||||
"description": "Enter uid for non-root user"
|
||||
},
|
||||
"gid": {
|
||||
"type": "string",
|
||||
"proposals": ["1000", "automatic"],
|
||||
"default": "automatic",
|
||||
"description": "Enter gid for non-root user"
|
||||
},
|
||||
"nonFreePackages": {
|
||||
"type":"boolean",
|
||||
"default": true,
|
||||
"description": "Add packages from non-free Debian repository?"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"ms-dotnettools.csharp"
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
454
collection/common/install.sh
Normal file
454
collection/common/install.sh
Normal file
|
@ -0,0 +1,454 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/common.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./common-debian.sh [install zsh flag] [username] [user UID] [user GID] [upgrade packages flag] [install Oh My Zsh! flag] [Add non-free packages]
|
||||
|
||||
set -e
|
||||
|
||||
INSTALL_ZSH=${1:-"true"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
USER_UID=${3:-"automatic"}
|
||||
USER_GID=${4:-"automatic"}
|
||||
UPGRADE_PACKAGES=${5:-"true"}
|
||||
INSTALL_OH_MYS=${6:-"true"}
|
||||
ADD_NON_FREE_PACKAGES=${7:-"false"}
|
||||
SCRIPT_DIR="$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)"
|
||||
MARKER_FILE="/usr/local/etc/vscode-dev-containers/common"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
# If in automatic mode, determine if a user already exists, if not use vscode
|
||||
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=vscode
|
||||
fi
|
||||
elif [ "${USERNAME}" = "none" ]; then
|
||||
USERNAME=root
|
||||
USER_UID=0
|
||||
USER_GID=0
|
||||
fi
|
||||
|
||||
# Load markers to see which steps have already run
|
||||
if [ -f "${MARKER_FILE}" ]; then
|
||||
echo "Marker file found:"
|
||||
cat "${MARKER_FILE}"
|
||||
source "${MARKER_FILE}"
|
||||
fi
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Function to call apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Run install apt-utils to avoid debconf warning then verify presence of other common developer tools and dependencies
|
||||
if [ "${PACKAGES_ALREADY_INSTALLED}" != "true" ]; then
|
||||
|
||||
package_list="apt-utils \
|
||||
openssh-client \
|
||||
gnupg2 \
|
||||
dirmngr \
|
||||
iproute2 \
|
||||
procps \
|
||||
lsof \
|
||||
htop \
|
||||
net-tools \
|
||||
psmisc \
|
||||
curl \
|
||||
wget \
|
||||
rsync \
|
||||
ca-certificates \
|
||||
unzip \
|
||||
zip \
|
||||
nano \
|
||||
vim-tiny \
|
||||
less \
|
||||
jq \
|
||||
lsb-release \
|
||||
apt-transport-https \
|
||||
dialog \
|
||||
libc6 \
|
||||
libgcc1 \
|
||||
libkrb5-3 \
|
||||
libgssapi-krb5-2 \
|
||||
libicu[0-9][0-9] \
|
||||
liblttng-ust0 \
|
||||
libstdc++6 \
|
||||
zlib1g \
|
||||
locales \
|
||||
sudo \
|
||||
ncdu \
|
||||
man-db \
|
||||
strace \
|
||||
manpages \
|
||||
manpages-dev \
|
||||
init-system-helpers"
|
||||
|
||||
# Needed for adding manpages-posix and manpages-posix-dev which are non-free packages in Debian
|
||||
if [ "${ADD_NON_FREE_PACKAGES}" = "true" ]; then
|
||||
# Bring in variables from /etc/os-release like VERSION_CODENAME
|
||||
. /etc/os-release
|
||||
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i -E "s/deb-src http:\/\/(deb|httredir)\.debian\.org\/debian ${VERSION_CODENAME} main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME} main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i -E "s/deb http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i -E "s/deb-src http:\/\/(deb|httpredir)\.debian\.org\/debian ${VERSION_CODENAME}-updates main/deb http:\/\/\1\.debian\.org\/debian ${VERSION_CODENAME}-updates main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}\/updates main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i "s/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i "s/deb-src http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main/deb http:\/\/deb\.debian\.org\/debian ${VERSION_CODENAME}-backports main contrib non-free/" /etc/apt/sources.list
|
||||
# Handle bullseye location for security https://www.debian.org/releases/bullseye/amd64/release-notes/ch-information.en.html
|
||||
sed -i "s/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
|
||||
sed -i "s/deb-src http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main/deb http:\/\/security\.debian\.org\/debian-security ${VERSION_CODENAME}-security main contrib non-free/" /etc/apt/sources.list
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
package_list="${package_list} manpages-posix manpages-posix-dev"
|
||||
else
|
||||
apt_get_update_if_needed
|
||||
fi
|
||||
|
||||
# Install libssl1.1 if available
|
||||
if [[ ! -z $(apt-cache --names-only search ^libssl1.1$) ]]; then
|
||||
package_list="${package_list} libssl1.1"
|
||||
fi
|
||||
|
||||
# Install appropriate version of libssl1.0.x if available
|
||||
libssl_package=$(dpkg-query -f '${db:Status-Abbrev}\t${binary:Package}\n' -W 'libssl1\.0\.?' 2>&1 || echo '')
|
||||
if [ "$(echo "$LIlibssl_packageBSSL" | 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, 16.04, earlier
|
||||
package_list="${package_list} libssl1.0.0"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Packages to verify are installed: ${package_list}"
|
||||
apt-get -y install --no-install-recommends ${package_list} 2> >( grep -v 'debconf: delaying package configuration, since apt-utils is not installed' >&2 )
|
||||
|
||||
# Install git if not already installed (may be more recent than distro version)
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
PACKAGES_ALREADY_INSTALLED="true"
|
||||
fi
|
||||
|
||||
# Get to latest versions of all packages
|
||||
if [ "${UPGRADE_PACKAGES}" = "true" ]; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y upgrade --no-install-recommends
|
||||
apt-get autoremove -y
|
||||
fi
|
||||
|
||||
# Ensure at least the en_US.UTF-8 UTF-8 locale is available.
|
||||
# Common need for both applications and things like the agnoster ZSH theme.
|
||||
if [ "${LOCALE_ALREADY_SET}" != "true" ] && ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
|
||||
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
|
||||
locale-gen
|
||||
LOCALE_ALREADY_SET="true"
|
||||
fi
|
||||
|
||||
# Create or update a non-root user to match UID/GID.
|
||||
group_name="${USERNAME}"
|
||||
if id -u ${USERNAME} > /dev/null 2>&1; then
|
||||
# User exists, update if needed
|
||||
if [ "${USER_GID}" != "automatic" ] && [ "$USER_GID" != "$(id -g $USERNAME)" ]; then
|
||||
group_name="$(id -gn $USERNAME)"
|
||||
groupmod --gid $USER_GID ${group_name}
|
||||
usermod --gid $USER_GID $USERNAME
|
||||
fi
|
||||
if [ "${USER_UID}" != "automatic" ] && [ "$USER_UID" != "$(id -u $USERNAME)" ]; then
|
||||
usermod --uid $USER_UID $USERNAME
|
||||
fi
|
||||
else
|
||||
# Create user
|
||||
if [ "${USER_GID}" = "automatic" ]; then
|
||||
groupadd $USERNAME
|
||||
else
|
||||
groupadd --gid $USER_GID $USERNAME
|
||||
fi
|
||||
if [ "${USER_UID}" = "automatic" ]; then
|
||||
useradd -s /bin/bash --gid $USERNAME -m $USERNAME
|
||||
else
|
||||
useradd -s /bin/bash --uid $USER_UID --gid $USERNAME -m $USERNAME
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add add sudo support for non-root user
|
||||
if [ "${USERNAME}" != "root" ] && [ "${EXISTING_NON_ROOT_USER}" != "${USERNAME}" ]; then
|
||||
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
|
||||
chmod 0440 /etc/sudoers.d/$USERNAME
|
||||
EXISTING_NON_ROOT_USER="${USERNAME}"
|
||||
fi
|
||||
|
||||
# ** Shell customization section **
|
||||
if [ "${USERNAME}" = "root" ]; then
|
||||
user_rc_path="/root"
|
||||
else
|
||||
user_rc_path="/home/${USERNAME}"
|
||||
fi
|
||||
|
||||
# Restore user .bashrc defaults from skeleton file if it doesn't exist or is empty
|
||||
if [ ! -f "${user_rc_path}/.bashrc" ] || [ ! -s "${user_rc_path}/.bashrc" ] ; then
|
||||
cp /etc/skel/.bashrc "${user_rc_path}/.bashrc"
|
||||
fi
|
||||
|
||||
# Restore user .profile defaults from skeleton file if it doesn't exist or is empty
|
||||
if [ ! -f "${user_rc_path}/.profile" ] || [ ! -s "${user_rc_path}/.profile" ] ; then
|
||||
cp /etc/skel/.profile "${user_rc_path}/.profile"
|
||||
fi
|
||||
|
||||
# .bashrc/.zshrc snippet
|
||||
rc_snippet="$(cat << 'EOF'
|
||||
|
||||
if [ -z "${USER}" ]; then export USER=$(whoami); fi
|
||||
if [[ "${PATH}" != *"$HOME/.local/bin"* ]]; then export PATH="${PATH}:$HOME/.local/bin"; fi
|
||||
|
||||
# Display optional first run image specific notice if configured and terminal is interactive
|
||||
if [ -t 1 ] && [[ "${TERM_PROGRAM}" = "vscode" || "${TERM_PROGRAM}" = "codespaces" ]] && [ ! -f "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed" ]; then
|
||||
if [ -f "/usr/local/etc/vscode-dev-containers/first-run-notice.txt" ]; then
|
||||
cat "/usr/local/etc/vscode-dev-containers/first-run-notice.txt"
|
||||
elif [ -f "/workspaces/.codespaces/shared/first-run-notice.txt" ]; then
|
||||
cat "/workspaces/.codespaces/shared/first-run-notice.txt"
|
||||
fi
|
||||
mkdir -p "$HOME/.config/vscode-dev-containers"
|
||||
# Mark first run notice as displayed after 10s to avoid problems with fast terminal refreshes hiding it
|
||||
((sleep 10s; touch "$HOME/.config/vscode-dev-containers/first-run-notice-already-displayed") &)
|
||||
fi
|
||||
|
||||
# Set the default git editor if not already set
|
||||
if [ -z "$(git config --get core.editor)" ] && [ -z "${GIT_EDITOR}" ]; then
|
||||
if [ "${TERM_PROGRAM}" = "vscode" ]; then
|
||||
if [[ -n $(command -v code-insiders) && -z $(command -v code) ]]; then
|
||||
export GIT_EDITOR="code-insiders --wait"
|
||||
else
|
||||
export GIT_EDITOR="code --wait"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
EOF
|
||||
)"
|
||||
|
||||
# code shim, it fallbacks to code-insiders if code is not available
|
||||
cat << 'EOF' > /usr/local/bin/code
|
||||
#!/bin/sh
|
||||
|
||||
get_in_path_except_current() {
|
||||
which -a "$1" | grep -A1 "$0" | grep -v "$0"
|
||||
}
|
||||
|
||||
code="$(get_in_path_except_current code)"
|
||||
|
||||
if [ -n "$code" ]; then
|
||||
exec "$code" "$@"
|
||||
elif [ "$(command -v code-insiders)" ]; then
|
||||
exec code-insiders "$@"
|
||||
else
|
||||
echo "code or code-insiders is not installed" >&2
|
||||
exit 127
|
||||
fi
|
||||
EOF
|
||||
chmod +x /usr/local/bin/code
|
||||
|
||||
# systemctl shim - tells people to use 'service' if systemd is not running
|
||||
cat << 'EOF' > /usr/local/bin/systemctl
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ -d "/run/systemd/system" ]; then
|
||||
exec /bin/systemctl/systemctl "$@"
|
||||
else
|
||||
echo '\n"systemd" is not running in this container due to its overhead.\nUse the "service" command to start services instead. e.g.: \n\nservice --status-all'
|
||||
fi
|
||||
EOF
|
||||
chmod +x /usr/local/bin/systemctl
|
||||
|
||||
# Codespaces bash and OMZ themes - partly inspired by https://github.com/ohmyzsh/ohmyzsh/blob/master/themes/robbyrussell.zsh-theme
|
||||
codespaces_bash="$(cat \
|
||||
<<'EOF'
|
||||
|
||||
# Codespaces bash prompt theme
|
||||
__bash_prompt() {
|
||||
local userpart='`export XIT=$? \
|
||||
&& [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \
|
||||
&& [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`'
|
||||
local gitbranch='`\
|
||||
if [ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ]; then \
|
||||
export BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null); \
|
||||
if [ "${BRANCH}" != "" ]; then \
|
||||
echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \
|
||||
&& if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \
|
||||
echo -n " \[\033[1;33m\]✗"; \
|
||||
fi \
|
||||
&& echo -n "\[\033[0;36m\]) "; \
|
||||
fi; \
|
||||
fi`'
|
||||
local lightblue='\[\033[1;34m\]'
|
||||
local removecolor='\[\033[0m\]'
|
||||
PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ "
|
||||
unset -f __bash_prompt
|
||||
}
|
||||
__bash_prompt
|
||||
|
||||
EOF
|
||||
)"
|
||||
|
||||
codespaces_zsh="$(cat \
|
||||
<<'EOF'
|
||||
# Codespaces zsh prompt theme
|
||||
__zsh_prompt() {
|
||||
local prompt_username
|
||||
if [ ! -z "${GITHUB_USER}" ]; then
|
||||
prompt_username="@${GITHUB_USER}"
|
||||
else
|
||||
prompt_username="%n"
|
||||
fi
|
||||
PROMPT="%{$fg[green]%}${prompt_username} %(?:%{$reset_color%}➜ :%{$fg_bold[red]%}➜ )" # User/exit code arrow
|
||||
PROMPT+='%{$fg_bold[blue]%}%(5~|%-1~/…/%3~|%4~)%{$reset_color%} ' # cwd
|
||||
PROMPT+='$([ "$(git config --get codespaces-theme.hide-status 2>/dev/null)" != 1 ] && git_prompt_info)' # Git status
|
||||
PROMPT+='%{$fg[white]%}$ %{$reset_color%}'
|
||||
unset -f __zsh_prompt
|
||||
}
|
||||
ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg_bold[cyan]%}(%{$fg_bold[red]%}"
|
||||
ZSH_THEME_GIT_PROMPT_SUFFIX="%{$reset_color%} "
|
||||
ZSH_THEME_GIT_PROMPT_DIRTY=" %{$fg_bold[yellow]%}✗%{$fg_bold[cyan]%})"
|
||||
ZSH_THEME_GIT_PROMPT_CLEAN="%{$fg_bold[cyan]%})"
|
||||
__zsh_prompt
|
||||
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Add RC snippet and custom bash prompt
|
||||
if [ "${RC_SNIPPET_ALREADY_ADDED}" != "true" ]; then
|
||||
echo "${rc_snippet}" >> /etc/bash.bashrc
|
||||
echo "${codespaces_bash}" >> "${user_rc_path}/.bashrc"
|
||||
echo 'export PROMPT_DIRTRIM=4' >> "${user_rc_path}/.bashrc"
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
echo "${codespaces_bash}" >> "/root/.bashrc"
|
||||
echo 'export PROMPT_DIRTRIM=4' >> "/root/.bashrc"
|
||||
fi
|
||||
chown ${USERNAME}:${group_name} "${user_rc_path}/.bashrc"
|
||||
RC_SNIPPET_ALREADY_ADDED="true"
|
||||
fi
|
||||
|
||||
# Optionally install and configure zsh and Oh My Zsh!
|
||||
if [ "${INSTALL_ZSH}" = "true" ]; then
|
||||
if ! type zsh > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get install -y zsh
|
||||
fi
|
||||
if [ "${ZSH_ALREADY_INSTALLED}" != "true" ]; then
|
||||
echo "${rc_snippet}" >> /etc/zsh/zshrc
|
||||
ZSH_ALREADY_INSTALLED="true"
|
||||
fi
|
||||
|
||||
# Adapted, simplified inline Oh My Zsh! install steps that adds, defaults to a codespaces theme.
|
||||
# See https://github.com/ohmyzsh/ohmyzsh/blob/master/tools/install.sh for official script.
|
||||
oh_my_install_dir="${user_rc_path}/.oh-my-zsh"
|
||||
if [ ! -d "${oh_my_install_dir}" ] && [ "${INSTALL_OH_MYS}" = "true" ]; then
|
||||
template_path="${oh_my_install_dir}/templates/zshrc.zsh-template"
|
||||
user_rc_file="${user_rc_path}/.zshrc"
|
||||
umask g-w,o-w
|
||||
mkdir -p ${oh_my_install_dir}
|
||||
git clone --depth=1 \
|
||||
-c core.eol=lf \
|
||||
-c core.autocrlf=false \
|
||||
-c fsck.zeroPaddedFilemode=ignore \
|
||||
-c fetch.fsck.zeroPaddedFilemode=ignore \
|
||||
-c receive.fsck.zeroPaddedFilemode=ignore \
|
||||
"https://github.com/ohmyzsh/ohmyzsh" "${oh_my_install_dir}" 2>&1
|
||||
echo -e "$(cat "${template_path}")\nDISABLE_AUTO_UPDATE=true\nDISABLE_UPDATE_PROMPT=true" > ${user_rc_file}
|
||||
sed -i -e 's/ZSH_THEME=.*/ZSH_THEME="codespaces"/g' ${user_rc_file}
|
||||
|
||||
mkdir -p ${oh_my_install_dir}/custom/themes
|
||||
echo "${codespaces_zsh}" > "${oh_my_install_dir}/custom/themes/codespaces.zsh-theme"
|
||||
# Shrink git while still enabling updates
|
||||
cd "${oh_my_install_dir}"
|
||||
git repack -a -d -f --depth=1 --window=1
|
||||
# Copy to non-root user if one is specified
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
cp -rf "${user_rc_file}" "${oh_my_install_dir}" /root
|
||||
chown -R ${USERNAME}:${group_name} "${user_rc_path}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Persist image metadata info, script if meta.env found in same directory
|
||||
meta_info_script="$(cat << 'EOF'
|
||||
#!/bin/sh
|
||||
. /usr/local/etc/vscode-dev-containers/meta.env
|
||||
|
||||
# Minimal output
|
||||
if [ "$1" = "version" ] || [ "$1" = "image-version" ]; then
|
||||
echo "${VERSION}"
|
||||
exit 0
|
||||
elif [ "$1" = "release" ]; then
|
||||
echo "${GIT_REPOSITORY_RELEASE}"
|
||||
exit 0
|
||||
elif [ "$1" = "content" ] || [ "$1" = "content-url" ] || [ "$1" = "contents" ] || [ "$1" = "contents-url" ]; then
|
||||
echo "${CONTENTS_URL}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
#Full output
|
||||
echo
|
||||
echo "Development container image information"
|
||||
echo
|
||||
if [ ! -z "${VERSION}" ]; then echo "- Image version: ${VERSION}"; fi
|
||||
if [ ! -z "${DEFINITION_ID}" ]; then echo "- Definition ID: ${DEFINITION_ID}"; fi
|
||||
if [ ! -z "${VARIANT}" ]; then echo "- Variant: ${VARIANT}"; fi
|
||||
if [ ! -z "${GIT_REPOSITORY}" ]; then echo "- Source code repository: ${GIT_REPOSITORY}"; fi
|
||||
if [ ! -z "${GIT_REPOSITORY_RELEASE}" ]; then echo "- Source code release/branch: ${GIT_REPOSITORY_RELEASE}"; fi
|
||||
if [ ! -z "${BUILD_TIMESTAMP}" ]; then echo "- Timestamp: ${BUILD_TIMESTAMP}"; fi
|
||||
if [ ! -z "${CONTENTS_URL}" ]; then echo && echo "More info: ${CONTENTS_URL}"; fi
|
||||
echo
|
||||
EOF
|
||||
)"
|
||||
if [ -f "${SCRIPT_DIR}/meta.env" ]; then
|
||||
mkdir -p /usr/local/etc/vscode-dev-containers/
|
||||
cp -f "${SCRIPT_DIR}/meta.env" /usr/local/etc/vscode-dev-containers/meta.env
|
||||
echo "${meta_info_script}" > /usr/local/bin/devcontainer-info
|
||||
chmod +x /usr/local/bin/devcontainer-info
|
||||
fi
|
||||
|
||||
# Write marker file
|
||||
mkdir -p "$(dirname "${MARKER_FILE}")"
|
||||
echo -e "\
|
||||
PACKAGES_ALREADY_INSTALLED=${PACKAGES_ALREADY_INSTALLED}\n\
|
||||
LOCALE_ALREADY_SET=${LOCALE_ALREADY_SET}\n\
|
||||
EXISTING_NON_ROOT_USER=${EXISTING_NON_ROOT_USER}\n\
|
||||
RC_SNIPPET_ALREADY_ADDED=${RC_SNIPPET_ALREADY_ADDED}\n\
|
||||
ZSH_ALREADY_INSTALLED=${ZSH_ALREADY_INSTALLED}" > "${MARKER_FILE}"
|
||||
|
||||
echo "Done!"
|
39
collection/desktop-lite/feature.json
Normal file
39
collection/desktop-lite/feature.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"id": "desktop-lite",
|
||||
"name": "Light-weight desktop (Fluxbox)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"enum": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Currently unused."
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"proposals": ["vscode","codespaces","password"],
|
||||
"default": "vscode",
|
||||
"description": "Enter a password for desktop connections"
|
||||
},
|
||||
"webPort": {
|
||||
"type": "string",
|
||||
"proposals": ["6080"],
|
||||
"default": "6080",
|
||||
"description": "Enter a port for the desktop web client"
|
||||
},
|
||||
"vncPort": {
|
||||
"type": "string",
|
||||
"proposals": ["5901"],
|
||||
"default": "5901",
|
||||
"description": "Enter a port for the desktop VNC server"
|
||||
}
|
||||
},
|
||||
"init": true,
|
||||
"entrypoint": "/usr/local/share/desktop-init.sh",
|
||||
"containerEnv": {
|
||||
"DISPLAY": ":1"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
394
collection/desktop-lite/install.sh
Normal file
394
collection/desktop-lite/install.sh
Normal file
|
@ -0,0 +1,394 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/desktop-lite.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./desktop-lite-debian.sh [non-root user] [Desktop password] [Install web client flag] [VNC port] [Web Port]
|
||||
|
||||
USERNAME=${1:-"automatic"}
|
||||
VNC_PASSWORD=${2:-"vscode"}
|
||||
INSTALL_NOVNC=${3:-"true"}
|
||||
VNC_PORT="${4:-5901}"
|
||||
NOVNC_PORT="${5:-6080}"
|
||||
|
||||
NOVNC_VERSION=1.2.0
|
||||
WEBSOCKETIFY_VERSION=0.10.0
|
||||
|
||||
package_list="
|
||||
tigervnc-standalone-server \
|
||||
tigervnc-common \
|
||||
fluxbox \
|
||||
dbus-x11 \
|
||||
x11-utils \
|
||||
x11-xserver-utils \
|
||||
xdg-utils \
|
||||
fbautostart \
|
||||
at-spi2-core \
|
||||
xterm \
|
||||
eterm \
|
||||
nautilus\
|
||||
mousepad \
|
||||
seahorse \
|
||||
gnome-icon-theme \
|
||||
gnome-keyring \
|
||||
libx11-dev \
|
||||
libxkbfile-dev \
|
||||
libsecret-1-dev \
|
||||
libgbm-dev \
|
||||
libnotify4 \
|
||||
libnss3 \
|
||||
libxss1 \
|
||||
libasound2 \
|
||||
xfonts-base \
|
||||
xfonts-terminus \
|
||||
fonts-noto \
|
||||
fonts-wqy-microhei \
|
||||
fonts-droid-fallback \
|
||||
htop \
|
||||
ncdu \
|
||||
curl \
|
||||
ca-certificates\
|
||||
unzip \
|
||||
nano \
|
||||
locales"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
# Add default Fluxbox config files if none are already present
|
||||
fluxbox_apps="$(cat \
|
||||
<< 'EOF'
|
||||
[transient] (role=GtkFileChooserDialog)
|
||||
[Dimensions] {70% 70%}
|
||||
[Position] (CENTER) {0 0}
|
||||
[end]
|
||||
EOF
|
||||
)"
|
||||
|
||||
fluxbox_init="$(cat \
|
||||
<< 'EOF'
|
||||
session.configVersion: 13
|
||||
session.menuFile: ~/.fluxbox/menu
|
||||
session.keyFile: ~/.fluxbox/keys
|
||||
session.styleFile: /usr/share/fluxbox/styles/qnx-photon
|
||||
session.screen0.workspaces: 1
|
||||
session.screen0.workspacewarping: false
|
||||
session.screen0.toolbar.widthPercent: 100
|
||||
session.screen0.strftimeFormat: %a %l:%M %p
|
||||
session.screen0.toolbar.tools: RootMenu, clock, iconbar, systemtray
|
||||
session.screen0.workspaceNames: One,
|
||||
EOF
|
||||
)"
|
||||
|
||||
fluxbox_menu="$(cat \
|
||||
<< 'EOF'
|
||||
[begin] ( Application Menu )
|
||||
[exec] (File Manager) { nautilus ~ } <>
|
||||
[exec] (Text Editor) { mousepad } <>
|
||||
[exec] (Terminal) { tilix -w ~ -e $(readlink -f /proc/$$/exe) -il } <>
|
||||
[exec] (Web Browser) { x-www-browser --disable-dev-shm-usage } <>
|
||||
[submenu] (System) {}
|
||||
[exec] (Set Resolution) { tilix -t "Set Resolution" -e bash /usr/local/bin/set-resolution } <>
|
||||
[exec] (Edit Application Menu) { mousepad ~/.fluxbox/menu } <>
|
||||
[exec] (Passwords and Keys) { seahorse } <>
|
||||
[exec] (Top Processes) { tilix -t "Top" -e htop } <>
|
||||
[exec] (Disk Utilization) { tilix -t "Disk Utilization" -e ncdu / } <>
|
||||
[exec] (Editres) {editres} <>
|
||||
[exec] (Xfontsel) {xfontsel} <>
|
||||
[exec] (Xkill) {xkill} <>
|
||||
[exec] (Xrefresh) {xrefresh} <>
|
||||
[end]
|
||||
[config] (Configuration)
|
||||
[workspaces] (Workspaces)
|
||||
[end]
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Copy config files if the don't already exist
|
||||
copy_fluxbox_config() {
|
||||
local target_dir="$1"
|
||||
mkdir -p "${target_dir}/.fluxbox"
|
||||
touch "${target_dir}/.Xmodmap"
|
||||
if [ ! -e "${target_dir}/.fluxbox/apps" ]; then
|
||||
echo "${fluxbox_apps}" > "${target_dir}/.fluxbox/apps"
|
||||
fi
|
||||
if [ ! -e "${target_dir}/.fluxbox/init" ]; then
|
||||
echo "${fluxbox_init}" > "${target_dir}/.fluxbox/init"
|
||||
fi
|
||||
if [ ! -e "${target_dir}/.fluxbox/menu" ]; then
|
||||
echo "${fluxbox_menu}" > "${target_dir}/.fluxbox/menu"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
apt_get_update_if_needed
|
||||
|
||||
# On older Ubuntu, Tilix is in a PPA. on Debian strech its in backports.
|
||||
if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
|
||||
. /etc/os-release
|
||||
if [ "${ID}" = "ubuntu" ]; then
|
||||
apt-get install -y --no-install-recommends apt-transport-https software-properties-common
|
||||
add-apt-repository -y ppa:webupd8team/terminix
|
||||
elif [ "${VERSION_CODENAME}" = "stretch" ]; then
|
||||
echo "deb http://deb.debian.org/debian stretch-backports main" > /etc/apt/sources.list.d/stretch-backports.list
|
||||
fi
|
||||
apt-get update
|
||||
if [[ -z $(apt-cache --names-only search ^tilix$) ]]; then
|
||||
echo "(!) WARNING: Tilix not available on ${ID} ${VERSION_CODENAME} architecture $(uname -m). Skipping."
|
||||
else
|
||||
package_list="${package_list} tilix"
|
||||
fi
|
||||
else
|
||||
package_list="${package_list} tilix"
|
||||
fi
|
||||
|
||||
# Install X11, fluxbox and VS Code dependencies
|
||||
check_packages ${package_list}
|
||||
|
||||
# Install Emoji font if available in distro - Available in Debian 10+, Ubuntu 18.04+
|
||||
if dpkg-query -W fonts-noto-color-emoji > /dev/null 2>&1 && ! dpkg -s fonts-noto-color-emoji > /dev/null 2>&1; then
|
||||
apt-get -y install --no-install-recommends fonts-noto-color-emoji
|
||||
fi
|
||||
|
||||
# Check at least one locale exists
|
||||
if ! grep -o -E '^\s*en_US.UTF-8\s+UTF-8' /etc/locale.gen > /dev/null; then
|
||||
echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen
|
||||
locale-gen
|
||||
fi
|
||||
|
||||
# Install the Cascadia Code fonts - https://github.com/microsoft/cascadia-code
|
||||
if [ ! -d "/usr/share/fonts/truetype/cascadia" ]; then
|
||||
curl -sSL https://github.com/microsoft/cascadia-code/releases/download/v2008.25/CascadiaCode-2008.25.zip -o /tmp/cascadia-fonts.zip
|
||||
unzip /tmp/cascadia-fonts.zip -d /tmp/cascadia-fonts
|
||||
mkdir -p /usr/share/fonts/truetype/cascadia
|
||||
mv /tmp/cascadia-fonts/ttf/* /usr/share/fonts/truetype/cascadia/
|
||||
rm -rf /tmp/cascadia-fonts.zip /tmp/cascadia-fonts
|
||||
fi
|
||||
|
||||
# Install noVNC
|
||||
if [ "${INSTALL_NOVNC}" = "true" ] && [ ! -d "/usr/local/novnc" ]; then
|
||||
mkdir -p /usr/local/novnc
|
||||
curl -sSL https://github.com/novnc/noVNC/archive/v${NOVNC_VERSION}.zip -o /tmp/novnc-install.zip
|
||||
unzip /tmp/novnc-install.zip -d /usr/local/novnc
|
||||
cp /usr/local/novnc/noVNC-${NOVNC_VERSION}/vnc.html /usr/local/novnc/noVNC-${NOVNC_VERSION}/index.html
|
||||
curl -sSL https://github.com/novnc/websockify/archive/v${WEBSOCKETIFY_VERSION}.zip -o /tmp/websockify-install.zip
|
||||
unzip /tmp/websockify-install.zip -d /usr/local/novnc
|
||||
ln -s /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION} /usr/local/novnc/noVNC-${NOVNC_VERSION}/utils/websockify
|
||||
rm -f /tmp/websockify-install.zip /tmp/novnc-install.zip
|
||||
|
||||
# Install noVNC dependencies and use them.
|
||||
if ! dpkg -s python3-minimal python3-numpy > /dev/null 2>&1; then
|
||||
apt-get -y install --no-install-recommends python3-minimal python3-numpy
|
||||
fi
|
||||
sed -i -E 's/^python /python3 /' /usr/local/novnc/websockify-${WEBSOCKETIFY_VERSION}/run
|
||||
fi
|
||||
|
||||
# Set up folders for scripts and init files
|
||||
mkdir -p /var/run/dbus /usr/local/etc/vscode-dev-containers/
|
||||
|
||||
# Script to change resolution of desktop
|
||||
cat << EOF > /usr/local/bin/set-resolution
|
||||
#!/bin/bash
|
||||
RESOLUTION=\${1:-\${VNC_RESOLUTION:-1920x1080}}
|
||||
DPI=\${2:-\${VNC_DPI:-96}}
|
||||
IGNORE_ERROR=\${3:-"false"}
|
||||
if [ -z "\$1" ]; then
|
||||
echo -e "**Current Settings **\n"
|
||||
xrandr
|
||||
echo -n -e "\nEnter new resolution (WIDTHxHEIGHT, blank for \${RESOLUTION}, Ctrl+C to abort).\n> "
|
||||
read NEW_RES
|
||||
if [ "\${NEW_RES}" != "" ]; then
|
||||
RESOLUTION=\${NEW_RES}
|
||||
fi
|
||||
if ! echo "\${RESOLUTION}" | grep -E '[0-9]+x[0-9]+' > /dev/null; then
|
||||
echo -e "\nInvalid resolution format!\n"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "\$2" ]; then
|
||||
echo -n -e "\nEnter new DPI (blank for \${DPI}, Ctrl+C to abort).\n> "
|
||||
read NEW_DPI
|
||||
if [ "\${NEW_DPI}" != "" ]; then
|
||||
DPI=\${NEW_DPI}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
xrandr --fb \${RESOLUTION} --dpi \${DPI} > /dev/null 2>&1
|
||||
|
||||
if [ \$? -ne 0 ] && [ "\${IGNORE_ERROR}" != "true" ]; then
|
||||
echo -e "\nFAILED TO SET RESOLUTION!\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "\nSuccess!\n"
|
||||
EOF
|
||||
|
||||
# Container ENTRYPOINT script
|
||||
cat << EOF > /usr/local/share/desktop-init.sh
|
||||
#!/bin/bash
|
||||
|
||||
user_name="${USERNAME}"
|
||||
group_name="$(id -gn ${USERNAME})"
|
||||
LOG=/tmp/container-init.log
|
||||
|
||||
export DBUS_SESSION_BUS_ADDRESS="${DBUS_SESSION_BUS_ADDRESS:-"autolaunch:"}"
|
||||
export DISPLAY="${DISPLAY:-:1}"
|
||||
export VNC_RESOLUTION="${VNC_RESOLUTION:-1440x768x16}"
|
||||
export LANG="${LANG:-"en_US.UTF-8"}"
|
||||
export LANGUAGE="${LANGUAGE:-"en_US.UTF-8"}"
|
||||
|
||||
# Execute the command it not already running
|
||||
startInBackgroundIfNotRunning()
|
||||
{
|
||||
log "Starting \$1."
|
||||
echo -e "\n** \$(date) **" | sudoIf tee -a /tmp/\$1.log > /dev/null
|
||||
if ! pidof \$1 > /dev/null; then
|
||||
keepRunningInBackground "\$@"
|
||||
while ! pidof \$1 > /dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
log "\$1 started."
|
||||
else
|
||||
echo "\$1 is already running." | sudoIf tee -a /tmp/\$1.log > /dev/null
|
||||
log "\$1 is already running."
|
||||
fi
|
||||
}
|
||||
|
||||
# Keep command running in background
|
||||
keepRunningInBackground()
|
||||
{
|
||||
(\$2 bash -c "while :; do echo [\\\$(date)] Process started.; \$3; echo [\\\$(date)] Process exited!; sleep 5; done 2>&1" | sudoIf tee -a /tmp/\$1.log > /dev/null & echo "\$!" | sudoIf tee /tmp/\$1.pid > /dev/null)
|
||||
}
|
||||
|
||||
# Use sudo to run as root when required
|
||||
sudoIf()
|
||||
{
|
||||
if [ "\$(id -u)" -ne 0 ]; then
|
||||
sudo "\$@"
|
||||
else
|
||||
"\$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Use sudo to run as non-root user if not already running
|
||||
sudoUserIf()
|
||||
{
|
||||
if [ "\$(id -u)" -eq 0 ] && [ "\${user_name}" != "root" ]; then
|
||||
sudo -u \${user_name} "\$@"
|
||||
else
|
||||
"\$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Log messages
|
||||
log()
|
||||
{
|
||||
echo -e "[\$(date)] \$@" | sudoIf tee -a \$LOG > /dev/null
|
||||
}
|
||||
|
||||
log "** SCRIPT START **"
|
||||
|
||||
# Start dbus.
|
||||
log 'Running "/etc/init.d/dbus start".'
|
||||
if [ -f "/var/run/dbus/pid" ] && ! pidof dbus-daemon > /dev/null; then
|
||||
sudoIf rm -f /var/run/dbus/pid
|
||||
fi
|
||||
sudoIf /etc/init.d/dbus start 2>&1 | sudoIf tee -a /tmp/dbus-daemon-system.log > /dev/null
|
||||
while ! pidof dbus-daemon > /dev/null; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# Startup tigervnc server and fluxbox
|
||||
sudo rm -rf /tmp/.X11-unix /tmp/.X*-lock
|
||||
mkdir -p /tmp/.X11-unix
|
||||
sudoIf chmod 1777 /tmp/.X11-unix
|
||||
sudoIf chown root:\${group_name} /tmp/.X11-unix
|
||||
if [ "\$(echo "\${VNC_RESOLUTION}" | tr -cd 'x' | wc -c)" = "1" ]; then VNC_RESOLUTION=\${VNC_RESOLUTION}x16; fi
|
||||
screen_geometry="\${VNC_RESOLUTION%*x*}"
|
||||
screen_depth="\${VNC_RESOLUTION##*x}"
|
||||
startInBackgroundIfNotRunning "Xtigervnc" sudoUserIf "tigervncserver \${DISPLAY} -geometry \${screen_geometry} -depth \${screen_depth} -rfbport ${VNC_PORT} -dpi \${VNC_DPI:-96} -localhost -desktop fluxbox -fg -passwd /usr/local/etc/vscode-dev-containers/vnc-passwd"
|
||||
|
||||
# Spin up noVNC if installed and not runnning.
|
||||
if [ -d "/usr/local/novnc" ] && [ "\$(ps -ef | grep /usr/local/novnc/noVNC*/utils/launch.sh | grep -v grep)" = "" ]; then
|
||||
keepRunningInBackground "noVNC" sudoIf "/usr/local/novnc/noVNC*/utils/launch.sh --listen ${NOVNC_PORT} --vnc localhost:${VNC_PORT}"
|
||||
log "noVNC started."
|
||||
else
|
||||
log "noVNC is already running or not installed."
|
||||
fi
|
||||
|
||||
# Run whatever was passed in
|
||||
log "Executing \"\$@\"."
|
||||
exec "\$@"
|
||||
log "** SCRIPT EXIT **"
|
||||
EOF
|
||||
|
||||
echo "${VNC_PASSWORD}" | vncpasswd -f > /usr/local/etc/vscode-dev-containers/vnc-passwd
|
||||
chmod +x /usr/local/share/desktop-init.sh /usr/local/bin/set-resolution
|
||||
|
||||
# Set up fluxbox config
|
||||
copy_fluxbox_config "/root"
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
copy_fluxbox_config "/home/${USERNAME}"
|
||||
chown -R ${USERNAME} /home/${USERNAME}/.Xmodmap /home/${USERNAME}/.fluxbox
|
||||
fi
|
||||
|
||||
cat << EOF
|
||||
|
||||
|
||||
You now have a working desktop! Connect to in one of the following ways:
|
||||
|
||||
- Forward port ${NOVNC_PORT} and use a web browser start the noVNC client (recommended)
|
||||
- Forward port ${VNC_PORT} using VS Code client and connect using a VNC Viewer
|
||||
|
||||
In both cases, use the password "${VNC_PASSWORD}" when connecting
|
||||
|
||||
(*) Done!
|
||||
|
||||
EOF
|
||||
|
41
collection/docker-from-docker/feature.json
Normal file
41
collection/docker-from-docker/feature.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"id": "docker-from-docker",
|
||||
"name": "Docker (Moby) support, reuse host Docker Engine (Docker-from-Docker)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "20.10"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Docker/Moby CLI version. (Availability can vary by OS version.)"
|
||||
},
|
||||
"moby": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install OSS Moby build instead of Docker CE"
|
||||
},
|
||||
"dockerDashComposeVersion": {
|
||||
"type": "string",
|
||||
"enum": ["v1", "v2" ],
|
||||
"default": "v1",
|
||||
"description": "Compose version to use for docker-compose (v1 or v2)"
|
||||
}
|
||||
},
|
||||
"entrypoint": "/usr/local/share/docker-init.sh",
|
||||
"containerEnv": {
|
||||
"DOCKER_BUILDKIT": "1"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker"
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"source":"/var/run/docker.sock",
|
||||
"target":"/var/run/docker-host.sock",
|
||||
"type":"bind"
|
||||
}
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
354
collection/docker-from-docker/install.sh
Normal file
354
collection/docker-from-docker/install.sh
Normal file
|
@ -0,0 +1,354 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./docker-debian.sh [enable non-root docker socket access flag] [source socket] [target socket] [non-root user] [use moby] [CLI version] [Major version for docker-compose]
|
||||
|
||||
ENABLE_NONROOT_DOCKER=${1:-"true"}
|
||||
SOURCE_SOCKET=${2:-"/var/run/docker-host.sock"}
|
||||
TARGET_SOCKET=${3:-"/var/run/docker.sock"}
|
||||
USERNAME=${4:-"automatic"}
|
||||
USE_MOBY=${5:-"true"}
|
||||
DOCKER_VERSION=${6:-"latest"}
|
||||
DOCKER_DASH_COMPOSE_VERSION=${7:-"v1"} # v1 or v2
|
||||
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
|
||||
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy"
|
||||
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
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
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install git
|
||||
fi
|
||||
|
||||
# Source /etc/os-release to get OS info
|
||||
. /etc/os-release
|
||||
# Fetch host/container arch.
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
|
||||
# Check if distro is suppported
|
||||
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
|
||||
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}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
|
||||
else
|
||||
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
|
||||
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
|
||||
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
|
||||
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}'"
|
||||
fi
|
||||
|
||||
# Set up the necessary apt repos (either Microsoft's or Docker's)
|
||||
if [ "${USE_MOBY}" = "true" ]; then
|
||||
|
||||
cli_package_name="moby-cli"
|
||||
|
||||
# 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
|
||||
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
|
||||
# Name of proprietary engine package
|
||||
cli_package_name="docker-ce-cli"
|
||||
|
||||
# Import key safely and import Docker apt repo
|
||||
curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list
|
||||
fi
|
||||
|
||||
# Refresh apt lists
|
||||
apt-get update
|
||||
|
||||
# Soft version matching for CLI
|
||||
if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then
|
||||
# Empty, meaning grab whatever "latest" is in apt repo
|
||||
cli_version_suffix=""
|
||||
else
|
||||
# Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...)
|
||||
docker_version_dot_escaped="${DOCKER_VERSION//./\\.}"
|
||||
docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}"
|
||||
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
|
||||
docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)"
|
||||
set +e # Don't exit if finding version fails - will handle gracefully
|
||||
cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")"
|
||||
set -e
|
||||
if [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ]; then
|
||||
echo "(!) No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:"
|
||||
apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
|
||||
exit 1
|
||||
fi
|
||||
echo "cli_version_suffix ${cli_version_suffix}"
|
||||
fi
|
||||
|
||||
# Install Docker / Moby CLI if not already installed
|
||||
if type docker > /dev/null 2>&1; then
|
||||
echo "Docker / Moby CLI already installed."
|
||||
else
|
||||
if [ "${USE_MOBY}" = "true" ]; then
|
||||
apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx
|
||||
apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
|
||||
else
|
||||
apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install Docker Compose if not already installed and is on a supported architecture
|
||||
if type docker-compose > /dev/null 2>&1; then
|
||||
echo "Docker Compose already installed."
|
||||
else
|
||||
TARGET_COMPOSE_ARCH="$(uname -m)"
|
||||
if [ "${TARGET_COMPOSE_ARCH}" = "amd64" ]; then
|
||||
TARGET_COMPOSE_ARCH="x86_64"
|
||||
fi
|
||||
if [ "${TARGET_COMPOSE_ARCH}" != "x86_64" ]; then
|
||||
# Use pip to get a version that runns on this architecture
|
||||
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
|
||||
fi
|
||||
export PIPX_HOME=/usr/local/pipx
|
||||
mkdir -p ${PIPX_HOME}
|
||||
export PIPX_BIN_DIR=/usr/local/bin
|
||||
export PYTHONUSERBASE=/tmp/pip-tmp
|
||||
export PIP_CACHE_DIR=/tmp/pip-tmp/cache
|
||||
pipx_bin=pipx
|
||||
if ! type pipx > /dev/null 2>&1; then
|
||||
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
|
||||
pipx_bin=/tmp/pip-tmp/bin/pipx
|
||||
fi
|
||||
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
|
||||
rm -rf /tmp/pip-tmp
|
||||
else
|
||||
compose_v1_version="1"
|
||||
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
|
||||
echo "(*) Installing docker-compose ${compose_v1_version}..."
|
||||
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
|
||||
current_v1_compose_path="$(which docker-compose)"
|
||||
target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1"
|
||||
if ! type compose-switch > /dev/null 2>&1; then
|
||||
echo "(*) Installing compose-switch..."
|
||||
compose_switch_version="latest"
|
||||
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch"
|
||||
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
|
||||
chmod +x /usr/local/bin/compose-switch
|
||||
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
|
||||
|
||||
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
|
||||
mv "${current_v1_compose_path}" "${target_v1_compose_path}"
|
||||
update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
|
||||
update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1
|
||||
fi
|
||||
if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
|
||||
update-alternatives --set docker-compose "${target_v1_compose_path}"
|
||||
else
|
||||
update-alternatives --set docker-compose /usr/local/bin/compose-switch
|
||||
fi
|
||||
|
||||
# If init file already exists, exit
|
||||
if [ -f "/usr/local/share/docker-init.sh" ]; then
|
||||
exit 0
|
||||
fi
|
||||
echo "docker-init doesnt exist, adding..."
|
||||
|
||||
# By default, make the source and target sockets the same
|
||||
if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then
|
||||
touch "${SOURCE_SOCKET}"
|
||||
ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}"
|
||||
fi
|
||||
|
||||
# Add a stub if not adding non-root user access, user is root
|
||||
if [ "${ENABLE_NONROOT_DOCKER}" = "false" ] || [ "${USERNAME}" = "root" ]; then
|
||||
echo -e '#!/usr/bin/env bash\nexec "$@"' > /usr/local/share/docker-init.sh
|
||||
chmod +x /usr/local/share/docker-init.sh
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Setup a docker group in the event the docker socket's group is not root
|
||||
if ! grep -qE '^docker:' /etc/group; then
|
||||
groupadd --system docker
|
||||
fi
|
||||
usermod -aG docker "${USERNAME}"
|
||||
DOCKER_GID="$(grep -oP '^docker:x:\K[^:]+' /etc/group)"
|
||||
|
||||
# If enabling non-root access and specified user is found, setup socat and add script
|
||||
chown -h "${USERNAME}":root "${TARGET_SOCKET}"
|
||||
if ! dpkg -s socat > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install socat
|
||||
fi
|
||||
tee /usr/local/share/docker-init.sh > /dev/null \
|
||||
<< EOF
|
||||
#!/usr/bin/env bash
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
set -e
|
||||
|
||||
SOCAT_PATH_BASE=/tmp/vscr-docker-from-docker
|
||||
SOCAT_LOG=\${SOCAT_PATH_BASE}.log
|
||||
SOCAT_PID=\${SOCAT_PATH_BASE}.pid
|
||||
|
||||
# Wrapper function to only use sudo if not already root
|
||||
sudoIf()
|
||||
{
|
||||
if [ "\$(id -u)" -ne 0 ]; then
|
||||
sudo "\$@"
|
||||
else
|
||||
"\$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Log messages
|
||||
log()
|
||||
{
|
||||
echo -e "[\$(date)] \$@" | sudoIf tee -a \${SOCAT_LOG} > /dev/null
|
||||
}
|
||||
|
||||
echo -e "\n** \$(date) **" | sudoIf tee -a \${SOCAT_LOG} > /dev/null
|
||||
log "Ensuring ${USERNAME} has access to ${SOURCE_SOCKET} via ${TARGET_SOCKET}"
|
||||
|
||||
# If enabled, try to update the docker group with the right GID. If the group is root,
|
||||
# fall back on using socat to forward the docker socket to another unix socket so
|
||||
# that we can set permissions on it without affecting the host.
|
||||
if [ "${ENABLE_NONROOT_DOCKER}" = "true" ] && [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ] && [ "${USERNAME}" != "root" ] && [ "${USERNAME}" != "0" ]; then
|
||||
SOCKET_GID=\$(stat -c '%g' ${SOURCE_SOCKET})
|
||||
if [ "\${SOCKET_GID}" != "0" ] && [ "\${SOCKET_GID}" != "${DOCKER_GID}" ] && ! grep -E ".+:x:\${SOCKET_GID}" /etc/group; then
|
||||
sudoIf groupmod --gid "\${SOCKET_GID}" docker
|
||||
else
|
||||
# Enable proxy if not already running
|
||||
if [ ! -f "\${SOCAT_PID}" ] || ! ps -p \$(cat \${SOCAT_PID}) > /dev/null; then
|
||||
log "Enabling socket proxy."
|
||||
log "Proxying ${SOURCE_SOCKET} to ${TARGET_SOCKET} for vscode"
|
||||
sudoIf rm -rf ${TARGET_SOCKET}
|
||||
(sudoIf socat UNIX-LISTEN:${TARGET_SOCKET},fork,mode=660,user=${USERNAME} UNIX-CONNECT:${SOURCE_SOCKET} 2>&1 | sudoIf tee -a \${SOCAT_LOG} > /dev/null & echo "\$!" | sudoIf tee \${SOCAT_PID} > /dev/null)
|
||||
else
|
||||
log "Socket proxy already running."
|
||||
fi
|
||||
fi
|
||||
log "Success"
|
||||
fi
|
||||
|
||||
# Execute whatever commands were passed in (if any). This allows us
|
||||
# to set this script to ENTRYPOINT while still executing the default CMD.
|
||||
set +e
|
||||
exec "\$@"
|
||||
EOF
|
||||
chmod +x /usr/local/share/docker-init.sh
|
||||
chown ${USERNAME}:root /usr/local/share/docker-init.sh
|
||||
echo "Done!"
|
42
collection/docker-in-docker/feature.json
Normal file
42
collection/docker-in-docker/feature.json
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"id": "docker-in-docker",
|
||||
"name": "Docker (Moby) support (Docker-in-Docker)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "20.10" ],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Docker/Moby Engine version. (Availability can vary by OS version.)"
|
||||
},
|
||||
"moby": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install OSS Moby build instead of Docker CE"
|
||||
},
|
||||
"dockerDashComposeVersion": {
|
||||
"type": "string",
|
||||
"enum": ["v1", "v2" ],
|
||||
"default": "v1",
|
||||
"description": "Default version of Docker Compose (v1 or v2)"
|
||||
}
|
||||
},
|
||||
"entrypoint": "/usr/local/share/docker-init.sh",
|
||||
"privileged": true,
|
||||
"containerEnv": {
|
||||
"DOCKER_BUILDKIT": "1"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-azuretools.vscode-docker"
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"source":"dind-var-lib-docker",
|
||||
"target":"/var/lib/docker",
|
||||
"type":"volume"
|
||||
}
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
397
collection/docker-in-docker/install.sh
Normal file
397
collection/docker-in-docker/install.sh
Normal file
|
@ -0,0 +1,397 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./docker-in-docker-debian.sh [enable non-root docker access flag] [non-root user] [use moby] [Engine/CLI Version] [Major version for docker-compose]
|
||||
|
||||
ENABLE_NONROOT_DOCKER=${1:-"true"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
USE_MOBY=${3:-"true"}
|
||||
DOCKER_VERSION=${4:-"latest"} # The Docker/Moby Engine + CLI should match in version
|
||||
DOCKER_DASH_COMPOSE_VERSION=${5:-"v1"} # v1 or v2
|
||||
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
|
||||
DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy"
|
||||
DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy"
|
||||
|
||||
# Default: Exit on any failure.
|
||||
set -e
|
||||
|
||||
# Setup STDERR.
|
||||
err() {
|
||||
echo "(!) $*" >&2
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
###################
|
||||
# Helper Functions
|
||||
# See: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/shared/utils.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
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
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
|
||||
err "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "${variable_name}=${!variable_name}"
|
||||
}
|
||||
|
||||
###########################################
|
||||
# Start docker-in-docker installation
|
||||
###########################################
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
|
||||
# Source /etc/os-release to get OS info
|
||||
. /etc/os-release
|
||||
# Fetch host/container arch.
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
|
||||
# Check if distro is suppported
|
||||
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
|
||||
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}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES}'"
|
||||
else
|
||||
get_common_setting DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES
|
||||
if [[ "${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}" != *"${VERSION_CODENAME}"* ]]; then
|
||||
err "Unsupported distribution version '${VERSION_CODENAME}'. To resolve, please choose a compatible OS distribution"
|
||||
err "Support distributions include: ${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}"
|
||||
exit 1
|
||||
fi
|
||||
echo "Distro codename '${VERSION_CODENAME}' matched filter '${DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES}'"
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
check_packages apt-transport-https curl ca-certificates pigz iptables gnupg2 dirmngr
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install git
|
||||
fi
|
||||
|
||||
# Swap to legacy iptables for compatibility
|
||||
if type iptables-legacy > /dev/null 2>&1; then
|
||||
update-alternatives --set iptables /usr/sbin/iptables-legacy
|
||||
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Set up the necessary apt repos (either Microsoft's or Docker's)
|
||||
if [ "${USE_MOBY}" = "true" ]; then
|
||||
|
||||
# Name of open source engine/cli
|
||||
engine_package_name="moby-engine"
|
||||
cli_package_name="moby-cli"
|
||||
|
||||
# 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
|
||||
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
|
||||
# Name of licensed engine/cli
|
||||
engine_package_name="docker-ce"
|
||||
cli_package_name="docker-ce-cli"
|
||||
|
||||
# Import key safely and import Docker apt repo
|
||||
curl -fsSL https://download.docker.com/linux/${ID}/gpg | gpg --dearmor > /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/${ID} ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list
|
||||
fi
|
||||
|
||||
# Refresh apt lists
|
||||
apt-get update
|
||||
|
||||
# Soft version matching
|
||||
if [ "${DOCKER_VERSION}" = "latest" ] || [ "${DOCKER_VERSION}" = "lts" ] || [ "${DOCKER_VERSION}" = "stable" ]; then
|
||||
# Empty, meaning grab whatever "latest" is in apt repo
|
||||
engine_version_suffix=""
|
||||
cli_version_suffix=""
|
||||
else
|
||||
# Fetch a valid version from the apt-cache (eg: the Microsoft repo appends +azure, breakfix, etc...)
|
||||
docker_version_dot_escaped="${DOCKER_VERSION//./\\.}"
|
||||
docker_version_dot_plus_escaped="${docker_version_dot_escaped//+/\\+}"
|
||||
# Regex needs to handle debian package version number format: https://www.systutorials.com/docs/linux/man/5-deb-version/
|
||||
docker_version_regex="^(.+:)?${docker_version_dot_plus_escaped}([\\.\\+ ~:-]|$)"
|
||||
set +e # Don't exit if finding version fails - will handle gracefully
|
||||
cli_version_suffix="=$(apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")"
|
||||
engine_version_suffix="=$(apt-cache madison ${engine_package_name} | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "${docker_version_regex}")"
|
||||
set -e
|
||||
if [ -z "${engine_version_suffix}" ] || [ "${engine_version_suffix}" = "=" ] || [ -z "${cli_version_suffix}" ] || [ "${cli_version_suffix}" = "=" ] ; then
|
||||
err "No full or partial Docker / Moby version match found for \"${DOCKER_VERSION}\" on OS ${ID} ${VERSION_CODENAME} (${architecture}). Available versions:"
|
||||
apt-cache madison ${cli_package_name} | awk -F"|" '{print $2}' | grep -oP '^(.+:)?\K.+'
|
||||
exit 1
|
||||
fi
|
||||
echo "engine_version_suffix ${engine_version_suffix}"
|
||||
echo "cli_version_suffix ${cli_version_suffix}"
|
||||
fi
|
||||
|
||||
# Install Docker / Moby CLI if not already installed
|
||||
if type docker > /dev/null 2>&1 && type dockerd > /dev/null 2>&1; then
|
||||
echo "Docker / Moby CLI and Engine already installed."
|
||||
else
|
||||
if [ "${USE_MOBY}" = "true" ]; then
|
||||
# Install engine
|
||||
set +e # Handle error gracefully
|
||||
apt-get -y install --no-install-recommends moby-cli${cli_version_suffix} moby-buildx moby-engine${engine_version_suffix}
|
||||
if [ $? -ne 0 ]; then
|
||||
err "Packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-20.04')."
|
||||
exit 1
|
||||
fi
|
||||
set -e
|
||||
|
||||
# Install compose
|
||||
apt-get -y install --no-install-recommends moby-compose || err "Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
|
||||
else
|
||||
apt-get -y install --no-install-recommends docker-ce-cli${cli_version_suffix} docker-ce${engine_version_suffix}
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Finished installing docker / moby!"
|
||||
|
||||
# Install Docker Compose if not already installed and is on a supported architecture
|
||||
if type docker-compose > /dev/null 2>&1; then
|
||||
echo "Docker Compose v1 already installed."
|
||||
else
|
||||
target_compose_arch="${architecture}"
|
||||
if [ "${target_compose_arch}" = "amd64" ]; then
|
||||
target_compose_arch="x86_64"
|
||||
fi
|
||||
if [ "${target_compose_arch}" != "x86_64" ]; then
|
||||
# Use pip to get a version that runs on this architecture
|
||||
if ! dpkg -s python3-minimal python3-pip libffi-dev python3-venv > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install python3-minimal python3-pip libffi-dev python3-venv
|
||||
fi
|
||||
export PIPX_HOME=/usr/local/pipx
|
||||
mkdir -p ${PIPX_HOME}
|
||||
export PIPX_BIN_DIR=/usr/local/bin
|
||||
export PYTHONUSERBASE=/tmp/pip-tmp
|
||||
export PIP_CACHE_DIR=/tmp/pip-tmp/cache
|
||||
pipx_bin=pipx
|
||||
if ! type pipx > /dev/null 2>&1; then
|
||||
pip3 install --disable-pip-version-check --no-cache-dir --user pipx
|
||||
pipx_bin=/tmp/pip-tmp/bin/pipx
|
||||
fi
|
||||
${pipx_bin} install --pip-args '--no-cache-dir --force-reinstall' docker-compose
|
||||
rm -rf /tmp/pip-tmp
|
||||
else
|
||||
compose_v1_version="1"
|
||||
find_version_from_git_tags compose_v1_version "https://github.com/docker/compose" "tags/"
|
||||
echo "(*) Installing docker-compose ${compose_v1_version}..."
|
||||
curl -fsSL "https://github.com/docker/compose/releases/download/${compose_v1_version}/docker-compose-Linux-x86_64" -o /usr/local/bin/docker-compose
|
||||
chmod +x /usr/local/bin/docker-compose
|
||||
fi
|
||||
fi
|
||||
|
||||
# Install docker-compose switch if not already installed - https://github.com/docker/compose-switch#manual-installation
|
||||
current_v1_compose_path="$(which docker-compose)"
|
||||
target_v1_compose_path="$(dirname "${current_v1_compose_path}")/docker-compose-v1"
|
||||
if ! type compose-switch > /dev/null 2>&1; then
|
||||
echo "(*) Installing compose-switch..."
|
||||
compose_switch_version="latest"
|
||||
find_version_from_git_tags compose_switch_version "https://github.com/docker/compose-switch"
|
||||
curl -fsSL "https://github.com/docker/compose-switch/releases/download/v${compose_switch_version}/docker-compose-linux-${architecture}" -o /usr/local/bin/compose-switch
|
||||
chmod +x /usr/local/bin/compose-switch
|
||||
# TODO: Verify checksum once available: https://github.com/docker/compose-switch/issues/11
|
||||
|
||||
# Setup v1 CLI as alternative in addition to compose-switch (which maps to v2)
|
||||
mv "${current_v1_compose_path}" "${target_v1_compose_path}"
|
||||
update-alternatives --install /usr/local/bin/docker-compose docker-compose /usr/local/bin/compose-switch 99
|
||||
update-alternatives --install /usr/local/bin/docker-compose docker-compose "${target_v1_compose_path}" 1
|
||||
fi
|
||||
if [ "${DOCKER_DASH_COMPOSE_VERSION}" = "v1" ]; then
|
||||
update-alternatives --set docker-compose "${target_v1_compose_path}"
|
||||
else
|
||||
update-alternatives --set docker-compose /usr/local/bin/compose-switch
|
||||
fi
|
||||
|
||||
# If init file already exists, exit
|
||||
if [ -f "/usr/local/share/docker-init.sh" ]; then
|
||||
echo "/usr/local/share/docker-init.sh already exists, so exiting."
|
||||
exit 0
|
||||
fi
|
||||
echo "docker-init doesnt exist, adding..."
|
||||
|
||||
# Add user to the docker group
|
||||
if [ "${ENABLE_NONROOT_DOCKER}" = "true" ]; then
|
||||
if ! getent group docker > /dev/null 2>&1; then
|
||||
groupadd docker
|
||||
fi
|
||||
|
||||
usermod -aG docker ${USERNAME}
|
||||
fi
|
||||
|
||||
tee /usr/local/share/docker-init.sh > /dev/null \
|
||||
<< 'EOF'
|
||||
#!/bin/sh
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
|
||||
#-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
set -e
|
||||
|
||||
dockerd_start="$(cat << 'INNEREOF'
|
||||
# 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 'container*.pid' -delete || :
|
||||
|
||||
## Dind wrapper script from docker team, adapted to a function
|
||||
# Maintained: https://github.com/moby/moby/blob/master/hack/dind
|
||||
|
||||
export container=docker
|
||||
|
||||
if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security; then
|
||||
mount -t securityfs none /sys/kernel/security || {
|
||||
echo >&2 'Could not mount /sys/kernel/security.'
|
||||
echo >&2 'AppArmor detection and --privileged mode might break.'
|
||||
}
|
||||
fi
|
||||
|
||||
# Mount /tmp (conditionally)
|
||||
if ! mountpoint -q /tmp; then
|
||||
mount -t tmpfs none /tmp
|
||||
fi
|
||||
|
||||
# cgroup v2: enable nesting
|
||||
if [ -f /sys/fs/cgroup/cgroup.controllers ]; then
|
||||
# move the processes from the root group to the /init group,
|
||||
# otherwise writing subtree_control fails with EBUSY.
|
||||
# An error during moving non-existent process (i.e., "cat") is ignored.
|
||||
mkdir -p /sys/fs/cgroup/init
|
||||
xargs -rn1 < /sys/fs/cgroup/cgroup.procs > /sys/fs/cgroup/init/cgroup.procs || :
|
||||
# enable controllers
|
||||
sed -e 's/ / +/g' -e 's/^/+/' < /sys/fs/cgroup/cgroup.controllers \
|
||||
> /sys/fs/cgroup/cgroup.subtree_control
|
||||
fi
|
||||
## Dind wrapper over.
|
||||
|
||||
# Handle DNS
|
||||
set +e
|
||||
cat /etc/resolv.conf | grep -i 'internal.cloudapp.net'
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo "Setting dockerd Azure DNS."
|
||||
CUSTOMDNS="--dns 168.63.129.16"
|
||||
else
|
||||
echo "Not setting dockerd DNS manually."
|
||||
CUSTOMDNS=""
|
||||
fi
|
||||
set -e
|
||||
|
||||
# Start docker/moby engine
|
||||
( dockerd $CUSTOMDNS > /tmp/dockerd.log 2>&1 ) &
|
||||
INNEREOF
|
||||
)"
|
||||
|
||||
# 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
|
||||
|
||||
set +e
|
||||
|
||||
# Execute whatever commands were passed in (if any). This allows us
|
||||
# to set this script to ENTRYPOINT while still executing the default CMD.
|
||||
exec "$@"
|
||||
EOF
|
||||
|
||||
chmod +x /usr/local/share/docker-init.sh
|
||||
chown ${USERNAME}:root /usr/local/share/docker-init.sh
|
||||
|
||||
echo 'docker-in-docker-debian script has completed!'
|
28
collection/dotnet/feature.json
Normal file
28
collection/dotnet/feature.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"id": "dotnet",
|
||||
"name": "Dotnet CLI",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest","6.0","5.0","3.1"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a dotnet CLI version. (Available versions may vary by Linux distribution.)"
|
||||
},
|
||||
"runtimeOnly": {
|
||||
"type":"boolean",
|
||||
"default": false,
|
||||
"description": "Install just the dotnet runtime if true, and sdk if false."
|
||||
}
|
||||
},
|
||||
"containerEnv": {
|
||||
"DOTNET_ROOT": "/usr/local/dotnet",
|
||||
"PATH": "${PATH}:${DOTNET_ROOT}"
|
||||
},
|
||||
"extensions": [
|
||||
"ms-dotnettools.csharp"
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
374
collection/dotnet/install.sh
Normal file
374
collection/dotnet/install.sh
Normal file
|
@ -0,0 +1,374 @@
|
|||
#!/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/microsoft/vscode-dev-containers/blob/main/script-library/docs/dotnet.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./dotnet-debian.sh [.NET version] [.NET runtime only] [non-root user] [add TARGET_DOTNET_ROOT to rc files flag] [.NET root] [access group name]
|
||||
|
||||
DOTNET_VERSION=${1:-"latest"}
|
||||
DOTNET_RUNTIME_ONLY=${2:-"false"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
TARGET_DOTNET_ROOT=${5:-"/usr/local/dotnet"}
|
||||
ACCESS_GROUP=${6:-"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"
|
||||
# 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"
|
||||
|
||||
# Exit on failure.
|
||||
set -e
|
||||
|
||||
# Setup STDERR.
|
||||
err() {
|
||||
echo "(!) $*" >&2
|
||||
}
|
||||
|
||||
# Ensure the appropriate root user is running the script.
|
||||
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
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Add TARGET_DOTNET_ROOT variable into PATH in bashrc/zshrc files.
|
||||
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
|
||||
}
|
||||
|
||||
# Run apt-get if needed.
|
||||
apt_get_update_if_needed() {
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if packages are installed and installs them if not.
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get appropriate architecture name for .NET binaries for the target OS
|
||||
get_architecture_name_for_target_os() {
|
||||
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
|
||||
|
||||
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}")"
|
||||
|
||||
# Ensure we've exported useful variables
|
||||
. /etc/os-release
|
||||
local architecture="$(dpkg --print-architecture)"
|
||||
|
||||
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 \"${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 dotnet_major_minor_version
|
||||
export DOTNET_PACKAGE="dotnet-${sdk_or_runtime}"
|
||||
|
||||
# Install dependencies
|
||||
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
|
||||
|
||||
# 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
|
||||
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
|
||||
apt-get update
|
||||
|
||||
if [ "${DOTNET_VERSION}" = "latest" ] || [ "${DOTNET_VERSION}" = "lts" ]; then
|
||||
DOTNET_VERSION=""
|
||||
DOTNET_PACKAGE="${DOTNET_PACKAGE}-6.0"
|
||||
else
|
||||
# Sets DOTNET_VERSION and DOTNET_PACKAGE if matches found.
|
||||
apt_cache_package_and_version_soft_match DOTNET_VERSION DOTNET_PACKAGE false
|
||||
if [ "$?" != 0 ]; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! (apt-get install -yq ${DOTNET_PACKAGE}${DOTNET_VERSION}); then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Find and extract .NET binary download details based on user-requested version
|
||||
# args:
|
||||
# sdk_or_runtime $1
|
||||
# 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_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 DOTNET_VERSION to empty string to ensure jq includes all .NET versions in reverse sort below
|
||||
if [ "${DOTNET_VERSION}" = "latest" ]; then
|
||||
DOTNET_VERSION=""
|
||||
fi
|
||||
|
||||
dotnet_patchless_version="$(echo "${DOTNET_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_VERSION}" | grep -o "\." | wc -l)" -lt "2" ]; then
|
||||
DOTNET_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_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_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_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
err "Unable to find .NET release details for version ${DOTNET_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"
|
||||
|
||||
# Check listed package dependecies and install them if they are not already installed.
|
||||
# NOTE: icu-devtools is a small package with similar dependecies 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 libssl1.1 zlib1g
|
||||
|
||||
get_full_version_details "${sdk_or_runtime}"
|
||||
# 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 "${TARGET_DOTNET_ROOT}"
|
||||
echo "Extract Binary to ${TARGET_DOTNET_ROOT}"
|
||||
tar -xzf "${TMP_DIR}/${DOTNET_DOWNLOAD_NAME}" -C "${TARGET_DOTNET_ROOT}" --strip-components=1
|
||||
|
||||
updaterc "$(cat << EOF
|
||||
export DOTNET_ROOT="${TARGET_DOTNET_ROOT}"
|
||||
if [[ "\${PATH}" != *"\${DOTNET_ROOT}"* ]]; then export PATH="\${PATH}:\${DOTNET_ROOT}"; fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Give write permissions to the user.
|
||||
chown -R ":${ACCESS_GROUP}" "${TARGET_DOTNET_ROOT}"
|
||||
chmod g+r+w+s "${TARGET_DOTNET_ROOT}"
|
||||
chmod -R g+r+w "${TARGET_DOTNET_ROOT}"
|
||||
}
|
||||
|
||||
###########################
|
||||
# Start .NET installation
|
||||
###########################
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# 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
|
||||
fi
|
||||
|
||||
# Install the .NET CLI
|
||||
echo "(*) Installing .NET CLI..."
|
||||
|
||||
. /etc/os-release
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
|
||||
use_dotnet_releases_url="false"
|
||||
if [[ "${DOTNET_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${DOTNET_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]]; then
|
||||
install_using_apt "${DOTNET_SDK_OR_RUNTIME}" || use_dotnet_releases_url="true"
|
||||
else
|
||||
use_dotnet_releases_url="true"
|
||||
fi
|
||||
|
||||
if [ "${use_dotnet_releases_url}" = "true" ]; then
|
||||
install_using_dotnet_releases_url "${DOTNET_SDK_OR_RUNTIME}"
|
||||
fi
|
||||
|
||||
echo "Done!"
|
16
collection/git-lfs/feature.json
Normal file
16
collection/git-lfs/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "git-lfs",
|
||||
"name": "Git Large File Support (LFS)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"enum": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Currently unused."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
194
collection/git-lfs/install.sh
Normal file
194
collection/git-lfs/install.sh
Normal file
|
@ -0,0 +1,194 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/git-lfs.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./git-lfs-debian.sh [version]
|
||||
|
||||
GIT_LFS_VERSION=${1:-"latest"}
|
||||
GIT_LFS_ARCHIVE_GPG_KEY_URI="https://packagecloud.io/github/git-lfs/gpgkey"
|
||||
GIT_LFS_ARCHIVE_ARCHITECTURES="amd64"
|
||||
GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal"
|
||||
GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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
|
||||
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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
install_using_apt() {
|
||||
# Soft version matching
|
||||
if [ "${GIT_LFS_VERSION}" != "latest" ] && [ "${GIT_LFS_VERSION}" != "lts" ] && [ "${GIT_LFS_VERSION}" != "stable" ]; then
|
||||
find_version_from_git_tags GIT_LFS_VERSION "https://github.com/git-lfs/git-lfs"
|
||||
version_suffix="=${GIT_LFS_VERSION}"
|
||||
else
|
||||
version_suffix=""
|
||||
fi
|
||||
# Install
|
||||
get_common_setting GIT_LFS_ARCHIVE_GPG_KEY_URI
|
||||
curl -sSL "${GIT_LFS_ARCHIVE_GPG_KEY_URI}" | gpg --dearmor > /usr/share/keyrings/gitlfs-archive-keyring.gpg
|
||||
echo -e "deb [arch=${architecture} signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main\ndeb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gitlfs-archive-keyring.gpg] https://packagecloud.io/github/git-lfs/${ID} ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/git-lfs.list
|
||||
|
||||
if ! (apt-get update && apt-get install -yq git-lfs${version_suffix}); then
|
||||
rm -f /etc/apt/sources.list.d/git-lfs.list
|
||||
return 1
|
||||
fi
|
||||
|
||||
git-lfs install --skip-repo
|
||||
}
|
||||
|
||||
install_using_github() {
|
||||
echo "(*) No apt package for ${VERSION_CODENAME} ${architecture}. Installing manually."
|
||||
mkdir -p /tmp/git-lfs
|
||||
cd /tmp/git-lfs
|
||||
find_version_from_git_tags GIT_LFS_VERSION "https://github.com/git-lfs/git-lfs"
|
||||
git_lfs_filename="git-lfs-linux-${architecture}-v${GIT_LFS_VERSION}.tar.gz"
|
||||
curl -sSL -o "${git_lfs_filename}" "https://github.com/git-lfs/git-lfs/releases/download/v${GIT_LFS_VERSION}/${git_lfs_filename}"
|
||||
# Verify file
|
||||
curl -sSL -o "sha256sums.asc" "https://github.com/git-lfs/git-lfs/releases/download/v${GIT_LFS_VERSION}/sha256sums.asc"
|
||||
receive_gpg_keys GIT_LFS_CHECKSUM_GPG_KEYS
|
||||
gpg -q --decrypt "sha256sums.asc" > sha256sums
|
||||
sha256sum --ignore-missing -c "sha256sums"
|
||||
# Extract and install
|
||||
tar xf "${git_lfs_filename}" -C .
|
||||
./install.sh
|
||||
rm -rf /tmp/git-lfs /tmp/tmp-gnupg
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install git, curl, gpg, dirmngr and debian-archive-keyring if missing
|
||||
. /etc/os-release
|
||||
check_packages curl ca-certificates gnupg2 dirmngr apt-transport-https
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
if [ "${ID}" = "debian" ]; then
|
||||
check_packages debian-archive-keyring
|
||||
fi
|
||||
|
||||
# Install Git LFS
|
||||
echo "Installing Git LFS..."
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
if [[ "${GIT_LFS_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${GIT_LFS_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]]; then
|
||||
install_using_apt || use_github="true"
|
||||
else
|
||||
use_github="true"
|
||||
fi
|
||||
|
||||
# If no archive exists or apt install fails, try direct from github
|
||||
if [ "${use_github}" = "true" ]; then
|
||||
install_using_github
|
||||
fi
|
||||
|
||||
echo "Done!"
|
21
collection/git/feature.json
Normal file
21
collection/git/feature.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "git",
|
||||
"name": "Git (may require compilation)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "os-provided"],
|
||||
"default": "os-provided",
|
||||
"description": "Select or enter a Git version."
|
||||
},
|
||||
"ppa": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install from PPA if available"
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
153
collection/git/install.sh
Normal file
153
collection/git/install.sh
Normal file
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/git-from-src.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./git-from-src-debian.sh [version] [use PPA if available]
|
||||
|
||||
GIT_VERSION=${1:-"latest"} # 'system' checks the base image first, else installs 'latest'
|
||||
USE_PPA_IF_AVAILABLE=${2:-"false"}
|
||||
|
||||
GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
mkdir -p "$(dirname \"$2\")"
|
||||
keyring_args="--no-default-keyring --keyring $2"
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Source /etc/os-release to get OS info
|
||||
. /etc/os-release
|
||||
|
||||
# If the os provided version is "good enough", just install that.
|
||||
if [ ${GIT_VERSION} = "os-provided" ] || [ ${GIT_VERSION} = "system" ]; then
|
||||
if type git > /dev/null 2>&1; then
|
||||
echo "Detected existing system install: $(git version)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Installing git from OS apt repository"
|
||||
check_packages git
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If ubuntu, PPAs allowed, and latest - install from there
|
||||
if ([ "${GIT_VERSION}" = "latest" ] || [ "${GIT_VERSION}" = "lts" ] || [ "${GIT_VERSION}" = "current" ]) && [ "${ID}" = "ubuntu" ] && [ "${USE_PPA_IF_AVAILABLE}" = "true" ]; then
|
||||
echo "Using PPA to install latest git..."
|
||||
check_packages apt-transport-https curl ca-certificates gnupg2 dirmngr
|
||||
receive_gpg_keys GIT_CORE_PPA_ARCHIVE_GPG_KEY /usr/share/keyrings/gitcoreppa-archive-keyring.gpg
|
||||
echo -e "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gitcoreppa-archive-keyring.gpg] http://ppa.launchpad.net/git-core/ppa/ubuntu ${VERSION_CODENAME} main\ndeb-src [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gitcoreppa-archive-keyring.gpg] http://ppa.launchpad.net/git-core/ppa/ubuntu ${VERSION_CODENAME} main" > /etc/apt/sources.list.d/git-core-ppa.list
|
||||
apt-get update
|
||||
apt-get -y install --no-install-recommends git
|
||||
rm -rf "/tmp/tmp-gnupg"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Install required packages to build if missing
|
||||
check_packages build-essential curl ca-certificates tar gettext libssl-dev zlib1g-dev libcurl?-openssl-dev libexpat1-dev
|
||||
|
||||
# Partial version matching
|
||||
if [ "$(echo "${GIT_VERSION}" | grep -o '\.' | wc -l)" != "2" ]; then
|
||||
requested_version="${GIT_VERSION}"
|
||||
version_list="$(curl -sSL -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/git/git/tags" | grep -oP '"name":\s*"v\K[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"' | sort -rV )"
|
||||
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "lts" ] || [ "${requested_version}" = "current" ]; then
|
||||
GIT_VERSION="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
GIT_VERSION="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${GIT_VERSION}" ] || ! echo "${version_list}" | grep "^${GIT_VERSION//./\\.}$" > /dev/null 2>&1; then
|
||||
echo "Invalid git version: ${requested_version}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Downloading source for ${GIT_VERSION}..."
|
||||
curl -sL https://github.com/git/git/archive/v${GIT_VERSION}.tar.gz | tar -xzC /tmp 2>&1
|
||||
echo "Building..."
|
||||
cd /tmp/git-${GIT_VERSION}
|
||||
make -s prefix=/usr/local all && make -s prefix=/usr/local install 2>&1
|
||||
rm -rf /tmp/git-${GIT_VERSION}
|
||||
echo "Done!"
|
16
collection/github-cli/feature.json
Normal file
16
collection/github-cli/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "github-cli",
|
||||
"name": "GitHub CLI",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select version of the GitHub CLI, if not latest."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
188
collection/github-cli/install.sh
Normal file
188
collection/github-cli/install.sh
Normal file
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/github.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./github-debian.sh [version]
|
||||
|
||||
CLI_VERSION=${1:-"latest"}
|
||||
|
||||
GITHUB_CLI_ARCHIVE_GPG_KEY=C99B11DEB97541F0
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
keyring_args="--no-default-keyring --keyring $2"
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install curl, apt-transport-https, curl, gpg, or dirmngr, git if missing
|
||||
check_packages curl ca-certificates apt-transport-https dirmngr gnupg2
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
# Soft version matching
|
||||
if [ "${CLI_VERSION}" != "latest" ] && [ "${CLI_VERSION}" != "lts" ] && [ "${CLI_VERSION}" != "stable" ]; then
|
||||
find_version_from_git_tags CLI_VERSION "https://github.com/cli/cli"
|
||||
version_suffix="=${CLI_VERSION}"
|
||||
else
|
||||
version_suffix=""
|
||||
fi
|
||||
|
||||
# Install the GitHub CLI
|
||||
echo "Downloading github CLI..."
|
||||
# Import key safely (new method rather than deprecated apt-key approach) and install
|
||||
. /etc/os-release
|
||||
receive_gpg_keys GITHUB_CLI_ARCHIVE_GPG_KEY /usr/share/keyrings/githubcli-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" > /etc/apt/sources.list.d/github-cli.list
|
||||
apt-get update
|
||||
apt-get -y install "gh${version_suffix}"
|
||||
rm -rf "/tmp/gh/gnupg"
|
||||
echo "Done!"
|
24
collection/go/feature.json
Normal file
24
collection/go/feature.json
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"id": "go",
|
||||
"name": "Go",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "1.18", "1.17"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Go version to install"
|
||||
}
|
||||
},
|
||||
"init": true,
|
||||
"extensions": ["golang.Go"],
|
||||
"containerEnv": {
|
||||
"GOPATH": "/go",
|
||||
"PATH": "${GOPATH}/bin:${GOROOT}/bin:${PATH}"
|
||||
},
|
||||
"capAdd": [ "SYS_PTRACE" ],
|
||||
"securityOpt": [ "seccomp=unconfined" ],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
255
collection/go/install.sh
Normal file
255
collection/go/install.sh
Normal file
|
@ -0,0 +1,255 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/go.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./go-debian.sh [Go version] [GOROOT] [GOPATH] [non-root user] [Add GOPATH, GOROOT to rc files flag] [Install tools flag]
|
||||
|
||||
TARGET_GO_VERSION=${1:-"latest"}
|
||||
TARGET_GOROOT=${2:-"/usr/local/go"}
|
||||
TARGET_GOPATH=${3:-"/go"}
|
||||
USERNAME=${4:-"automatic"}
|
||||
UPDATE_RC=${5:-"true"}
|
||||
INSTALL_GO_TOOLS=${6:-"true"}
|
||||
|
||||
# https://www.google.com/linuxrepositories/
|
||||
GO_GPG_KEY_URI="https://dl.google.com/linux/linux_signing_key.pub"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install curl, tar, git, other dependencies if missing
|
||||
check_packages curl ca-certificates gnupg2 tar g++ gcc libc6-dev make pkg-config
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
# Get closest match for version number specified
|
||||
find_version_from_git_tags TARGET_GO_VERSION "https://go.googlesource.com/go" "tags/go" "." "true"
|
||||
|
||||
architecture="$(uname -m)"
|
||||
case $architecture in
|
||||
x86_64) architecture="amd64";;
|
||||
aarch64 | armv8*) architecture="arm64";;
|
||||
aarch32 | armv7* | armvhf*) architecture="armv6l";;
|
||||
i?86) architecture="386";;
|
||||
*) echo "(!) Architecture $architecture unsupported"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Install Go
|
||||
umask 0002
|
||||
if ! cat /etc/group | grep -e "^golang:" > /dev/null 2>&1; then
|
||||
groupadd -r golang
|
||||
fi
|
||||
usermod -a -G golang "${USERNAME}"
|
||||
mkdir -p "${TARGET_GOROOT}" "${TARGET_GOPATH}"
|
||||
if [ "${TARGET_GO_VERSION}" != "none" ] && ! type go > /dev/null 2>&1; then
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
get_common_setting GO_GPG_KEY_URI
|
||||
curl -sSL -o /tmp/tmp-gnupg/golang_key "${GO_GPG_KEY_URI}"
|
||||
gpg -q --import /tmp/tmp-gnupg/golang_key
|
||||
echo "Downloading Go ${TARGET_GO_VERSION}..."
|
||||
set +e
|
||||
curl -fsSL -o /tmp/go.tar.gz "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz"
|
||||
exit_code=$?
|
||||
set -e
|
||||
if [ "$exit_code" != "0" ]; then
|
||||
echo "(!) Download failed."
|
||||
# Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios.
|
||||
set +e
|
||||
major="$(echo "${TARGET_GO_VERSION}" | grep -oE '^[0-9]+' || echo '')"
|
||||
minor="$(echo "${TARGET_GO_VERSION}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')"
|
||||
breakfix="$(echo "${TARGET_GO_VERSION}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')"
|
||||
# Handle Go's odd version pattern where "0" releases omit the last part
|
||||
if [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then
|
||||
((minor=minor-1))
|
||||
TARGET_GO_VERSION="${major}.${minor}"
|
||||
# Look for latest version from previous minor release
|
||||
find_version_from_git_tags TARGET_GO_VERSION "https://go.googlesource.com/go" "tags/go" "." "true"
|
||||
else
|
||||
((breakfix=breakfix-1))
|
||||
if [ "${breakfix}" = "0" ]; then
|
||||
TARGET_GO_VERSION="${major}.${minor}"
|
||||
else
|
||||
TARGET_GO_VERSION="${major}.${minor}.${breakfix}"
|
||||
fi
|
||||
fi
|
||||
set -e
|
||||
echo "Trying ${TARGET_GO_VERSION}..."
|
||||
curl -fsSL -o /tmp/go.tar.gz "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz"
|
||||
fi
|
||||
curl -fsSL -o /tmp/go.tar.gz.asc "https://golang.org/dl/go${TARGET_GO_VERSION}.linux-${architecture}.tar.gz.asc"
|
||||
gpg --verify /tmp/go.tar.gz.asc /tmp/go.tar.gz
|
||||
echo "Extracting Go ${TARGET_GO_VERSION}..."
|
||||
tar -xzf /tmp/go.tar.gz -C "${TARGET_GOROOT}" --strip-components=1
|
||||
rm -rf /tmp/go.tar.gz /tmp/go.tar.gz.asc /tmp/tmp-gnupg
|
||||
else
|
||||
echo "Go already installed. Skipping."
|
||||
fi
|
||||
|
||||
# Install Go tools that are isImportant && !replacedByGopls based on
|
||||
# https://github.com/golang/vscode-go/blob/v0.31.1/src/goToolsInformation.ts
|
||||
GO_TOOLS="\
|
||||
golang.org/x/tools/gopls@latest \
|
||||
honnef.co/go/tools/cmd/staticcheck@latest \
|
||||
golang.org/x/lint/golint@latest \
|
||||
github.com/mgechev/revive@latest \
|
||||
github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest \
|
||||
github.com/ramya-rao-a/go-outline@latest \
|
||||
github.com/go-delve/delve/cmd/dlv@latest \
|
||||
github.com/golangci/golangci-lint/cmd/golangci-lint@latest"
|
||||
if [ "${INSTALL_GO_TOOLS}" = "true" ]; then
|
||||
echo "Installing common Go tools..."
|
||||
export PATH=${TARGET_GOROOT}/bin:${PATH}
|
||||
mkdir -p /tmp/gotools /usr/local/etc/vscode-dev-containers ${TARGET_GOPATH}/bin
|
||||
cd /tmp/gotools
|
||||
export GOPATH=/tmp/gotools
|
||||
export GOCACHE=/tmp/gotools/cache
|
||||
|
||||
# Use go get for versions of go under 1.16
|
||||
go_install_command=install
|
||||
if [[ "1.16" > "$(go version | grep -oP 'go\K[0-9]+\.[0-9]+(\.[0-9]+)?')" ]]; then
|
||||
export GO111MODULE=on
|
||||
go_install_command=get
|
||||
echo "Go version < 1.16, using go get."
|
||||
fi
|
||||
|
||||
(echo "${GO_TOOLS}" | xargs -n 1 go ${go_install_command} -v )2>&1 | tee -a /usr/local/etc/vscode-dev-containers/go.log
|
||||
|
||||
# Move Go tools into path and clean up
|
||||
mv /tmp/gotools/bin/* ${TARGET_GOPATH}/bin/
|
||||
|
||||
rm -rf /tmp/gotools
|
||||
fi
|
||||
|
||||
# Add GOPATH variable and bin directory into PATH in bashrc/zshrc files (unless disabled)
|
||||
updaterc "$(cat << EOF
|
||||
export GOPATH="${TARGET_GOPATH}"
|
||||
if [[ "\${PATH}" != *"\${GOPATH}/bin"* ]]; then export PATH="\${PATH}:\${GOPATH}/bin"; fi
|
||||
export GOROOT="${TARGET_GOROOT}"
|
||||
if [[ "\${PATH}" != *"\${GOROOT}/bin"* ]]; then export PATH="\${PATH}:\${GOROOT}/bin"; fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
chown -R :golang "${TARGET_GOROOT}" "${TARGET_GOPATH}"
|
||||
chmod -R g+r+w "${TARGET_GOROOT}" "${TARGET_GOPATH}"
|
||||
find "${TARGET_GOROOT}" -type d | xargs -n 1 chmod g+s
|
||||
find "${TARGET_GOPATH}" -type d | xargs -n 1 chmod g+s
|
||||
|
||||
echo "Done!"
|
||||
|
22
collection/gradle/feature.json
Normal file
22
collection/gradle/feature.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"id": "gradle",
|
||||
"name": "Gradle (via SDKMAN!)",
|
||||
"documentationURL": "https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/gradle.md",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "7", "6", "5"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Gradle version to install"
|
||||
}
|
||||
},
|
||||
"extensions": ["vscjava.vscode-java-pack"],
|
||||
"containerEnv": {
|
||||
"SDKMAN_DIR": "/usr/local/sdkman",
|
||||
"PATH": "${SDKMAN_DIR}/bin:${SDKMAN_DIR}/candidates/gradle/current/bin:${PATH}"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
0
collection/gradle/install.sh
Normal file
0
collection/gradle/install.sh
Normal file
16
collection/hugo/feature.json
Normal file
16
collection/hugo/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "hugo",
|
||||
"name": "Hugo",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a version."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
113
collection/hugo/install.sh
Normal file
113
collection/hugo/install.sh
Normal file
|
@ -0,0 +1,113 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/hugo.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./hugo-debian.sh [Hugo version] [HUGO_DIR] [Non-root user] [Add rc files flag]
|
||||
|
||||
VERSION=${1:-"latest"}
|
||||
export HUGO_DIR=${2:-"/usr/local/hugo"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
architecture="$(uname -m)"
|
||||
if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then
|
||||
echo "(!) Architecture $architecture unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Install dependencies
|
||||
check_packages curl ca-certificates tar
|
||||
|
||||
# Fetch latest version of Hugo if needed
|
||||
if [ "${VERSION}" = "latest" ] || [ "${VERSION}" = "lts" ]; then
|
||||
export VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}')
|
||||
fi
|
||||
|
||||
# Install Hugo if it's missing
|
||||
if ! hugo version &> /dev/null ; then
|
||||
echo "Installing Hugo..."
|
||||
installation_dir="$HUGO_DIR/bin"
|
||||
mkdir -p "$installation_dir"
|
||||
|
||||
# Install ARM or x86 version of hugo based on current machine architecture
|
||||
if [ "$(uname -m)" == "aarch64" ]; then
|
||||
arch="ARM64"
|
||||
else
|
||||
arch="64bit"
|
||||
fi
|
||||
|
||||
hugo_filename="hugo_${VERSION}_Linux-${arch}.tar.gz"
|
||||
|
||||
curl -fsSLO --compressed "https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${hugo_filename}"
|
||||
tar -xzf "$hugo_filename" -C "$installation_dir"
|
||||
rm "$hugo_filename"
|
||||
|
||||
updaterc "export HUGO_DIR=${installation_dir}"
|
||||
fi
|
||||
|
||||
echo "Done!"
|
26
collection/java/feature.json
Normal file
26
collection/java/feature.json
Normal file
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"id": "java",
|
||||
"name": "Java (via SDKMAN!)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["lts", "latest", "17", "11", "8"],
|
||||
"default": "lts",
|
||||
"description": "Select or enter a Java version to install"
|
||||
}
|
||||
},
|
||||
"buildArg": "_VSC_INSTALL_JAVA",
|
||||
"extensions": ["vscjava.vscode-java-pack"],
|
||||
"containerEnv": {
|
||||
"SDKMAN_DIR": "/usr/local/sdkman",
|
||||
"PATH": "${SDKMAN_DIR}/bin:${SDKMAN_DIR}/candidates/java/current/bin:${PATH}"
|
||||
},
|
||||
"settings": {
|
||||
"java.home": "/extension-java-home",
|
||||
"java.import.gradle.java.home": "/usr/local/sdkman/candidates/java/current"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
145
collection/java/install.sh
Normal file
145
collection/java/install.sh
Normal file
|
@ -0,0 +1,145 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/java.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./java-debian.sh [JDK version] [SDKMAN_DIR] [non-root user] [Add to rc files flag]
|
||||
|
||||
JAVA_VERSION=${1:-"lts"}
|
||||
export SDKMAN_DIR=${2:-"/usr/local/sdkman"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Use SDKMAN to install something using a partial version match
|
||||
sdk_install() {
|
||||
local install_type=$1
|
||||
local requested_version=$2
|
||||
local prefix=$3
|
||||
local suffix="${4:-"\\s*"}"
|
||||
local full_version_check=${5:-".*-[a-z]+"}
|
||||
if [ "${requested_version}" = "none" ]; then return; fi
|
||||
# Blank will install latest stable version SDKMAN has
|
||||
if [ "${requested_version}" = "lts" ] || [ "${requested_version}" = "default" ]; then
|
||||
requested_version=""
|
||||
elif echo "${requested_version}" | grep -oE "${full_version_check}" > /dev/null 2>&1; then
|
||||
echo "${requested_version}"
|
||||
else
|
||||
local regex="${prefix}\\K[0-9]+\\.[0-9]+\\.[0-9]+${suffix}"
|
||||
local version_list="$(. ${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
|
||||
requested_version="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then
|
||||
echo -e "Version $2 not found. Available versions:\n${version_list}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
su ${USERNAME} -c "umask 0002 && . ${SDKMAN_DIR}/bin/sdkman-init.sh && sdk install ${install_type} ${requested_version} && sdk flush archives && sdk flush temp"
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
architecture="$(uname -m)"
|
||||
if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then
|
||||
echo "(!) Architecture $architecture unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
check_packages curl ca-certificates zip unzip sed
|
||||
|
||||
# Install sdkman if not installed
|
||||
if [ ! -d "${SDKMAN_DIR}" ]; then
|
||||
# Create sdkman group, dir, and set sticky bit
|
||||
if ! cat /etc/group | grep -e "^sdkman:" > /dev/null 2>&1; then
|
||||
groupadd -r sdkman
|
||||
fi
|
||||
usermod -a -G sdkman ${USERNAME}
|
||||
umask 0002
|
||||
# Install SDKMAN
|
||||
curl -sSL "https://get.sdkman.io?rcupdate=false" | bash
|
||||
chown -R :sdkman ${SDKMAN_DIR}
|
||||
find ${SDKMAN_DIR} -type d | xargs -d '\n' chmod g+s
|
||||
# Add sourcing of sdkman into bashrc/zshrc files (unless disabled)
|
||||
updaterc "export SDKMAN_DIR=${SDKMAN_DIR}\n. \${SDKMAN_DIR}/bin/sdkman-init.sh"
|
||||
fi
|
||||
|
||||
# Use Microsoft JDK for everything but JDK 8
|
||||
jdk_distro="ms"
|
||||
if echo "${JAVA_VERSION}" | grep -E '^8([\s\.]|$)' > /dev/null 2>&1; then
|
||||
jdk_distro="tem"
|
||||
fi
|
||||
if [ "${JAVA_VERSION}" = "lts" ]; then
|
||||
JAVA_VERSION="17"
|
||||
fi
|
||||
sdk_install java ${JAVA_VERSION} "\\s*" "(\\.[a-z0-9]+)*-${jdk_distro}\\s*" ".*-[a-z]+$"
|
||||
|
||||
echo "Done!"
|
16
collection/jekyll/feature.json
Normal file
16
collection/jekyll/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "jekyll",
|
||||
"name": "Jekyll",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a version."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
60
collection/jekyll/install.sh
Normal file
60
collection/jekyll/install.sh
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/hugo.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./jekyll-debian.sh [Jekyll version] [Non-root user] [Add rc files flag]
|
||||
|
||||
VERSION=${1:-"latest"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
|
||||
set -e
|
||||
|
||||
# If in automatic mode, determine if a user already exists, if not use codespace
|
||||
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=codespace
|
||||
fi
|
||||
elif [ "${USERNAME}" = "none" ]; then
|
||||
USERNAME=root
|
||||
USER_UID=0
|
||||
USER_GID=0
|
||||
fi
|
||||
|
||||
# Use sudo to run as non-root user is not already running
|
||||
sudoUserIf()
|
||||
{
|
||||
if [ "$(id -u)" -eq 0 ] && [ "${USERNAME}" != "root" ]; then
|
||||
sudo -u ${USERNAME} "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# If we don't yet have Ruby installed, exit.
|
||||
if ! /usr/local/rvm/rubies/default/bin/ruby --version > /dev/null ; then
|
||||
echo "You need to install Ruby before installing Jekyll."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If we don't already have Jekyll installed, install it now.
|
||||
if ! jekyll --version > /dev/null ; then
|
||||
echo "Installing Jekyll..."
|
||||
if [ "${VERSION}" = "latest" ]; then
|
||||
PATH="/usr/local/rvm/rubies/default/bin:${PATH}" /usr/local/rvm/rubies/default/bin/gem install jekyll
|
||||
else
|
||||
PATH="/usr/local/rvm/rubies/default/bin:${PATH}" /usr/local/rvm/rubies/default/bin/gem install jekyll -v "${VERSION}"
|
||||
fi
|
||||
fi
|
21
collection/jupyterlab/feature.json
Normal file
21
collection/jupyterlab/feature.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"id": "jupyterlab",
|
||||
"name": "Jupyter Lab",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "3.6.2"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a jupyterlab version."
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"ms-python.vscode-pylance",
|
||||
"ms-toolsai.jupyter"
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
51
collection/jupyterlab/install.sh
Normal file
51
collection/jupyterlab/install.sh
Normal file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=${1:-"latest"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
|
||||
# If in automatic mode, determine if a user already exists, if not use vscode
|
||||
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=vscode
|
||||
fi
|
||||
elif [ "${USERNAME}" = "none" ]; then
|
||||
USERNAME=root
|
||||
USER_UID=0
|
||||
USER_GID=0
|
||||
fi
|
||||
|
||||
# Use sudo to run as non-root user is not already running
|
||||
sudoUserIf()
|
||||
{
|
||||
if [ "$(id -u)" -eq 0 ] && [ "${USERNAME}" != "root" ]; then
|
||||
sudo -u ${USERNAME} "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# If we don't yet have Python, install it now.
|
||||
if ! python --version > /dev/null ; then
|
||||
echo "You need to install Python before installing JupyterLab."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If we don't already have JupyterLab installed, install it now.
|
||||
if ! jupyter-lab --version > /dev/null ; then
|
||||
echo "Installing JupyterLab..."
|
||||
if [ "${VERSION}" = "latest" ]; then
|
||||
pip install jupyterlab
|
||||
else
|
||||
pip install jupyterlab=="${VERSION}" --no-cache-dir
|
||||
fi
|
||||
fi
|
38
collection/kubectl-helm-minikube/feature.json
Normal file
38
collection/kubectl-helm-minikube/feature.json
Normal file
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"id": "kubectl-helm-minikube",
|
||||
"name": "Kubectl, Helm, and Minkube",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "1.23", "1.22", "1.21"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Kubernetes version to install"
|
||||
},
|
||||
"helm": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Helm version to install"
|
||||
},
|
||||
"minikube": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Minikube version to install"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools"
|
||||
],
|
||||
"mounts": [
|
||||
{
|
||||
"source":"minikube-config",
|
||||
"target":"/home/vscode/.minikube",
|
||||
"type":"volume"
|
||||
}
|
||||
],
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
249
collection/kubectl-helm-minikube/install.sh
Normal file
249
collection/kubectl-helm-minikube/install.sh
Normal file
|
@ -0,0 +1,249 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/kubectl-helm.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./kubectl-helm-debian.sh [kubectl verison] [Helm version] [minikube version] [kubectl SHA256] [Helm SHA256] [minikube SHA256]
|
||||
|
||||
set -e
|
||||
|
||||
KUBECTL_VERSION="${1:-"latest"}"
|
||||
HELM_VERSION="${2:-"latest"}"
|
||||
MINIKUBE_VERSION="${3:-"none"}" # latest is also valid
|
||||
KUBECTL_SHA256="${4:-"automatic"}"
|
||||
HELM_SHA256="${5:-"automatic"}"
|
||||
MINIKUBE_SHA256="${6:-"automatic"}"
|
||||
USERNAME=${7:-"automatic"}
|
||||
|
||||
HELM_GPG_KEYS_URI="https://raw.githubusercontent.com/helm/helm/main/KEYS"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
USERHOME="/home/$USERNAME"
|
||||
if [ "$USERNAME" = "root" ]; then
|
||||
USERHOME="/root"
|
||||
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
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
check_packages curl ca-certificates coreutils gnupg2 dirmngr bash-completion
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
architecture="$(uname -m)"
|
||||
case $architecture in
|
||||
x86_64) architecture="amd64";;
|
||||
aarch64 | armv8*) architecture="arm64";;
|
||||
aarch32 | armv7* | armvhf*) architecture="arm";;
|
||||
i?86) architecture="386";;
|
||||
*) echo "(!) Architecture $architecture unsupported"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# Install the kubectl, verify checksum
|
||||
echo "Downloading kubectl..."
|
||||
if [ "${KUBECTL_VERSION}" = "latest" ] || [ "${KUBECTL_VERSION}" = "lts" ] || [ "${KUBECTL_VERSION}" = "current" ] || [ "${KUBECTL_VERSION}" = "stable" ]; then
|
||||
KUBECTL_VERSION="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
|
||||
else
|
||||
find_version_from_git_tags KUBECTL_VERSION https://github.com/kubernetes/kubernetes
|
||||
fi
|
||||
if [ "${KUBECTL_VERSION::1}" != 'v' ]; then
|
||||
KUBECTL_VERSION="v${KUBECTL_VERSION}"
|
||||
fi
|
||||
curl -sSL -o /usr/local/bin/kubectl "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${architecture}/kubectl"
|
||||
chmod 0755 /usr/local/bin/kubectl
|
||||
if [ "$KUBECTL_SHA256" = "automatic" ]; then
|
||||
KUBECTL_SHA256="$(curl -sSL "https://dl.k8s.io/${KUBECTL_VERSION}/bin/linux/${architecture}/kubectl.sha256")"
|
||||
fi
|
||||
([ "${KUBECTL_SHA256}" = "dev-mode" ] || (echo "${KUBECTL_SHA256} */usr/local/bin/kubectl" | sha256sum -c -))
|
||||
if ! type kubectl > /dev/null 2>&1; then
|
||||
echo '(!) kubectl installation failed!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# kubectl bash completion
|
||||
kubectl completion bash > /etc/bash_completion.d/kubectl
|
||||
|
||||
# kubectl zsh completion
|
||||
if [ -e "${USERHOME}}/.oh-my-zsh" ]; then
|
||||
mkdir -p "${USERHOME}/.oh-my-zsh/completions"
|
||||
kubectl completion zsh > "${USERHOME}/.oh-my-zsh/completions/_kubectl"
|
||||
chown -R "${USERNAME}" "${USERHOME}/.oh-my-zsh"
|
||||
fi
|
||||
|
||||
# Install Helm, verify signature and checksum
|
||||
echo "Downloading Helm..."
|
||||
find_version_from_git_tags HELM_VERSION "https://github.com/helm/helm"
|
||||
if [ "${HELM_VERSION::1}" != 'v' ]; then
|
||||
HELM_VERSION="v${HELM_VERSION}"
|
||||
fi
|
||||
mkdir -p /tmp/helm
|
||||
helm_filename="helm-${HELM_VERSION}-linux-${architecture}.tar.gz"
|
||||
tmp_helm_filename="/tmp/helm/${helm_filename}"
|
||||
curl -sSL "https://get.helm.sh/${helm_filename}" -o "${tmp_helm_filename}"
|
||||
curl -sSL "https://github.com/helm/helm/releases/download/${HELM_VERSION}/${helm_filename}.asc" -o "${tmp_helm_filename}.asc"
|
||||
export GNUPGHOME="/tmp/helm/gnupg"
|
||||
mkdir -p "${GNUPGHOME}"
|
||||
chmod 700 ${GNUPGHOME}
|
||||
get_common_setting HELM_GPG_KEYS_URI
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
curl -sSL "${HELM_GPG_KEYS_URI}" -o /tmp/helm/KEYS
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
gpg -q --import "/tmp/helm/KEYS"
|
||||
if ! gpg --verify "${tmp_helm_filename}.asc" > ${GNUPGHOME}/verify.log 2>&1; then
|
||||
echo "Verification failed!"
|
||||
cat /tmp/helm/gnupg/verify.log
|
||||
exit 1
|
||||
fi
|
||||
if [ "${HELM_SHA256}" = "automatic" ]; then
|
||||
curl -sSL "https://get.helm.sh/${helm_filename}.sha256" -o "${tmp_helm_filename}.sha256"
|
||||
curl -sSL "https://github.com/helm/helm/releases/download/${HELM_VERSION}/${helm_filename}.sha256.asc" -o "${tmp_helm_filename}.sha256.asc"
|
||||
if ! gpg --verify "${tmp_helm_filename}.sha256.asc" > /tmp/helm/gnupg/verify.log 2>&1; then
|
||||
echo "Verification failed!"
|
||||
cat /tmp/helm/gnupg/verify.log
|
||||
exit 1
|
||||
fi
|
||||
HELM_SHA256="$(cat "${tmp_helm_filename}.sha256")"
|
||||
fi
|
||||
([ "${HELM_SHA256}" = "dev-mode" ] || (echo "${HELM_SHA256} *${tmp_helm_filename}" | sha256sum -c -))
|
||||
tar xf "${tmp_helm_filename}" -C /tmp/helm
|
||||
mv -f "/tmp/helm/linux-${architecture}/helm" /usr/local/bin/
|
||||
chmod 0755 /usr/local/bin/helm
|
||||
rm -rf /tmp/helm
|
||||
if ! type helm > /dev/null 2>&1; then
|
||||
echo '(!) Helm installation failed!'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install Minikube, verify checksum
|
||||
if [ "${MINIKUBE_VERSION}" != "none" ]; then
|
||||
echo "Downloading minikube..."
|
||||
if [ "${MINIKUBE_VERSION}" = "latest" ] || [ "${MINIKUBE_VERSION}" = "lts" ] || [ "${MINIKUBE_VERSION}" = "current" ] || [ "${MINIKUBE_VERSION}" = "stable" ]; then
|
||||
MINIKUBE_VERSION="latest"
|
||||
else
|
||||
find_version_from_git_tags MINIKUBE_VERSION https://github.com/kubernetes/minikube
|
||||
if [ "${MINIKUBE_VERSION::1}" != "v" ]; then
|
||||
MINIKUBE_VERSION="v${MINIKUBE_VERSION}"
|
||||
fi
|
||||
fi
|
||||
# latest is also valid in the download URLs
|
||||
curl -sSL -o /usr/local/bin/minikube "https://storage.googleapis.com/minikube/releases/${MINIKUBE_VERSION}/minikube-linux-${architecture}"
|
||||
chmod 0755 /usr/local/bin/minikube
|
||||
if [ "$MINIKUBE_SHA256" = "automatic" ]; then
|
||||
MINIKUBE_SHA256="$(curl -sSL "https://storage.googleapis.com/minikube/releases/${MINIKUBE_VERSION}/minikube-linux-${architecture}.sha256")"
|
||||
fi
|
||||
([ "${MINIKUBE_SHA256}" = "dev-mode" ] || (echo "${MINIKUBE_SHA256} */usr/local/bin/minikube" | sha256sum -c -))
|
||||
if ! type minikube > /dev/null 2>&1; then
|
||||
echo '(!) minikube installation failed!'
|
||||
exit 1
|
||||
fi
|
||||
# Create minkube folder with correct privs in case a volume is mounted here
|
||||
mkdir -p "${USERHOME}/.minikube"
|
||||
chown -R $USERNAME "${USERHOME}/.minikube"
|
||||
chmod -R u+wrx "${USERHOME}/.minikube"
|
||||
fi
|
||||
|
||||
if ! type docker > /dev/null 2>&1; then
|
||||
echo -e '\n(*) Warning: The docker command was not found.\n\nYou can use one of the following scripts to install it:\n\nhttps://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker-in-docker.md\n\nor\n\nhttps://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/docker.md'
|
||||
fi
|
||||
|
||||
echo -e "\nDone!"
|
22
collection/maven/feature.json
Normal file
22
collection/maven/feature.json
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"id": "maven",
|
||||
"name": "Maven (via SDKMAN!)",
|
||||
"documentationURL": "https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/docs/maven.md",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "3.8", "3.6", "3.5"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Maven version to install"
|
||||
}
|
||||
},
|
||||
"extensions": ["vscjava.vscode-java-pack"],
|
||||
"containerEnv": {
|
||||
"SDKMAN_DIR": "/usr/local/sdkman",
|
||||
"PATH": "${SDKMAN_DIR}/bin:${SDKMAN_DIR}/candidates/maven/current/bin:${PATH}"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
133
collection/maven/install.sh
Normal file
133
collection/maven/install.sh
Normal file
|
@ -0,0 +1,133 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/maven.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./maven-debian.sh [maven version] [SDKMAN_DIR] [non-root user] [Update rc files flag]
|
||||
|
||||
MAVEN_VERSION=${1:-"latest"}
|
||||
export SDKMAN_DIR=${2:-"/usr/local/sdkman"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Use SDKMAN to install something using a partial version match
|
||||
sdk_install() {
|
||||
local install_type=$1
|
||||
local requested_version=$2
|
||||
local prefix=$3
|
||||
local suffix="${4:-"\\s*"}"
|
||||
local full_version_check=${5:-".*-[a-z]+"}
|
||||
if [ "${requested_version}" = "none" ]; then return; fi
|
||||
# Blank will install latest stable version
|
||||
if [ "${requested_version}" = "lts" ] || [ "${requested_version}" = "default" ]; then
|
||||
requested_version=""
|
||||
elif echo "${requested_version}" | grep -oE "${full_version_check}" > /dev/null 2>&1; then
|
||||
echo "${requested_version}"
|
||||
else
|
||||
local regex="${prefix}\\K[0-9]+\\.[0-9]+\\.[0-9]+${suffix}"
|
||||
local version_list="$(. ${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
|
||||
requested_version="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then
|
||||
echo -e "Version $2 not found. Available versions:\n${version_list}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
su ${USERNAME} -c "umask 0002 && . ${SDKMAN_DIR}/bin/sdkman-init.sh && sdk install ${install_type} ${requested_version} && sdk flush archives && sdk flush temp"
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
check_packages curl ca-certificates zip unzip sed
|
||||
|
||||
# Install sdkman if not installed
|
||||
if [ ! -d "${SDKMAN_DIR}" ]; then
|
||||
# Create sdkman group, dir, and set sticky bit
|
||||
if ! cat /etc/group | grep -e "^sdkman:" > /dev/null 2>&1; then
|
||||
groupadd -r sdkman
|
||||
fi
|
||||
usermod -a -G sdkman ${USERNAME}
|
||||
umask 0002
|
||||
# Install SDKMAN
|
||||
curl -sSL "https://get.sdkman.io?rcupdate=false" | bash
|
||||
chown -R :sdkman ${SDKMAN_DIR}
|
||||
find ${SDKMAN_DIR} -type d | xargs -d '\n' chmod g+s
|
||||
# Add sourcing of sdkman into bashrc/zshrc files (unless disabled)
|
||||
updaterc "export SDKMAN_DIR=${SDKMAN_DIR}\n. \${SDKMAN_DIR}/bin/sdkman-init.sh"
|
||||
fi
|
||||
|
||||
# Install Maven
|
||||
sdk_install maven ${MAVEN_VERSION} '\s\s' '\s\s' '^[0-9]+\.[0-9]+\.[0-9]+$'
|
||||
updaterc '[ -z "$M2" ] && export M2=$HOME/.m2'
|
||||
|
||||
echo "Done!"
|
27
collection/node/feature.json
Normal file
27
collection/node/feature.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"id": "node",
|
||||
"name": "Node.js (via nvm) and yarn",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": [ "lts", "latest", "18", "16", "14" ],
|
||||
"default": "lts",
|
||||
"description": "Select or enter a Node.js version to install"
|
||||
},
|
||||
"nodeGypDependencies": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install dependencies to compile native node modules (node-gyp)?"
|
||||
}
|
||||
},
|
||||
"extensions": ["dbaeumer.vscode-eslint"],
|
||||
"containerEnv": {
|
||||
"NVM_DIR":"/usr/local/share/nvm",
|
||||
"NVM_SYMLINK_CURRENT": "true",
|
||||
"PATH": "${NVM_DIR}/current/bin:${PATH}"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
169
collection/node/install.sh
Normal file
169
collection/node/install.sh
Normal file
|
@ -0,0 +1,169 @@
|
|||
#!/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/microsoft/vscode-dev-containers/blob/main/script-library/docs/node.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./node-debian.sh [directory to install nvm] [node version to install (use "none" to skip)] [non-root user] [Update rc files flag] [install node-gyp deps]
|
||||
|
||||
export NVM_DIR=${1:-"/usr/local/share/nvm"}
|
||||
export NODE_VERSION=${2:-"lts"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
INSTALL_TOOLS_FOR_NODE_GYP="${5:-true}"
|
||||
export NVM_VERSION="0.38.0"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies
|
||||
check_packages apt-transport-https curl ca-certificates tar gnupg2 dirmngr
|
||||
|
||||
# Install yarn
|
||||
if type yarn > /dev/null 2>&1; then
|
||||
echo "Yarn already installed."
|
||||
else
|
||||
# Import key safely (new method rather than deprecated apt-key approach) and install
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor > /usr/share/keyrings/yarn-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/yarn-archive-keyring.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
|
||||
apt-get update
|
||||
apt-get -y install --no-install-recommends yarn
|
||||
fi
|
||||
|
||||
# Adjust node version if required
|
||||
if [ "${NODE_VERSION}" = "none" ]; then
|
||||
export NODE_VERSION=
|
||||
elif [ "${NODE_VERSION}" = "lts" ]; then
|
||||
export NODE_VERSION="lts/*"
|
||||
fi
|
||||
|
||||
# Create a symlink to the installed version for use in Dockerfile PATH statements
|
||||
export NVM_SYMLINK_CURRENT=true
|
||||
|
||||
# Install the specified node version if NVM directory already exists, then exit
|
||||
if [ -d "${NVM_DIR}" ]; then
|
||||
echo "NVM already installed."
|
||||
if [ "${NODE_VERSION}" != "" ]; then
|
||||
su ${USERNAME} -c ". $NVM_DIR/nvm.sh && nvm install ${NODE_VERSION} && nvm clear-cache"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create nvm group, nvm dir, and set sticky bit
|
||||
if ! cat /etc/group | grep -e "^nvm:" > /dev/null 2>&1; then
|
||||
groupadd -r nvm
|
||||
fi
|
||||
umask 0002
|
||||
usermod -a -G nvm ${USERNAME}
|
||||
mkdir -p ${NVM_DIR}
|
||||
chown :nvm ${NVM_DIR}
|
||||
chmod g+s ${NVM_DIR}
|
||||
su ${USERNAME} -c "$(cat << EOF
|
||||
set -e
|
||||
umask 0002
|
||||
# Do not update profile - we'll do this manually
|
||||
export PROFILE=/dev/null
|
||||
curl -so- https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash
|
||||
source ${NVM_DIR}/nvm.sh
|
||||
if [ "${NODE_VERSION}" != "" ]; then
|
||||
nvm alias default ${NODE_VERSION}
|
||||
fi
|
||||
nvm clear-cache
|
||||
EOF
|
||||
)" 2>&1
|
||||
# Update rc files
|
||||
if [ "${UPDATE_RC}" = "true" ]; then
|
||||
updaterc "$(cat <<EOF
|
||||
export NVM_DIR="${NVM_DIR}"
|
||||
[ -s "\$NVM_DIR/nvm.sh" ] && . "\$NVM_DIR/nvm.sh"
|
||||
[ -s "\$NVM_DIR/bash_completion" ] && . "\$NVM_DIR/bash_completion"
|
||||
EOF
|
||||
)"
|
||||
fi
|
||||
|
||||
# If enabled, verify "python3", "make", "gcc", "g++" commands are available so node-gyp works - https://github.com/nodejs/node-gyp
|
||||
if [ "${INSTALL_TOOLS_FOR_NODE_GYP}" = "true" ]; then
|
||||
echo "Verifying node-gyp OS requirements..."
|
||||
to_install=""
|
||||
if ! type make > /dev/null 2>&1; then
|
||||
to_install="${to_install} make"
|
||||
fi
|
||||
if ! type gcc > /dev/null 2>&1; then
|
||||
to_install="${to_install} gcc"
|
||||
fi
|
||||
if ! type g++ > /dev/null 2>&1; then
|
||||
to_install="${to_install} g++"
|
||||
fi
|
||||
if ! type python3 > /dev/null 2>&1; then
|
||||
to_install="${to_install} python3-minimal"
|
||||
fi
|
||||
if [ ! -z "${to_install}" ]; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install ${to_install}
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Done!"
|
16
collection/powershell/feature.json
Normal file
16
collection/powershell/feature.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"id": "powershell",
|
||||
"name": "PowerShell",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "7.1"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a version of PowerShell."
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
170
collection/powershell/install.sh
Normal file
170
collection/powershell/install.sh
Normal file
|
@ -0,0 +1,170 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/powershell.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./powershell-debian.sh [version]
|
||||
|
||||
set -e
|
||||
|
||||
POWERSHELL_VERSION=${1:-"latest"}
|
||||
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
|
||||
POWERSHELL_ARCHIVE_ARCHITECTURES="amd64"
|
||||
POWERSHELL_ARCHIVE_VERSION_CODENAMES="stretch buster bionic focal"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
install_using_apt() {
|
||||
# Install dependencies
|
||||
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
|
||||
get_common_setting MICROSOFT_GPG_KEYS_URI
|
||||
curl -sSL ${MICROSOFT_GPG_KEYS_URI} | gpg --dearmor > /usr/share/keyrings/microsoft-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-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
|
||||
|
||||
# Update lists
|
||||
apt-get update -yq
|
||||
|
||||
# Soft version matching for CLI
|
||||
if [ "${POWERSHELL_VERSION}" = "latest" ] || [ "${POWERSHELL_VERSION}" = "lts" ] || [ "${POWERSHELL_VERSION}" = "stable" ]; then
|
||||
# Empty, meaning grab whatever "latest" is in apt repo
|
||||
version_suffix=""
|
||||
else
|
||||
version_suffix="=$(apt-cache madison powershell | awk -F"|" '{print $2}' | sed -e 's/^[ \t]*//' | grep -E -m 1 "^(${POWERSHELL_VERSION})(\.|$|\+.*|-.*)")"
|
||||
|
||||
if [ -z ${version_suffix} ] || [ ${version_suffix} = "=" ]; then
|
||||
echo "Provided POWERSHELL_VERSION (${POWERSHELL_VERSION}) was not found in the apt-cache for this package+distribution combo";
|
||||
return 1
|
||||
fi
|
||||
echo "version_suffix ${version_suffix}"
|
||||
fi
|
||||
|
||||
apt-get install -yq powershell${version_suffix} || return 1
|
||||
}
|
||||
|
||||
install_using_github() {
|
||||
# Fall back on direct download if no apt package exists in microsoft pool
|
||||
check_packages curl ca-certificates gnupg2 dirmngr libc6 libgcc1 libgssapi-krb5-2 liblttng-ust0 libstdc++6 libunwind8 libuuid1 zlib1g libicu[0-9][0-9]
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get install -y --no-install-recommends git
|
||||
fi
|
||||
if [ "${architecture}" = "amd64" ]; then
|
||||
architecture="x64"
|
||||
fi
|
||||
find_version_from_git_tags POWERSHELL_VERSION https://github.com/PowerShell/PowerShell
|
||||
powershell_filename="powershell-${POWERSHELL_VERSION}-linux-${architecture}.tar.gz"
|
||||
powershell_target_path="/opt/microsoft/powershell/$(echo ${POWERSHELL_VERSION} | grep -oE '[^\.]+' | head -n 1)"
|
||||
mkdir -p /tmp/pwsh "${powershell_target_path}"
|
||||
cd /tmp/pwsh
|
||||
curl -sSL -o "${powershell_filename}" "https://github.com/PowerShell/PowerShell/releases/download/v${POWERSHELL_VERSION}/${powershell_filename}"
|
||||
# Ugly - but only way to get sha256 is to parse release HTML. Remove newlines and tags, then look for filename followed by 64 hex characters.
|
||||
curl -sSL -o "release.html" "https://github.com/PowerShell/PowerShell/releases/tag/v${POWERSHELL_VERSION}"
|
||||
powershell_archive_sha256="$(cat release.html | tr '\n' ' ' | sed 's|<[^>]*>||g' | grep -oP "${powershell_filename}\s+\K[0-9a-fA-F]{64}" || echo '')"
|
||||
if [ -z "${powershell_archive_sha256}" ]; then
|
||||
echo "(!) WARNING: Failed to retrieve SHA256 for archive. Skipping validaiton."
|
||||
else
|
||||
echo "SHA256: ${powershell_archive_sha256}"
|
||||
echo "${powershell_archive_sha256} *${powershell_filename}" | sha256sum -c -
|
||||
fi
|
||||
tar xf "${powershell_filename}" -C "${powershell_target_path}"
|
||||
ln -s "${powershell_target_path}/pwsh" /usr/local/bin/pwsh
|
||||
rm -rf /tmp/pwsh
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Source /etc/os-release to get OS info
|
||||
. /etc/os-release
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
|
||||
if [[ "${POWERSHELL_ARCHIVE_ARCHITECTURES}" = *"${architecture}"* ]] && [[ "${POWERSHELL_ARCHIVE_VERSION_CODENAMES}" = *"${VERSION_CODENAME}"* ]]; then
|
||||
install_using_apt || use_github="true"
|
||||
else
|
||||
use_github="true"
|
||||
fi
|
||||
|
||||
if [ "${use_github}" = "true" ]; then
|
||||
echo "Attempting install from GitHub release..."
|
||||
install_using_github
|
||||
fi
|
||||
|
||||
echo "Done!"
|
56
collection/python/feature.json
Normal file
56
collection/python/feature.json
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"id": "python",
|
||||
"name": "Python (may require compilation)",
|
||||
"description": "Python (may require compilation)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"enum": ["latest", "os-provided", "3.10", "3.9", "3.8", "3.7", "3.6"],
|
||||
"default": "os-provided",
|
||||
"description": "Select a Python version to install."
|
||||
},
|
||||
"installTools": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Install common Python tools like pylint"
|
||||
},
|
||||
"optimize": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Optimize Python for performance when compiled (slow)"
|
||||
},
|
||||
"install_path": {
|
||||
"type": "string",
|
||||
"default": "/usr/local/python",
|
||||
"description": "The path where python will be installed."
|
||||
},
|
||||
"setup_links": {
|
||||
"type": "boolean",
|
||||
"default": "true",
|
||||
"description": "If links and aliases should be created."
|
||||
}
|
||||
},
|
||||
"containerEnv": {
|
||||
"PYTHON_PATH": "/usr/local/python",
|
||||
"PIPX_HOME": "/usr/local/py-utils",
|
||||
"PIPX_BIN_DIR": "/usr/local/py-utils/bin",
|
||||
"PATH": "${PYTHON_PATH}/bin:${PATH}:${PIPX_BIN_DIR}"
|
||||
},
|
||||
"extensions": ["ms-python.python", "ms-python.vscode-pylance"],
|
||||
"settings": {
|
||||
"python.defaultInterpreterPath": "/usr/local/bin/python",
|
||||
"python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8",
|
||||
"python.formatting.blackPath": "/usr/local/py-utils/bin/black",
|
||||
"python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf",
|
||||
"python.linting.banditPath": "/usr/local/py-utils/bin/bandit",
|
||||
"python.linting.flake8Path": "/usr/local/py-utils/bin/flake8",
|
||||
"python.linting.mypyPath": "/usr/local/py-utils/bin/mypy",
|
||||
"python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle",
|
||||
"python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle",
|
||||
"python.linting.pylintPath": "/usr/local/py-utils/bin/pylint"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
376
collection/python/python-debian.sh
Normal file
376
collection/python/python-debian.sh
Normal file
|
@ -0,0 +1,376 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PYTHON_VERSION=($PYTHON_VERSION) # 'system' checks the base image first, else installs 'latest'
|
||||
PYTHON_INSTALL_PATH=($PYTHON_INSTALL_PATH)
|
||||
PYTHON_SETUP_LINKS=($PYTHON_SETUP_LINKS)
|
||||
|
||||
#PYTHON_VERSION=${1:-"latest"} # 'system' checks the base image first, else installs 'latest'
|
||||
#PYTHON_INSTALL_PATH=${2:-"/usr/local/python"}
|
||||
export PIPX_HOME=${3:-"/usr/local/py-utils"}
|
||||
USERNAME=${4:-"automatic"}
|
||||
UPDATE_RC=${5:-"true"}
|
||||
INSTALL_PYTHON_TOOLS=${6:-"true"}
|
||||
USE_ORYX_IF_AVAILABLE=${7:-"true"}
|
||||
OPTIMIZE_BUILD_FROM_SOURCE=${8-"false"}
|
||||
|
||||
DEFAULT_UTILS=("pylint" "flake8" "autopep8" "black" "yapf" "mypy" "pydocstyle" "pycodestyle" "bandit" "pipenv" "virtualenv")
|
||||
PYTHON_SOURCE_GPG_KEYS="64E628F8D684696D B26995E310250568 2D347EA6AA65421D FB9921286F5E1540 3A5CA953F73C700D 04C367C218ADD4FF 0EDDC5F26A45C816 6AF053F07D9DC8D2 C9BE28DEE6DF025C 126EB563A74B06BF D9866941EA5BBD71 ED9D77D5"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
mkdir -p "$(dirname \"$2\")"
|
||||
keyring_args="--no-default-keyring --keyring $2"
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Use Oryx to install something using a partial version match
|
||||
oryx_install() {
|
||||
local platform=$1
|
||||
local requested_version=$2
|
||||
local target_folder=${3:-none}
|
||||
local ldconfig_folder=${4:-none}
|
||||
echo "(*) Installing ${platform} ${requested_version} using Oryx..."
|
||||
check_packages jq
|
||||
# Soft match if full version not specified
|
||||
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
|
||||
local version_list="$(oryx platforms --json | jq -r ".[] | select(.Name == \"${platform}\") | .Versions | sort | reverse | @tsv" | tr '\t' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$')"
|
||||
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
|
||||
requested_version="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then
|
||||
echo -e "(!) Oryx does not support ${platform} version $2\nValid values:\n${version_list}" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "(*) Using ${requested_version} in place of $2."
|
||||
fi
|
||||
|
||||
export ORYX_ENV_TYPE=vsonline-present ORYX_PREFER_USER_INSTALLED_SDKS=true ENABLE_DYNAMIC_INSTALL=true DYNAMIC_INSTALL_ROOT_DIR=/opt
|
||||
oryx prep --skip-detection --platforms-and-versions "${platform}=${requested_version}"
|
||||
local opt_folder="/opt/${platform}/${requested_version}"
|
||||
if [ "${target_folder}" != "none" ] && [ "${target_folder}" != "${opt_folder}" ]; then
|
||||
ln -s "${opt_folder}" "${target_folder}"
|
||||
fi
|
||||
# Update library path add to conf
|
||||
if [ "${ldconfig_folder}" != "none" ]; then
|
||||
echo "/opt/${platform}/${requested_version}/lib" >> "/etc/ld.so.conf.d/${platform}.conf"
|
||||
ldconfig
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
install_from_source() {
|
||||
if [ -d "${PYTHON_INSTALL_PATH}" ]; then
|
||||
echo "(!) Path ${PYTHON_INSTALL_PATH} already exists. Remove this existing path or select a different one."
|
||||
exit 1
|
||||
fi
|
||||
echo "(*) Building Python ${PYTHON_VERSION} from source..."
|
||||
# Install prereqs if missing
|
||||
check_packages curl ca-certificates gnupg2 tar make gcc libssl-dev zlib1g-dev libncurses5-dev \
|
||||
libbz2-dev libreadline-dev libxml2-dev xz-utils libgdbm-dev tk-dev dirmngr \
|
||||
libxmlsec1-dev libsqlite3-dev libffi-dev liblzma-dev uuid-dev
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
# Find version using soft match
|
||||
find_version_from_git_tags PYTHON_VERSION "https://github.com/python/cpython"
|
||||
|
||||
# Download tgz of source
|
||||
mkdir -p /tmp/python-src "${PYTHON_INSTALL_PATH}"
|
||||
cd /tmp/python-src
|
||||
local tgz_filename="Python-${PYTHON_VERSION}.tgz"
|
||||
local tgz_url="https://www.python.org/ftp/python/${PYTHON_VERSION}/${tgz_filename}"
|
||||
echo "Downloading ${tgz_filename}..."
|
||||
curl -sSL -o "/tmp/python-src/${tgz_filename}" "${tgz_url}"
|
||||
|
||||
# Verify signature
|
||||
receive_gpg_keys PYTHON_SOURCE_GPG_KEYS
|
||||
echo "Downloading ${tgz_filename}.asc..."
|
||||
curl -sSL -o "/tmp/python-src/${tgz_filename}.asc" "${tgz_url}.asc"
|
||||
gpg --verify "${tgz_filename}.asc"
|
||||
|
||||
# Update min protocol for testing only - https://bugs.python.org/issue41561
|
||||
cp /etc/ssl/openssl.cnf /tmp/python-src/
|
||||
sed -i -E 's/MinProtocol[=\ ]+.*/MinProtocol = TLSv1.0/g' /tmp/python-src/openssl.cnf
|
||||
export OPENSSL_CONF=/tmp/python-src/openssl.cnf
|
||||
|
||||
# Untar and build
|
||||
tar -xzf "/tmp/python-src/${tgz_filename}" -C "/tmp/python-src" --strip-components=1
|
||||
local config_args=""
|
||||
if [ "${OPTIMIZE_BUILD_FROM_SOURCE}" = "true" ]; then
|
||||
config_args="--enable-optimizations"
|
||||
fi
|
||||
./configure --prefix="${PYTHON_INSTALL_PATH}" --with-ensurepip=install ${config_args}
|
||||
make -j 8
|
||||
make install
|
||||
cd /tmp
|
||||
rm -rf /tmp/python-src ${GNUPGHOME} /tmp/vscdc-settings.env
|
||||
chown -R ${USERNAME} "${PYTHON_INSTALL_PATH}"
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/python3 ${PYTHON_INSTALL_PATH}/bin/python
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/pip3 ${PYTHON_INSTALL_PATH}/bin/pip
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/idle3 ${PYTHON_INSTALL_PATH}/bin/idle
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/pydoc3 ${PYTHON_INSTALL_PATH}/bin/pydoc
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/python3-config ${PYTHON_INSTALL_PATH}/bin/python-config
|
||||
}
|
||||
|
||||
install_using_oryx() {
|
||||
if [ -d "${PYTHON_INSTALL_PATH}" ]; then
|
||||
echo "(!) Path ${PYTHON_INSTALL_PATH} already exists. Remove this existing path or select a different one."
|
||||
exit 1
|
||||
fi
|
||||
oryx_install "python" "${PYTHON_VERSION}" "${PYTHON_INSTALL_PATH}" "lib" || return 1
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/idle3 ${PYTHON_INSTALL_PATH}/bin/idle
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/pydoc3 ${PYTHON_INSTALL_PATH}/bin/pydoc
|
||||
ln -s ${PYTHON_INSTALL_PATH}/bin/python3-config ${PYTHON_INSTALL_PATH}/bin/python-config
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# General requirements
|
||||
check_packages curl ca-certificates gnupg2 tar make gcc libssl-dev zlib1g-dev libncurses5-dev \
|
||||
libbz2-dev libreadline-dev libxml2-dev xz-utils libgdbm-dev tk-dev dirmngr \
|
||||
libxmlsec1-dev libsqlite3-dev libffi-dev liblzma-dev uuid-dev
|
||||
|
||||
|
||||
# Install python from source if needed
|
||||
if [ "${PYTHON_VERSION}" != "none" ]; then
|
||||
# If the os-provided versions are "good enough", detect that and bail out.
|
||||
if [ ${PYTHON_VERSION} = "os-provided" ] || [ ${PYTHON_VERSION} = "system" ]; then
|
||||
check_packages python3 python3-doc python3-pip python3-venv python3-dev python3-tk
|
||||
PYTHON_INSTALL_PATH="/usr"
|
||||
should_install_from_source=false
|
||||
elif [ "$(dpkg --print-architecture)" = "amd64" ] && [ "${USE_ORYX_IF_AVAILABLE}" = "true" ] && type oryx > /dev/null 2>&1; then
|
||||
install_using_oryx || should_install_from_source=true
|
||||
else
|
||||
should_install_from_source=true
|
||||
fi
|
||||
if [ "${should_install_from_source}" = "true" ]; then
|
||||
install_from_source
|
||||
fi
|
||||
updaterc "if [[ \"\${PATH}\" != *\"${PYTHON_INSTALL_PATH}/bin\"* ]]; then export PATH=${PYTHON_INSTALL_PATH}/bin:\${PATH}; fi"
|
||||
fi
|
||||
|
||||
# If not installing python tools, exit
|
||||
if [ "${INSTALL_PYTHON_TOOLS}" != "true" ]; then
|
||||
echo "Done!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
export PIPX_BIN_DIR="${PIPX_HOME}/bin"
|
||||
export PATH="${PYTHON_INSTALL_PATH}/bin:${PIPX_BIN_DIR}:${PATH}"
|
||||
|
||||
# Create pipx group, dir, and set sticky bit
|
||||
if ! cat /etc/group | grep -e "^pipx:" > /dev/null 2>&1; then
|
||||
groupadd -r pipx
|
||||
fi
|
||||
usermod -a -G pipx ${USERNAME}
|
||||
umask 0002
|
||||
mkdir -p ${PIPX_BIN_DIR}
|
||||
chown :pipx ${PIPX_HOME} ${PIPX_BIN_DIR}
|
||||
chmod g+s ${PIPX_HOME} ${PIPX_BIN_DIR}
|
||||
|
||||
# Update pip if not using os provided python
|
||||
if [ ${PYTHON_VERSION} != "os-provided" ] && [ ${PYTHON_VERSION} != "system" ]; then
|
||||
echo "Updating pip..."
|
||||
${PYTHON_INSTALL_PATH}/bin/python3 -m pip install --no-cache-dir --upgrade pip
|
||||
fi
|
||||
|
||||
# Install tools
|
||||
echo "Installing Python tools..."
|
||||
export PYTHONUSERBASE=/tmp/pip-tmp
|
||||
export PIP_CACHE_DIR=/tmp/pip-tmp/cache
|
||||
pipx_path=""
|
||||
if ! type pipx > /dev/null 2>&1; then
|
||||
pip3 install --disable-pip-version-check --no-cache-dir --user pipx 2>&1
|
||||
/tmp/pip-tmp/bin/pipx install --pip-args=--no-cache-dir pipx
|
||||
pipx_path="/tmp/pip-tmp/bin/"
|
||||
fi
|
||||
for util in ${DEFAULT_UTILS[@]}; do
|
||||
if ! type ${util} > /dev/null 2>&1; then
|
||||
${pipx_path}pipx install --system-site-packages --pip-args '--no-cache-dir --force-reinstall' ${util}
|
||||
else
|
||||
echo "${util} already installed. Skipping."
|
||||
fi
|
||||
done
|
||||
rm -rf /tmp/pip-tmp
|
||||
|
||||
echo "(*) Updating RC files."
|
||||
|
||||
updaterc "$(cat << EOF
|
||||
export PIPX_HOME="${PIPX_HOME}"
|
||||
export PIPX_BIN_DIR="${PIPX_BIN_DIR}"
|
||||
if [[ "\${PATH}" != *"\${PIPX_BIN_DIR}"* ]]; then export PATH="\${PATH}:\${PIPX_BIN_DIR}"; fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
echo "(*) About to do final Python setup."
|
||||
|
||||
INSTALLED_PYTHON_VERSION=$(${PYTHON_INSTALL_PATH}/bin/python --version 2>&1 | grep -Po '(?<=Python )(.+)')
|
||||
if [[ ! -z "$INSTALLED_PYTHON_VERSION" ]] ; then
|
||||
echo "(*) Installed Python version is ${INSTALLED_PYTHON_VERSION}"
|
||||
PYTHON_MAJOR_VERSION=$(echo ${INSTALLED_PYTHON_VERSION} | cut -f1 -d'.')
|
||||
PYTHON_MINOR_VERSION=$(echo ${INSTALLED_PYTHON_VERSION} | cut -f2 -d'.')
|
||||
|
||||
if [ "${PYTHON_SETUP_LINKS}" = "true" ]; then
|
||||
mkdir -p /opt/python
|
||||
ln -s /usr/share/python /opt/python/${INSTALLED_PYTHON_VERSION}
|
||||
ln -s /opt/python/${INSTALLED_PYTHON_VERSION} /opt/python/${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION}
|
||||
ln -s /opt/python/${PYTHON_MAJOR_VERSION}.${PYTHON_MINOR_VERSION} /opt/python/${PYTHON_MAJOR_VERSION}
|
||||
ln -s /opt/python/${INSTALLED_PYTHON_VERSION} /opt/python/latest
|
||||
ln -s /opt/python/${INSTALLED_PYTHON_VERSION} /opt/python/stable
|
||||
|
||||
mkdir -p /home/${USERNAME}/.python
|
||||
ln -s /opt/python/${INSTALLED_PYTHON_VERSION} /home/${USERNAME}/.python/current
|
||||
fi
|
||||
else
|
||||
echo "(*) Failed to get installed Python version."
|
||||
fi
|
||||
|
||||
echo "(*) Done with Python feature."
|
25
collection/ruby/feature.json
Normal file
25
collection/ruby/feature.json
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"id": "ruby",
|
||||
"name": "Ruby (via rvm)",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "3.1", "3.0", "2.7"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a Ruby version to install"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"rebornix.Ruby"
|
||||
],
|
||||
"containerEnv": {
|
||||
"GEM_PATH": "/usr/local/rvm/gems/default:/usr/local/rvm/gems/default@global",
|
||||
"GEM_HOME": "/usr/local/rvm/gems/default",
|
||||
"MY_RUBY_HOME": "/usr/local/rvm/rubies/default",
|
||||
"PATH": "/usr/local/rvm/gems/default/bin:/usr/local/rvm/gems/default@global/bin:/usr/local/rvm/rubies/default/bin:${PATH}"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
271
collection/ruby/install.sh
Normal file
271
collection/ruby/install.sh
Normal file
|
@ -0,0 +1,271 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/ruby.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./ruby-debian.sh [Ruby version] [non-root user] [Add to rc files flag] [Install tools flag]
|
||||
|
||||
RUBY_VERSION=${1:-"latest"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
UPDATE_RC=${3:-"true"}
|
||||
INSTALL_RUBY_TOOLS=${6:-"true"}
|
||||
|
||||
# Note: ruby-debug-ide will install the right version of debase if missing and
|
||||
# installing debase directly fails on Ruby 3.1.0 as of 1/7/2022, so omitting.
|
||||
DEFAULT_GEMS="rake ruby-debug-ide"
|
||||
|
||||
RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
keyring_args="--no-default-keyring --keyring \"$2\""
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
architecture="$(uname -m)"
|
||||
if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then
|
||||
echo "(!) Architecture $architecture unsupported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
check_packages curl ca-certificates software-properties-common build-essential gnupg2 libreadline-dev \
|
||||
procps dirmngr gawk autoconf automake bison libffi-dev libgdbm-dev libncurses5-dev \
|
||||
libsqlite3-dev libtool libyaml-dev pkg-config sqlite3 zlib1g-dev libgmp-dev libssl-dev
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
|
||||
# Figure out correct version of a three part version number is not passed
|
||||
find_version_from_git_tags RUBY_VERSION "https://github.com/ruby/ruby" "tags/v" "_"
|
||||
|
||||
# Just install Ruby if RVM already installed
|
||||
if [ -d "/usr/local/rvm" ]; then
|
||||
echo "Ruby Version Manager already exists."
|
||||
if [ "${RUBY_VERSION}" != "none" ]; then
|
||||
echo "Installing specified Ruby version."
|
||||
su ${USERNAME} -c "&& rvm install ruby ${RUBY_VERSION}"
|
||||
fi
|
||||
SKIP_GEM_INSTALL="false"
|
||||
else
|
||||
# Install RVM
|
||||
receive_gpg_keys RVM_GPG_KEYS
|
||||
# Determine appropriate settings for rvm installer
|
||||
if [ "${RUBY_VERSION}" = "none" ]; then
|
||||
RVM_INSTALL_ARGS=""
|
||||
else
|
||||
if [ "${RUBY_VERSION}" = "latest" ] || [ "${RUBY_VERSION}" = "current" ] || [ "${RUBY_VERSION}" = "lts" ]; then
|
||||
RVM_INSTALL_ARGS="--ruby"
|
||||
RUBY_VERSION=""
|
||||
else
|
||||
RVM_INSTALL_ARGS="--ruby=${RUBY_VERSION}"
|
||||
fi
|
||||
if [ "${INSTALL_RUBY_TOOLS}" = "true" ]; then
|
||||
SKIP_GEM_INSTALL="true"
|
||||
else
|
||||
DEFAULT_GEMS=""
|
||||
fi
|
||||
fi
|
||||
# Create rvm group as a system group to reduce the odds of conflict with local user UIDs
|
||||
if ! cat /etc/group | grep -e "^rvm:" > /dev/null 2>&1; then
|
||||
groupadd -r rvm
|
||||
fi
|
||||
# Install rvm
|
||||
curl -sSL https://get.rvm.io | bash -s stable --ignore-dotfiles ${RVM_INSTALL_ARGS} --with-default-gems="${DEFAULT_GEMS}" 2>&1
|
||||
usermod -aG rvm ${USERNAME}
|
||||
su ${USERNAME} -c ". /usr/local/rvm/scripts/rvm && rvm fix-permissions system"
|
||||
rm -rf ${GNUPGHOME}
|
||||
fi
|
||||
|
||||
if [ "${INSTALL_RUBY_TOOLS}" = "true" ]; then
|
||||
# Non-root user may not have "gem" in path when script is run and no ruby version
|
||||
# is installed by rvm, so handle this by using root's default gem in this case
|
||||
ROOT_GEM='$(which gem || echo "")'
|
||||
su ${USERNAME} -c ". /usr/local/rvm/scripts/rvm && \"$(which gem || echo ${ROOT_GEM})\" install ${DEFAULT_GEMS}"
|
||||
fi
|
||||
|
||||
# VS Code server usually first in the path, so silence annoying rvm warning (that does not apply) and then source it
|
||||
updaterc "if ! grep rvm_silence_path_mismatch_check_flag \$HOME/.rvmrc > /dev/null 2>&1; then echo 'rvm_silence_path_mismatch_check_flag=1' >> \$HOME/.rvmrc; fi\nsource /usr/local/rvm/scripts/rvm > /dev/null 2>&1"
|
||||
|
||||
# Install rbenv/ruby-build for good measure
|
||||
git clone --depth=1 \
|
||||
-c core.eol=lf \
|
||||
-c core.autocrlf=false \
|
||||
-c fsck.zeroPaddedFilemode=ignore \
|
||||
-c fetch.fsck.zeroPaddedFilemode=ignore \
|
||||
-c receive.fsck.zeroPaddedFilemode=ignore \
|
||||
https://github.com/rbenv/rbenv.git /usr/local/share/rbenv
|
||||
ln -s /usr/local/share/rbenv/bin/rbenv /usr/local/bin
|
||||
updaterc 'eval "$(rbenv init -)"'
|
||||
git clone --depth=1 \
|
||||
-c core.eol=lf \
|
||||
-c core.autocrlf=false \
|
||||
-c fsck.zeroPaddedFilemode=ignore \
|
||||
-c fetch.fsck.zeroPaddedFilemode=ignore \
|
||||
-c receive.fsck.zeroPaddedFilemode=ignore \
|
||||
https://github.com/rbenv/ruby-build.git /usr/local/share/ruby-build
|
||||
mkdir -p /root/.rbenv/plugins
|
||||
ln -s /usr/local/share/ruby-build /root/.rbenv/plugins/ruby-build
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
mkdir -p /home/${USERNAME}/.rbenv/plugins
|
||||
chown -R ${USERNAME} /home/${USERNAME}/.rbenv
|
||||
ln -s /usr/local/share/ruby-build /home/${USERNAME}/.rbenv/plugins/ruby-build
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
source /usr/local/rvm/scripts/rvm
|
||||
rvm cleanup all
|
||||
gem cleanup
|
||||
echo "Done!"
|
43
collection/rust/feature.json
Normal file
43
collection/rust/feature.json
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"id": "rust",
|
||||
"name": "Rust",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "1.55", "1.54", "1.53"],
|
||||
"default": "latest",
|
||||
"description": "Select or enter a version of Rust to install."
|
||||
},
|
||||
"profile": {
|
||||
"type": "string",
|
||||
"proposals": ["minimal", "default", "complete"],
|
||||
"default": "minimal",
|
||||
"description": "Select a rustup install profile."
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"vadimcn.vscode-lldb",
|
||||
"mutantdino.resourcemonitor",
|
||||
"matklad.rust-analyzer",
|
||||
"tamasfe.even-better-toml",
|
||||
"serayuzgur.crates"
|
||||
],
|
||||
"containerEnv": {
|
||||
"CARGO_HOME": "/usr/local/cargo",
|
||||
"RUSTUP_HOME": "/usr/local/rustup",
|
||||
"PATH": "${CARGO_HOME}/bin:${PATH}"
|
||||
},
|
||||
"capAdd": [ "SYS_PTRACE" ],
|
||||
"securityOpt": ["seccomp=unconfined"],
|
||||
"settings": {
|
||||
"lldb.executable": "/usr/bin/lldb",
|
||||
"files.watcherExclude": {
|
||||
"**/target/**": true
|
||||
},
|
||||
"rust-analyzer.checkOnSave.command": "clippy"
|
||||
},
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
199
collection/rust/install.sh
Normal file
199
collection/rust/install.sh
Normal file
|
@ -0,0 +1,199 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/rust.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./rust-debian.sh [CARGO_HOME] [RUSTUP_HOME] [non-root user] [add CARGO/RUSTUP_HOME to rc files flag] [whether to update rust] [Rust version] [rustup install profile]
|
||||
|
||||
export CARGO_HOME=${1:-"/usr/local/cargo"}
|
||||
export RUSTUP_HOME=${2:-"/usr/local/rustup"}
|
||||
USERNAME=${3:-"automatic"}
|
||||
UPDATE_RC=${4:-"true"}
|
||||
UPDATE_RUST=${5:-"false"}
|
||||
RUST_VERSION=${6:-"latest"}
|
||||
RUSTUP_PROFILE=${7:-"minimal"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e '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
|
||||
|
||||
# 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
|
||||
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}"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install curl, lldb, python3-minimal,libpython and rust dependencies if missing
|
||||
if ! dpkg -s curl ca-certificates gnupg2 lldb python3-minimal gcc libc6-dev > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends curl ca-certificates gcc libc6-dev
|
||||
apt-get -y install lldb python3-minimal libpython3.?
|
||||
fi
|
||||
|
||||
architecture="$(dpkg --print-architecture)"
|
||||
download_architecture="${architecture}"
|
||||
case ${download_architecture} in
|
||||
amd64)
|
||||
download_architecture="x86_64"
|
||||
;;
|
||||
arm64)
|
||||
download_architecture="aarch64"
|
||||
;;
|
||||
*) echo "(!) Architecture ${architecture} not supported."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install Rust
|
||||
umask 0002
|
||||
if ! cat /etc/group | grep -e "^rustlang:" > /dev/null 2>&1; then
|
||||
groupadd -r rustlang
|
||||
fi
|
||||
usermod -a -G rustlang "${USERNAME}"
|
||||
mkdir -p "${CARGO_HOME}" "${RUSTUP_HOME}"
|
||||
chown :rustlang "${RUSTUP_HOME}" "${CARGO_HOME}"
|
||||
chmod g+r+w+s "${RUSTUP_HOME}" "${CARGO_HOME}"
|
||||
|
||||
if [ "${RUST_VERSION}" = "none" ] || type rustup > /dev/null 2>&1; then
|
||||
echo "Rust already installed. Skipping..."
|
||||
else
|
||||
if [ "${RUST_VERSION}" != "latest" ] && [ "${RUST_VERSION}" != "lts" ] && [ "${RUST_VERSION}" != "stable" ]; then
|
||||
# Find version using soft match
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
find_version_from_git_tags RUST_VERSION "https://github.com/rust-lang/rust" "tags/"
|
||||
default_toolchain_arg="--default-toolchain ${RUST_VERSION}"
|
||||
fi
|
||||
echo "Installing Rust..."
|
||||
# Download and verify rustup sha
|
||||
mkdir -p /tmp/rustup/target/${download_architecture}-unknown-linux-gnu/release/
|
||||
curl -sSL --proto '=https' --tlsv1.2 "https://static.rust-lang.org/rustup/dist/${download_architecture}-unknown-linux-gnu/rustup-init" -o /tmp/rustup/target/${download_architecture}-unknown-linux-gnu/release/rustup-init
|
||||
curl -sSL --proto '=https' --tlsv1.2 "https://static.rust-lang.org/rustup/dist/${download_architecture}-unknown-linux-gnu/rustup-init.sha256" -o /tmp/rustup/rustup-init.sha256
|
||||
cd /tmp/rustup
|
||||
sha256sum -c rustup-init.sha256
|
||||
chmod +x target/${download_architecture}-unknown-linux-gnu/release/rustup-init
|
||||
target/${download_architecture}-unknown-linux-gnu/release/rustup-init -y --no-modify-path --profile ${RUSTUP_PROFILE} ${default_toolchain_arg}
|
||||
cd ~
|
||||
rm -rf /tmp/rustup
|
||||
fi
|
||||
|
||||
export PATH=${CARGO_HOME}/bin:${PATH}
|
||||
if [ "${UPDATE_RUST}" = "true" ]; then
|
||||
echo "Updating Rust..."
|
||||
rustup update 2>&1
|
||||
fi
|
||||
echo "Installing common Rust dependencies..."
|
||||
rustup component add rls rust-analysis rust-src rustfmt clippy 2>&1
|
||||
|
||||
# Add CARGO_HOME, RUSTUP_HOME and bin directory into bashrc/zshrc files (unless disabled)
|
||||
updaterc "$(cat << EOF
|
||||
export RUSTUP_HOME="${RUSTUP_HOME}"
|
||||
export CARGO_HOME="${CARGO_HOME}"
|
||||
if [[ "\${PATH}" != *"\${CARGO_HOME}/bin"* ]]; then export PATH="\${CARGO_HOME}/bin:\${PATH}"; fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Make files writable for rustlang group
|
||||
chmod -R g+r+w "${RUSTUP_HOME}" "${CARGO_HOME}"
|
||||
|
||||
echo "Done!"
|
||||
|
17
collection/sshd/feature.json
Normal file
17
collection/sshd/feature.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "sshd",
|
||||
"name": "SSH server",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"enum": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Currently unused."
|
||||
}
|
||||
},
|
||||
"entrypoint": "/usr/local/share/ssh-init.sh",
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
169
collection/sshd/install.sh
Normal file
169
collection/sshd/install.sh
Normal file
|
@ -0,0 +1,169 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/sshd.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./sshd-debian.sh [SSH Port (don't use 22)] [non-root user] [start sshd now flag] [new password for user] [fix environment flag]
|
||||
#
|
||||
# Note: You can change your user's password with "sudo passwd $(whoami)" (or just "passwd" if running as root).
|
||||
|
||||
SSHD_PORT=${1:-"2222"}
|
||||
USERNAME=${2:-"automatic"}
|
||||
START_SSHD=${3:-"false"}
|
||||
NEW_PASSWORD=${4:-"skip"}
|
||||
FIX_ENVIRONMENT=${5:-"true"}
|
||||
|
||||
set -e
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 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
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install openssh-server openssh-client
|
||||
check_packages openssh-server openssh-client lsof
|
||||
|
||||
# Generate password if new password set to the word "random"
|
||||
if [ "${NEW_PASSWORD}" = "random" ]; then
|
||||
NEW_PASSWORD="$(openssl rand -hex 16)"
|
||||
EMIT_PASSWORD="true"
|
||||
elif [ "${NEW_PASSWORD}" != "skip" ]; then
|
||||
# If new password not set to skip, set it for the specified user
|
||||
echo "${USERNAME}:${NEW_PASSWORD}" | chpasswd
|
||||
fi
|
||||
|
||||
# Add user to ssh group
|
||||
if [ "${USERNAME}" != "root" ]; then
|
||||
usermod -aG ssh ${USERNAME}
|
||||
fi
|
||||
|
||||
# Setup sshd
|
||||
mkdir -p /var/run/sshd
|
||||
sed -i 's/session\s*required\s*pam_loginuid\.so/session optional pam_loginuid.so/g' /etc/pam.d/sshd
|
||||
sed -i 's/#*PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config
|
||||
sed -i -E "s/#*\s*Port\s+.+/Port ${SSHD_PORT}/g" /etc/ssh/sshd_config
|
||||
# Need to UsePAM so /etc/environment is processed
|
||||
sed -i -E "s/#?\s*UsePAM\s+.+/UsePAM yes/g" /etc/ssh/sshd_config
|
||||
|
||||
# Script to store variables that exist at the time the ENTRYPOINT is fired
|
||||
store_env_script="$(cat << 'EOF'
|
||||
# Wire in codespaces secret processing to zsh if present (since may have been added to image after script was run)
|
||||
if [ -f /etc/zsh/zlogin ] && ! grep '/etc/profile.d/00-restore-secrets.sh' /etc/zsh/zlogin > /dev/null 2>&1; then
|
||||
echo -e "if [ -f /etc/profile.d/00-restore-secrets.sh ]; then . /etc/profile.d/00-restore-secrets.sh; fi\n$(cat /etc/zsh/zlogin 2>/dev/null || echo '')" | sudoIf tee /etc/zsh/zlogin > /dev/null
|
||||
fi
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Script to ensure login shells get the latest Codespaces secrets
|
||||
restore_secrets_script="$(cat << 'EOF'
|
||||
#!/bin/sh
|
||||
if [ "${CODESPACES}" != "true" ] || [ "${VSCDC_FIXED_SECRETS}" = "true" ] || [ ! -z "${GITHUB_CODESPACES_TOKEN}" ]; then
|
||||
# Not codespaces, already run, or secrets already in environment, so return
|
||||
return
|
||||
fi
|
||||
if [ -f /workspaces/.codespaces/shared/.env-secrets ]; then
|
||||
while read line
|
||||
do
|
||||
key=$(echo $line | sed "s/=.*//")
|
||||
value=$(echo $line | sed "s/$key=//1")
|
||||
decodedValue=$(echo $value | base64 -d)
|
||||
export $key="$decodedValue"
|
||||
done < /workspaces/.codespaces/shared/.env-secrets
|
||||
fi
|
||||
export VSCDC_FIXED_SECRETS=true
|
||||
EOF
|
||||
)"
|
||||
|
||||
# Write out a scripts that can be referenced as an ENTRYPOINT to auto-start sshd and fix login environments
|
||||
tee /usr/local/share/ssh-init.sh > /dev/null \
|
||||
<< 'EOF'
|
||||
#!/usr/bin/env bash
|
||||
# This script is intended to be run as root with a container that runs as root (even if you connect with a different user)
|
||||
# However, it supports running as a user other than root if passwordless sudo is configured for that same user.
|
||||
|
||||
set -e
|
||||
|
||||
sudoIf()
|
||||
{
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
sudo "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
EOF
|
||||
if [ "${FIX_ENVIRONMENT}" = "true" ]; then
|
||||
echo "${store_env_script}" >> /usr/local/share/ssh-init.sh
|
||||
echo "${restore_secrets_script}" > /etc/profile.d/00-restore-secrets.sh
|
||||
chmod +x /etc/profile.d/00-restore-secrets.sh
|
||||
# Wire in zsh if present
|
||||
if type zsh > /dev/null 2>&1; then
|
||||
echo -e "if [ -f /etc/profile.d/00-restore-secrets.sh ]; then . /etc/profile.d/00-restore-secrets.sh; fi\n$(cat /etc/zsh/zlogin 2>/dev/null || echo '')" > /etc/zsh/zlogin
|
||||
fi
|
||||
fi
|
||||
tee -a /usr/local/share/ssh-init.sh > /dev/null \
|
||||
<< 'EOF'
|
||||
|
||||
# ** Start SSH server **
|
||||
sudoIf /etc/init.d/ssh start 2>&1 | sudoIf tee /tmp/sshd.log > /dev/null
|
||||
|
||||
set +e
|
||||
exec "$@"
|
||||
EOF
|
||||
chmod +x /usr/local/share/ssh-init.sh
|
||||
|
||||
# If we should start sshd now, do so
|
||||
if [ "${START_SSHD}" = "true" ]; then
|
||||
/usr/local/share/ssh-init.sh
|
||||
fi
|
||||
|
||||
# Output success details
|
||||
echo -e "Done!\n\n- Port: ${SSHD_PORT}\n- User: ${USERNAME}"
|
||||
if [ "${EMIT_PASSWORD}" = "true" ]; then
|
||||
echo "- Password: ${NEW_PASSWORD}"
|
||||
fi
|
||||
echo -e "\nForward port ${SSHD_PORT} to your local machine and run:\n\n ssh -p ${SSHD_PORT} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o GlobalKnownHostsFile=/dev/null ${USERNAME}@localhost\n"
|
39
collection/terraform/feature.json
Normal file
39
collection/terraform/feature.json
Normal file
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"id": "terraform",
|
||||
"name": "Terraform, tflint, and TFGrunt",
|
||||
"options": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"proposals": ["latest", "1.1", "1.0", "0.15"],
|
||||
"default": "latest",
|
||||
"description": "Terraform version"
|
||||
},
|
||||
"tflint": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Tflint version"
|
||||
},
|
||||
"terragrunt": {
|
||||
"type": "string",
|
||||
"proposals": ["latest"],
|
||||
"default": "latest",
|
||||
"description": "Terragrunt version"
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
"HashiCorp.terraform",
|
||||
"ms-azuretools.vscode-azureterraform"
|
||||
],
|
||||
"settings": {
|
||||
"terraform.languageServer": {
|
||||
"enabled": true,
|
||||
"args": []
|
||||
},
|
||||
"azureTerraform.terminal": "integrated",
|
||||
"install": {
|
||||
"app": "",
|
||||
"file": "install.sh"
|
||||
}
|
||||
}
|
||||
}
|
218
collection/terraform/install.sh
Normal file
218
collection/terraform/install.sh
Normal file
|
@ -0,0 +1,218 @@
|
|||
#!/usr/bin/env 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/microsoft/vscode-dev-containers/blob/main/script-library/docs/terraform.md
|
||||
# Maintainer: The VS Code and Codespaces Teams
|
||||
#
|
||||
# Syntax: ./terraform-debian.sh [terraform version] [tflint version] [terragrunt version] [terraform SHA] [tflint SHA] [terragrunt SHA]
|
||||
|
||||
set -e
|
||||
|
||||
TERRAFORM_VERSION="${1:-"latest"}"
|
||||
TFLINT_VERSION="${2:-"latest"}"
|
||||
TERRAGRUNT_VERSION="${3:-"latest"}"
|
||||
TERRAFORM_SHA256="${4:-"automatic"}"
|
||||
TFLINT_SHA256="${5:-"automatic"}"
|
||||
TERRAGRUNT_SHA256="${6:-"automatic"}"
|
||||
|
||||
TERRAFORM_GPG_KEY="72D7468F"
|
||||
TFLINT_GPG_KEY_URI="https://raw.githubusercontent.com/terraform-linters/tflint/master/8CE69160EB3F2FE9.key"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
|
||||
architecture="$(uname -m)"
|
||||
case ${architecture} in
|
||||
x86_64) architecture="amd64";;
|
||||
aarch64 | armv8*) architecture="arm64";;
|
||||
aarch32 | armv7* | armvhf*) architecture="arm";;
|
||||
i?86) architecture="386";;
|
||||
*) echo "(!) Architecture ${architecture} unsupported"; exit 1 ;;
|
||||
esac
|
||||
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
keyring_args="--no-default-keyring --keyring $2"
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Ensure apt is in non-interactive to avoid prompts
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies if missing
|
||||
check_packages curl ca-certificates gnupg2 dirmngr coreutils unzip
|
||||
if ! type git > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends git
|
||||
fi
|
||||
|
||||
# Verify requested version is available, convert latest
|
||||
find_version_from_git_tags TERRAFORM_VERSION 'https://github.com/hashicorp/terraform'
|
||||
find_version_from_git_tags TFLINT_VERSION 'https://github.com/terraform-linters/tflint'
|
||||
find_version_from_git_tags TERRAGRUNT_VERSION 'https://github.com/gruntwork-io/terragrunt'
|
||||
|
||||
mkdir -p /tmp/tf-downloads
|
||||
cd /tmp/tf-downloads
|
||||
|
||||
# Install Terraform, tflint, Terragrunt
|
||||
echo "Downloading terraform..."
|
||||
terraform_filename="terraform_${TERRAFORM_VERSION}_linux_${architecture}.zip"
|
||||
curl -sSL -o ${terraform_filename} "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/${terraform_filename}"
|
||||
if [ "${TERRAFORM_SHA256}" != "dev-mode" ]; then
|
||||
if [ "${TERRAFORM_SHA256}" = "automatic" ]; then
|
||||
receive_gpg_keys TERRAFORM_GPG_KEY
|
||||
curl -sSL -o terraform_SHA256SUMS https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS
|
||||
curl -sSL -o terraform_SHA256SUMS.sig https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_SHA256SUMS.${TERRAFORM_GPG_KEY}.sig
|
||||
gpg --verify terraform_SHA256SUMS.sig terraform_SHA256SUMS
|
||||
else
|
||||
echo "${TERRAFORM_SHA256} *${terraform_filename}" > terraform_SHA256SUMS
|
||||
fi
|
||||
sha256sum --ignore-missing -c terraform_SHA256SUMS
|
||||
fi
|
||||
unzip ${terraform_filename}
|
||||
mv -f terraform /usr/local/bin/
|
||||
|
||||
if [ "${TFLINT_VERSION}" != "none" ]; then
|
||||
echo "Downloading tflint..."
|
||||
TFLINT_FILENAME="tflint_linux_${architecture}.zip"
|
||||
curl -sSL -o /tmp/tf-downloads/${TFLINT_FILENAME} https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/${TFLINT_FILENAME}
|
||||
if [ "${TFLINT_SHA256}" != "dev-mode" ]; then
|
||||
if [ "${TFLINT_SHA256}" = "automatic" ]; then
|
||||
get_common_setting TFLINT_GPG_KEY_URI
|
||||
curl -sSL -o tflint_key "${TFLINT_GPG_KEY_URI}"
|
||||
gpg -q --import tflint_key
|
||||
curl -sSL -o tflint_checksums.txt https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt
|
||||
curl -sSL -o tflint_checksums.txt.sig https://github.com/terraform-linters/tflint/releases/download/v${TFLINT_VERSION}/checksums.txt.sig
|
||||
gpg --verify tflint_checksums.txt.sig tflint_checksums.txt
|
||||
else
|
||||
echo "${TFLINT_SHA256} *${TFLINT_FILENAME}" > tflint_checksums.txt
|
||||
fi
|
||||
sha256sum --ignore-missing -c tflint_checksums.txt
|
||||
fi
|
||||
unzip /tmp/tf-downloads/${TFLINT_FILENAME}
|
||||
mv -f tflint /usr/local/bin/
|
||||
fi
|
||||
if [ "${TERRAGRUNT_VERSION}" != "none" ]; then
|
||||
echo "Downloading Terragrunt..."
|
||||
terragrunt_filename="terragrunt_linux_${architecture}"
|
||||
curl -sSL -o /tmp/tf-downloads/${terragrunt_filename} https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/${terragrunt_filename}
|
||||
if [ "${TERRAGRUNT_SHA256}" != "dev-mode" ]; then
|
||||
if [ "${TERRAGRUNT_SHA256}" = "automatic" ]; then
|
||||
curl -sSL -o terragrunt_SHA256SUMS https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/SHA256SUMS
|
||||
else
|
||||
echo "${TERRAGRUNT_SHA256} *${terragrunt_filename}" > terragrunt_SHA256SUMS
|
||||
fi
|
||||
sha256sum --ignore-missing -c terragrunt_SHA256SUMS
|
||||
fi
|
||||
chmod a+x /tmp/tf-downloads/${terragrunt_filename}
|
||||
mv -f /tmp/tf-downloads/${terragrunt_filename} /usr/local/bin/terragrunt
|
||||
fi
|
||||
|
||||
rm -rf /tmp/tf-downloads ${GNUPGHOME}
|
||||
echo "Done!"
|
244
lib/utils.sh
Normal file
244
lib/utils.sh
Normal file
|
@ -0,0 +1,244 @@
|
|||
# If in automatic mode, determine if a user already exists, if not use root
|
||||
detect_user() {
|
||||
local user_variable_name=${1:-username}
|
||||
local possible_users=${2:-("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)")}
|
||||
if [ "${!user_variable_name}" = "auto" ] || [ "${!user_variable_name}" = "automatic" ]; then
|
||||
declare -g ${user_variable_name}=""
|
||||
for current_user in ${possible_users[@]}; do
|
||||
if id -u "${current_user}" > /dev/null 2>&1; then
|
||||
declare -g ${user_variable_name}="${current_user}"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [ "${!user_variable_name}" = "" ] || [ "${!user_variable_name}" = "none" ] || ! id -u "${!user_variable_name}" > /dev/null 2>&1; then
|
||||
declare -g ${user_variable_name}=root
|
||||
fi
|
||||
}
|
||||
|
||||
# Use Oryx to install something using a partial version match
|
||||
oryx_install() {
|
||||
local platform=$1
|
||||
local requested_version=$2
|
||||
local target_folder=${3:-none}
|
||||
local ldconfig_folder=${4:-none}
|
||||
echo "(*) Installing ${platform} ${requested_version} using Oryx..."
|
||||
check_packages jq
|
||||
# Soft match if full version not specified
|
||||
if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then
|
||||
local version_list="$(oryx platforms --json | jq -r ".[] | select(.Name == \"${platform}\") | .Versions | sort | reverse | @tsv" | tr '\t' '\n' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$')"
|
||||
if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then
|
||||
requested_version="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then
|
||||
echo -e "(!) Oryx does not support ${platform} version $2\nValid values:\n${version_list}" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "(*) Using ${requested_version} in place of $2."
|
||||
fi
|
||||
|
||||
export ORYX_ENV_TYPE=vsonline-present ORYX_PREFER_USER_INSTALLED_SDKS=true ENABLE_DYNAMIC_INSTALL=true DYNAMIC_INSTALL_ROOT_DIR=/opt
|
||||
oryx prep --skip-detection --platforms-and-versions "${platform}=${requested_version}"
|
||||
local opt_folder="/opt/${platform}/${requested_version}"
|
||||
if [ "${target_folder}" != "none" ] && [ "${target_folder}" != "${opt_folder}" ]; then
|
||||
ln -s "${opt_folder}" "${target_folder}"
|
||||
fi
|
||||
# Update library path add to conf
|
||||
if [ "${ldconfig_folder}" != "none" ]; then
|
||||
echo "/opt/${platform}/${requested_version}/lib" >> "/etc/ld.so.conf.d/${platform}.conf"
|
||||
ldconfig
|
||||
fi
|
||||
}
|
||||
|
||||
# Use SDKMAN to install something using a partial version match
|
||||
sdk_install() {
|
||||
local install_type=$1
|
||||
local requested_version=$2
|
||||
local prefix=$3
|
||||
local suffix="${4:-"\\s*"}"
|
||||
local full_version_check=${5:-".*-[a-z]+"}
|
||||
if [ "${requested_version}" = "none" ]; then return; fi
|
||||
# Blank will install latest stable version
|
||||
if [ "${requested_version}" = "lts" ] || [ "${requested_version}" = "default" ]; then
|
||||
requested_version=""
|
||||
elif echo "${requested_version}" | grep -oE "${full_version_check}" > /dev/null 2>&1; then
|
||||
echo "${requested_version}"
|
||||
else
|
||||
local regex="${prefix}\\K[0-9]+\\.[0-9]+\\.[0-9]+${suffix}"
|
||||
local version_list="$(. ${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
|
||||
requested_version="$(echo "${version_list}" | head -n 1)"
|
||||
else
|
||||
set +e
|
||||
requested_version="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")"
|
||||
set -e
|
||||
fi
|
||||
if [ -z "${requested_version}" ] || ! echo "${version_list}" | grep "^${requested_version//./\\.}$" > /dev/null 2>&1; then
|
||||
echo -e "Version $2 not found. Available versions:\n${version_list}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
su ${USERNAME} -c "umask 0002 && . ${SDKMAN_DIR}/bin/sdkman-init.sh && sdk install ${install_type} ${requested_version} && sdk flush archives && sdk flush temp"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
# 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}"
|
||||
}
|
||||
|
||||
# Import the specified key in a variable name passed in as
|
||||
receive_gpg_keys() {
|
||||
get_common_setting $1
|
||||
local keys=${!1}
|
||||
get_common_setting GPG_KEY_SERVERS true
|
||||
local keyring_args=""
|
||||
if [ ! -z "$2" ]; then
|
||||
keyring_args="--no-default-keyring --keyring $2"
|
||||
fi
|
||||
|
||||
# Use a temporary locaiton for gpg keys to avoid polluting image
|
||||
export GNUPGHOME="/tmp/tmp-gnupg"
|
||||
mkdir -p ${GNUPGHOME}
|
||||
chmod 700 ${GNUPGHOME}
|
||||
echo -e "disable-ipv6\n${GPG_KEY_SERVERS}" > ${GNUPGHOME}/dirmngr.conf
|
||||
# GPG key download sometimes fails for some reason and retrying fixes it.
|
||||
local retry_count=0
|
||||
local gpg_ok="false"
|
||||
set +e
|
||||
until [ "${gpg_ok}" = "true" ] || [ "${retry_count}" -eq "5" ];
|
||||
do
|
||||
echo "(*) Downloading GPG key..."
|
||||
( echo "${keys}" | xargs -n 1 gpg -q ${keyring_args} --recv-keys) 2>&1 && gpg_ok="true"
|
||||
if [ "${gpg_ok}" != "true" ]; then
|
||||
echo "(*) Failed getting key, retring in 10s..."
|
||||
(( retry_count++ ))
|
||||
sleep 10s
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ "${gpg_ok}" = "false" ]; then
|
||||
echo "(!) Failed to get gpg key."
|
||||
exit 1
|
||||
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}"
|
||||
}
|
||||
|
||||
# Function to run apt-get if needed
|
||||
apt_get_update_if_needed()
|
||||
{
|
||||
if [ ! -d "/var/lib/apt/lists" ] || [ "$(ls /var/lib/apt/lists/ | wc -l)" = "0" ]; then
|
||||
echo "Running apt-get update..."
|
||||
apt-get update
|
||||
else
|
||||
echo "Skipping apt-get update."
|
||||
fi
|
||||
}
|
||||
|
||||
# Checks if packages are installed and installs them if not
|
||||
check_packages() {
|
||||
if ! dpkg -s "$@" > /dev/null 2>&1; then
|
||||
apt_get_update_if_needed
|
||||
apt-get -y install --no-install-recommends "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# 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_version_soft_match() {
|
||||
|
||||
# Version
|
||||
local variable_name="$1"
|
||||
local requested_version=${!variable_name}
|
||||
# Package Name
|
||||
local package_name="$2"
|
||||
# Exit on no match?
|
||||
local exit_on_no_match="${3:-true}"
|
||||
|
||||
# Ensure we've exported useful variables
|
||||
. /etc/os-release
|
||||
local architecture="$(dpkg --print-architecture)"
|
||||
|
||||
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 \"${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 ${variable_name}="=${fuzzy_version}"
|
||||
echo "${variable_name}=${!variable_name}"
|
||||
}
|
56
settings.env
Normal file
56
settings.env
Normal file
|
@ -0,0 +1,56 @@
|
|||
PYTHON_SOURCE_GPG_KEYS="64E628F8D684696D B26995E310250568 2D347EA6AA65421D FB9921286F5E1540 3A5CA953F73C700D 04C367C218ADD4FF 0EDDC5F26A45C816 6AF053F07D9DC8D2 C9BE28DEE6DF025C 126EB563A74B06BF D9866941EA5BBD71 ED9D77D5"
|
||||
DEADSNAKES_PPA_ARCHIVE_GPG_KEY="F23C5A6CF475977595C89F51BA6932366A755776"
|
||||
RVM_GPG_KEYS="409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB"
|
||||
TERRAFORM_GPG_KEYS=72D7468F
|
||||
GIT_CORE_PPA_ARCHIVE_GPG_KEY=E1DD270288B4E6030699E45FA1715D88E1DF1F24
|
||||
GITHUB_CLI_ARCHIVE_GPG_KEY=C99B11DEB97541F0
|
||||
GIT_LFS_ARCHIVE_GPG_KEY_URI="https://packagecloud.io/github/git-lfs/gpgkey"
|
||||
GIT_LFS_ARCHIVE_ARCHITECTURES="amd64"
|
||||
GIT_LFS_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal"
|
||||
GIT_LFS_CHECKSUM_GPG_KEYS="0x88ace9b29196305ba9947552f1ba225c0223b187 0x86cd3297749375bcf8206715f54fe648088335a9 0xaa3b3450295830d2de6db90caba67be5a5795889"
|
||||
POWERSHELL_ARCHIVE_ARCHITECTURES="amd64"
|
||||
POWERSHELL_ARCHIVE_VERSION_CODENAMES="stretch buster bionic focal"
|
||||
AZCLI_ARCHIVE_ARCHITECTURES="amd64"
|
||||
AZCLI_ARCHIVE_VERSION_CODENAMES="stretch buster bullseye bionic focal"
|
||||
DOTNET_ARCHIVE_ARCHITECTURES="amd64"
|
||||
DOTNET_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute"
|
||||
DOTNET_CDN_FEED_URI="https://dotnetcli.azureedge.net"
|
||||
HELM_GPG_KEYS_URI="https://raw.githubusercontent.com/helm/helm/main/KEYS"
|
||||
MICROSOFT_GPG_KEYS_URI="https://packages.microsoft.com/keys/microsoft.asc"
|
||||
TFLINT_GPG_KEY_URI="https://raw.githubusercontent.com/terraform-linters/tflint/master/8CE69160EB3F2FE9.key"
|
||||
GO_GPG_KEY_URI="https://dl.google.com/linux/linux_signing_key.pub"
|
||||
GPG_KEY_SERVERS="keyserver hkp://keyserver.ubuntu.com:80
|
||||
keyserver hkps://keys.openpgp.org
|
||||
keyserver hkp://keyserver.pgp.com"
|
||||
AWSCLI_GPG_KEY=FB5DB77FD5C118B80511ADA8A6310ACC4672475C
|
||||
AWSCLI_GPG_KEY_MATERIAL="-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBF2Cr7UBEADJZHcgusOJl7ENSyumXh85z0TRV0xJorM2B/JL0kHOyigQluUG
|
||||
ZMLhENaG0bYatdrKP+3H91lvK050pXwnO/R7fB/FSTouki4ciIx5OuLlnJZIxSzx
|
||||
PqGl0mkxImLNbGWoi6Lto0LYxqHN2iQtzlwTVmq9733zd3XfcXrZ3+LblHAgEt5G
|
||||
TfNxEKJ8soPLyWmwDH6HWCnjZ/aIQRBTIQ05uVeEoYxSh6wOai7ss/KveoSNBbYz
|
||||
gbdzoqI2Y8cgH2nbfgp3DSasaLZEdCSsIsK1u05CinE7k2qZ7KgKAUIcT/cR/grk
|
||||
C6VwsnDU0OUCideXcQ8WeHutqvgZH1JgKDbznoIzeQHJD238GEu+eKhRHcz8/jeG
|
||||
94zkcgJOz3KbZGYMiTh277Fvj9zzvZsbMBCedV1BTg3TqgvdX4bdkhf5cH+7NtWO
|
||||
lrFj6UwAsGukBTAOxC0l/dnSmZhJ7Z1KmEWilro/gOrjtOxqRQutlIqG22TaqoPG
|
||||
fYVN+en3Zwbt97kcgZDwqbuykNt64oZWc4XKCa3mprEGC3IbJTBFqglXmZ7l9ywG
|
||||
EEUJYOlb2XrSuPWml39beWdKM8kzr1OjnlOm6+lpTRCBfo0wa9F8YZRhHPAkwKkX
|
||||
XDeOGpWRj4ohOx0d2GWkyV5xyN14p2tQOCdOODmz80yUTgRpPVQUtOEhXQARAQAB
|
||||
tCFBV1MgQ0xJIFRlYW0gPGF3cy1jbGlAYW1hem9uLmNvbT6JAlQEEwEIAD4WIQT7
|
||||
Xbd/1cEYuAURraimMQrMRnJHXAUCXYKvtQIbAwUJB4TOAAULCQgHAgYVCgkICwIE
|
||||
FgIDAQIeAQIXgAAKCRCmMQrMRnJHXJIXEAChLUIkg80uPUkGjE3jejvQSA1aWuAM
|
||||
yzy6fdpdlRUz6M6nmsUhOExjVIvibEJpzK5mhuSZ4lb0vJ2ZUPgCv4zs2nBd7BGJ
|
||||
MxKiWgBReGvTdqZ0SzyYH4PYCJSE732x/Fw9hfnh1dMTXNcrQXzwOmmFNNegG0Ox
|
||||
au+VnpcR5Kz3smiTrIwZbRudo1ijhCYPQ7t5CMp9kjC6bObvy1hSIg2xNbMAN/Do
|
||||
ikebAl36uA6Y/Uczjj3GxZW4ZWeFirMidKbtqvUz2y0UFszobjiBSqZZHCreC34B
|
||||
hw9bFNpuWC/0SrXgohdsc6vK50pDGdV5kM2qo9tMQ/izsAwTh/d/GzZv8H4lV9eO
|
||||
tEis+EpR497PaxKKh9tJf0N6Q1YLRHof5xePZtOIlS3gfvsH5hXA3HJ9yIxb8T0H
|
||||
QYmVr3aIUes20i6meI3fuV36VFupwfrTKaL7VXnsrK2fq5cRvyJLNzXucg0WAjPF
|
||||
RrAGLzY7nP1xeg1a0aeP+pdsqjqlPJom8OCWc1+6DWbg0jsC74WoesAqgBItODMB
|
||||
rsal1y/q+bPzpsnWjzHV8+1/EtZmSc8ZUGSJOPkfC7hObnfkl18h+1QtKTjZme4d
|
||||
H17gsBJr+opwJw/Zio2LMjQBOqlm3K1A4zFTh7wBC7He6KPQea1p2XAMgtvATtNe
|
||||
YLZATHZKTJyiqA==
|
||||
=vYOk
|
||||
-----END PGP PUBLIC KEY BLOCK-----"
|
||||
DOCKER_IN_DOCKER_MOBY_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal jammy"
|
||||
DOCKER_IN_DOCKER_LICENSED_ARCHIVE_VERSION_CODENAMES="buster bullseye bionic focal hirsute impish jammy"
|
Loading…
Reference in a new issue