|
| 1 | +#! /usr/bin/env bash |
| 2 | + |
| 3 | +# Define common shell functions |
| 4 | + |
| 5 | +# Run apt update if apt lists aren't populated |
| 6 | +apt_get_update() { |
| 7 | + if [ "$(find /var/lib/apt/lists -mindepth 1 | head -n1 | wc -l)" = "0" ]; then |
| 8 | + echo "Running apt-get update..."; |
| 9 | + apt-get update -y; |
| 10 | + fi |
| 11 | +} |
| 12 | + |
| 13 | +export -f apt_get_update; |
| 14 | + |
| 15 | +# Checks if packages are installed and installs them if not |
| 16 | +check_packages() { |
| 17 | + if ! dpkg -s "$@" > /dev/null 2>&1; then |
| 18 | + apt_get_update; |
| 19 | + echo "Installing packages: $@"; |
| 20 | + DEBIAN_FRONTEND=noninteractive \ |
| 21 | + apt-get -y install --no-install-recommends "$@"; |
| 22 | + fi |
| 23 | +} |
| 24 | + |
| 25 | +export -f check_packages; |
| 26 | + |
| 27 | +for_each_user_bashrc() { |
| 28 | + # Update all bashrc files |
| 29 | + find / /etc /home ${_REMOTE_USER_HOME} ${_CONTAINER_USER_HOME} -maxdepth 2 -type f -name .bashrc \ |
| 30 | + | sort | uniq | xargs -r -d'\n' -n1 bash -c "${@}"; |
| 31 | +} |
| 32 | + |
| 33 | +export -f for_each_user_bashrc; |
| 34 | + |
| 35 | +append_to_all_bashrcs() { |
| 36 | + # Update all bashrc files |
| 37 | + for bashrc in $(find / /etc /home ${_REMOTE_USER_HOME} ${_CONTAINER_USER_HOME} -maxdepth 2 -type f -name .bashrc | sort | uniq); do |
| 38 | + if [[ "$(cat "$bashrc")" != *"$1"* ]]; then |
| 39 | + echo "Appending to $bashrc..."; |
| 40 | + echo -e "$1" >> "$bashrc"; |
| 41 | + fi |
| 42 | + done |
| 43 | +} |
| 44 | + |
| 45 | +export -f append_to_all_bashrcs; |
| 46 | + |
| 47 | +prepend_to_all_bashrcs() { |
| 48 | + # Update all bashrc files |
| 49 | + for bashrc in $(find / /etc /home ${_REMOTE_USER_HOME} ${_CONTAINER_USER_HOME} -maxdepth 2 -type f -name .bashrc | sort | uniq); do |
| 50 | + if [[ "$(cat "$bashrc")" != *"$1"* ]]; then |
| 51 | + echo "Prepending to $bashrc..."; |
| 52 | + echo -e "$1\n$(cat "$bashrc")" > "$bashrc"; |
| 53 | + fi |
| 54 | + done |
| 55 | +} |
| 56 | + |
| 57 | +export -f prepend_to_all_bashrcs; |
| 58 | + |
| 59 | +append_to_etc_profile() { |
| 60 | + if [[ "$(cat /etc/profile)" != *"$1"* ]]; then |
| 61 | + echo "Appending to /etc/profile..."; |
| 62 | + echo -e "$1" >> /etc/profile; |
| 63 | + fi |
| 64 | +} |
| 65 | + |
| 66 | +export -f append_to_etc_profile; |
| 67 | + |
| 68 | +prepend_to_etc_profile() { |
| 69 | + if [[ "$(cat /etc/profile)" != *"$1"* ]]; then |
| 70 | + echo "Prepending to /etc/profile..."; |
| 71 | + echo -e "$1\n$(cat /etc/profile)" > /etc/profile; |
| 72 | + fi |
| 73 | +} |
| 74 | + |
| 75 | +export -f prepend_to_etc_profile; |
| 76 | + |
| 77 | +append_to_etc_bashrc() { |
| 78 | + if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then |
| 79 | + echo "Appending to /etc/bash.bashrc..."; |
| 80 | + echo -e "$1" >> /etc/bash.bashrc; |
| 81 | + fi |
| 82 | +} |
| 83 | + |
| 84 | +export -f append_to_etc_bashrc; |
| 85 | + |
| 86 | +prepend_to_etc_bashrc() { |
| 87 | + if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then |
| 88 | + echo "Prepending to /etc/bash.bashrc..."; |
| 89 | + echo -e "$1\n$(cat /etc/bash.bashrc)" > /etc/bash.bashrc; |
| 90 | + fi |
| 91 | +} |
| 92 | + |
| 93 | +export -f prepend_to_etc_bashrc; |
| 94 | + |
| 95 | +append_etc_zshrc() { |
| 96 | + if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then |
| 97 | + echo "Appending to /etc/zsh/zshrc..."; |
| 98 | + echo -e "$1" >> /etc/zsh/zshrc; |
| 99 | + fi |
| 100 | +} |
| 101 | + |
| 102 | +export -f append_etc_zshrc; |
| 103 | + |
| 104 | +prepend_to_etc_zshrc() { |
| 105 | + if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then |
| 106 | + echo "Prepending to /etc/zsh/zshrc..."; |
| 107 | + echo -e "$1\n$(cat /etc/zsh/zshrc)" > /etc/zsh/zshrc; |
| 108 | + fi |
| 109 | +} |
| 110 | + |
| 111 | +export -f prepend_to_etc_zshrc; |
| 112 | + |
| 113 | +add_etc_profile_d_script() { |
| 114 | + local name="$(($(ls -1q /etc/profile.d/*.sh | wc -l) + 20))-${1}.sh"; |
| 115 | + echo -e "#! /usr/bin/env bash\n${@:2}" > "/etc/profile.d/${name}"; |
| 116 | + chmod +x "/etc/profile.d/${name}"; |
| 117 | +} |
| 118 | + |
| 119 | +export -f add_etc_profile_d_script; |
| 120 | + |
| 121 | +# Figure out correct version of a three part version number is not passed |
| 122 | +find_version_from_git_tags() { |
| 123 | + check_packages git; |
| 124 | + local variable_name=$1 |
| 125 | + local requested_version=${!variable_name} |
| 126 | + if [ "${requested_version}" = "none" ]; then return; fi |
| 127 | + local repository=$2 |
| 128 | + local prefix=${3:-"tags/v"} |
| 129 | + local separator=${4:-"."} |
| 130 | + local suffix=${5:-} |
| 131 | + local last_part_optional=${6:-"false"} |
| 132 | + if [ "$(echo "${requested_version}" | grep -o "." | wc -l)" != "2" ]; then |
| 133 | + local escaped_separator=${separator//./\\.} |
| 134 | + local last_part= |
| 135 | + if [ "${last_part_optional}" = "true" ]; then |
| 136 | + last_part+="(${escaped_separator}[0-9]+)?" |
| 137 | + last_part+="(${escaped_separator}[0-9]+)?" |
| 138 | + if [ -n "${suffix}" ]; then |
| 139 | + last_part+="(${suffix})?" |
| 140 | + fi |
| 141 | + else |
| 142 | + last_part+="${escaped_separator}[0-9]+" |
| 143 | + last_part+="${escaped_separator}[0-9]+" |
| 144 | + if [ -n "${suffix}" ]; then |
| 145 | + last_part+="(${suffix})" |
| 146 | + fi |
| 147 | + fi |
| 148 | + local regex="${prefix}\\K[0-9]+${last_part}$" |
| 149 | + local version_list="$(git ls-remote --tags ${repository} | grep -oP "${regex}" | tr -d ' ' | tr "${separator}" "." | sort -rV)" |
| 150 | + if [ "${requested_version}" = "latest" ] || [ "${requested_version}" = "current" ] || [ "${requested_version}" = "lts" ]; then |
| 151 | + declare -g ${variable_name}="$(echo "${version_list}" | head -n 1)" |
| 152 | + else |
| 153 | + set +e |
| 154 | + declare -g ${variable_name}="$(echo "${version_list}" | grep -E -m 1 "^${requested_version//./\\.}([\\.\\s]|$)")" |
| 155 | + set -e |
| 156 | + fi |
| 157 | + fi |
| 158 | + if [ -z "${!variable_name}" ] || ! echo "${version_list}" | grep "^${!variable_name//./\\.}$" > /dev/null 2>&1; then |
| 159 | + echo -e "Invalid ${variable_name} value: ${requested_version}\nValid values:\n${version_list}" >&2 |
| 160 | + exit 1 |
| 161 | + fi |
| 162 | + echo "${variable_name}=${!variable_name}" |
| 163 | +} |
| 164 | + |
| 165 | +export -f find_version_from_git_tags; |
| 166 | + |
| 167 | +# Use semver logic to decrement a version number then look for the closest match |
| 168 | +find_prev_version_from_git_tags() { |
| 169 | + local variable_name=$1 |
| 170 | + local current_version=${!variable_name} |
| 171 | + local repository=$2 |
| 172 | + # Normally a "v" is used before the version number, but support alternate cases |
| 173 | + local prefix=${3:-"tags/v"} |
| 174 | + # Some repositories use "_" instead of "." for version number part separation, support that |
| 175 | + local separator=${4:-"."} |
| 176 | + # Some repositories may have tags that include a suffix (e.g. actions/node-versions) |
| 177 | + local version_suffix_regex=${5:-} |
| 178 | + # Some tools release versions that omit the last digit (e.g. go) |
| 179 | + local last_part_optional=${6:-"false"} |
| 180 | + # Try one break fix version number less if we get a failure. Use "set +e" since "set -e" can cause failures in valid scenarios. |
| 181 | + set +e |
| 182 | + major="$(echo "${current_version}" | grep -oE '^[0-9]+' || echo '')" |
| 183 | + minor="$(echo "${current_version}" | grep -oP '^[0-9]+\.\K[0-9]+' || echo '')" |
| 184 | + breakfix="$(echo "${current_version}" | grep -oP '^[0-9]+\.[0-9]+\.\K[0-9]+' 2>/dev/null || echo '')" |
| 185 | + |
| 186 | + if [ "${minor}" = "0" ] && [ "${breakfix}" = "0" ]; then |
| 187 | + ((major=major-1)) |
| 188 | + declare -g ${variable_name}="${major}" |
| 189 | + # Look for latest version from previous major release |
| 190 | + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${version_suffix_regex}" "${last_part_optional}" |
| 191 | + # Handle situations like Go's odd version pattern where "0" releases omit the last part |
| 192 | + elif [ "${breakfix}" = "" ] || [ "${breakfix}" = "0" ]; then |
| 193 | + ((minor=minor-1)) |
| 194 | + declare -g ${variable_name}="${major}.${minor}" |
| 195 | + # Look for latest version from previous minor release |
| 196 | + find_version_from_git_tags "${variable_name}" "${repository}" "${prefix}" "${separator}" "${version_suffix_regex}" "${last_part_optional}" |
| 197 | + else |
| 198 | + ((breakfix=breakfix-1)) |
| 199 | + if [ "${breakfix}" = "0" ] && [ "${last_part_optional}" = "true" ]; then |
| 200 | + declare -g ${variable_name}="${major}.${minor}" |
| 201 | + else |
| 202 | + declare -g ${variable_name}="${major}.${minor}.${breakfix}" |
| 203 | + fi |
| 204 | + fi |
| 205 | + set -e |
| 206 | +} |
| 207 | + |
| 208 | +export -f find_prev_version_from_git_tags; |
| 209 | + |
| 210 | +# Determine the appropriate non-root user |
| 211 | +find_non_root_user() { |
| 212 | + USERNAME="${USERNAME:-"${_REMOTE_USER:-"auto"}"}"; |
| 213 | + if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then |
| 214 | + USERNAME="" |
| 215 | + POSSIBLE_USERS=("vscode" "node" "codespace" "coder" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") |
| 216 | + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do |
| 217 | + if id -u ${CURRENT_USER} > /dev/null 2>&1; then |
| 218 | + USERNAME=${CURRENT_USER} |
| 219 | + break |
| 220 | + fi |
| 221 | + done |
| 222 | + elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" > /dev/null 2>&1; then |
| 223 | + USERNAME=root |
| 224 | + fi |
| 225 | + if [ "${USERNAME}" = "" ]; then |
| 226 | + USERNAME=root |
| 227 | + fi |
| 228 | +} |
| 229 | + |
| 230 | +export -f find_non_root_user; |
0 commit comments