2022-05-10 01:16:15 +03:00
#!/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 } "
2022-05-16 23:18:54 +03:00
# Display a notice on conda when not running in GitHub Codespaces
cat << 'EOF' > /usr/local/etc/vscode-dev-containers/conda-notice.txt
When using "conda" from outside of GitHub Codespaces, note the Anaconda repository contains
restrictions on commercial use that may impact certain organizations. See https://aka.ms/ghcs-conda
EOF
notice_script = " $( cat << 'EOF'
if [ -t 1 ] && [ " ${ IGNORE_NOTICE } " != "true" ] && [ " ${ TERM_PROGRAM } " = "vscode" ] && [ " ${ CODESPACES } " != "true" ] && [ ! -f " $HOME /.config/vscode-dev-containers/conda-notice-already-displayed " ] ; then
cat "/usr/local/etc/vscode-dev-containers/conda-notice.txt"
mkdir -p " $HOME /.config/vscode-dev-containers "
( ( sleep 10s; touch " $HOME /.config/vscode-dev-containers/conda-notice-already-displayed " ) & )
fi
EOF
) "
echo " ${ notice_script } " | tee -a /etc/bash.bashrc >> /etc/zsh/zshrc
2022-05-10 01:16:15 +03:00
echo "Done!"