#! /usr/bin/env bash ######### Repo list # this is a reference template # function _repo_template_function() { # REPO_FQDN='' # host FQDN, this will also be the name of the apt list/source file # GPG_KEY_URL='' # [optional] URL to the GPG Key, if not supplied it will be https://$REPO_FQDN/gpg # GPG_KEY_PATH='' # [optional] Location on disk for the public GPG key # REPO_ARCH='' # [optional] System architecture specific package, '-' is shorthand for current architecture # REPO_URL='' # [optional] REPO Url if not https://$REPO_FQDN, which is the default. # REPO_SUITE='' # [optional] Suite / Release codename # REPO_CMP='' # [optional] Package Channel - default is 'main', this is vendor specific. # } function _repo_docker() { # for: docker-ce docker-ce-cli containerd.io docker-compose-plugin # Also don't forget: # remove: docker docker-engine docker.io containerd runc # possibly remove docker-compose and install: https://github.com/docker/compose-switch REPO_FQDN='download.docker.com' REPO_URL="https://download.docker.com/linux/$_OS" GPG_KEY_URL="$REPO_URL/gpg" REPO_CMP='stable' } function _repo_anydesk() { # for: anydesk REPO_FQDN='deb.anydesk.com' REPO_URL="http://$REPO_FQDN/" # NOTE, this is http, not https GPG_KEY_URL='https://keys.anydesk.com/repos/DEB-GPG-KEY' REPO_ARCH='-' REPO_CMP='main' REPO_SUITE='all' } function _repo_gcloud() { # for: google-cloud-cli REPO_FQDN='packages.cloud.google.com' REPO_URL="https://$REPO_FQDN/apt" GPG_KEY_URL="$REPO_URL/doc/apt-key.gpg" REPO_ARCH='-' REPO_SUITE='cloud-sdk' } function _repo_mspackages() { # for: code (optionally: powershell dotnet-*) REPO_FQDN='packages.microsoft.com' GPG_KEY_URL="https://$REPO_FQDN/keys/microsoft.asc" REPO_URL="https://$REPO_FQDN/$_OS/$_REL/prod" } function _repo_msedge() { # for: microsoft-edge-stable REPO_FQDN='edge.microsoft.com' GPG_KEY_URL="https://packages.microsoft.com/keys/microsoft.asc" REPO_URL="http://packages.microsoft.com/repos/edge" REPO_SUITE='stable' } function _repo_hashicorp() { # for: terraform REPO_FQDN='apt.releases.hashicorp.com' # GPG_KEY_URL='' # default value is enough here # REPO_URL='' # default value is enough here } function _repo_custom_xscreensavers() { # I really LOVE xscreensavers, but it's been stuck in limbo on the # `stable` repos, so I install it from the `unstable`, aka `sid`, suite. # To do that safely, I make sure the repo is pinned to a lower priority. # I do this BEFORE I add the repo. # # Debian sid (unstable) as low-priority option (echo 'Package: *'; echo 'Pin: release a=unstable'; echo 'Pin-Priority: 200') | sudo tee /etc/apt/preferences.d/unstable > /dev/null # # pin xscreensaver to unstable (echo 'Package: xscreenaver*'; echo 'Pin: release a=unstable'; echo 'Pin-Priority: 2000') | sudo tee /etc/apt/preferences.d/xscreensaver > /dev/null # for: xscreensaver xscreensaver-data xscreensaver-data-extra # xscreensaver-screensaver-bsod xscreensaver-screensaver-webcollage # Add the unstable/sid repo - THIS IS DANGEROUS without the pinning via # /etc/apt/preferences.d files created by the code above. sudo apt-add-repository $_YES --no-update "deb [arch=amd64] http://deb.debian.org/debian sid main non-free contrib" } ############ Script Logic starts here set -e ANSI_Print() { printf "\E[${ANSI_Code}m%s\E[0m" "$@"; } ANSI_Red() { ANSI_Code=31 ANSI_Print "$@"; } ANSI_Green() { ANSI_Code=32 ANSI_Print "$@"; } ANSI_Yellow() { ANSI_Code=33 ANSI_Print "$@"; } ANSI_Blue() { ANSI_Code=34 ANSI_Print "$@"; } ANSI_Indiego() { ANSI_Code=35 ANSI_Print "$@"; } ANSI_Cyan() { ANSI_Code=36 ANSI_Print "$@"; } error() { (>&2 ANSI_Red 'ERROR: ' && printf '%s\n' "$@" ) } require_root() { if [[ $EUID -ne 0 ]]; then error "This script should only be run using sudo or as the root user" exit 1 fi } # The function that does the _Heavy Lifting_. # see the code that follows for the parameter signature function add_repo() { unset REPO_FQDN GPG_KEY_URL GPG_KEY_PATH REPO_ARCH REPO_URL REPO_SUITE REPO_CMP # In case custom handling is required, call it and return. if [[ "$(type -t _repo_custom_$1)" == 'function' ]]; then eval "_repo_custom_$1" && return 0 return $? # If no repo defintion exist, exit with an error elif [[ "$(type -t _repo_$1)" != 'function' ]]; then error "Can't configure $1, could not find _repo_$1 definition!" return 1 fi # Grab the repo's defintion eval "_repo_$1" # Prepare all necessary vraiables [[ -z "$REPO_FQDN" ]] && error "REPO_FQDN not defined for '$1'" && return 1 GPG_KEY_URL=${GPG_KEY_URL:-https:\/\/$REPO_FQDN\/gpg} GPG_KEY_PATH=${GPG_KEY_PATH:-/usr/share/keyrings/$REPO_FQDN.gpg} REPO_ARCH=${REPO_ARCH:-${_ARC}} [[ $REPO_ARCH == '-' ]] && REPO_ARCH='' || REPO_ARCH="arch=$REPO_ARCH" REPO_URL=${REPO_URL:-https:\/\/$REPO_FQDN} REPO_SUITE=${REPO_SUITE:-$_CNM} REPO_CMP=${REPO_CMP:-main} # Grab the GPG key and save it. curl -fsSL $GPG_KEY_URL | sudo gpg --dearmor -o $GPG_KEY_PATH $_YES echo "Key created: $GPG_KEY_PATH" # Define the package source echo "deb [$(echo "$REPO_ARCH signed-by=$GPG_KEY_PATH" | xargs )] $REPO_URL $REPO_SUITE $REPO_CMP" | \ sudo tee /etc/apt/sources.list.d/$REPO_FQDN.list > /dev/null echo "APT source list added: /etc/apt/sources.list.d/$REPO_FQDN.list" } function usage() { printf " %s\n" \ "Usage:" \ " $0 [-v[v]] " \ " [--no-init] [--no-overwrite] " \ " all|[ ...] " \ "" \ "Repos: $( \ printf "\n %s" $( \ typeset -f \ | grep '^_repo_' \ | sed 's/_repo\(_custom\)\?_\(.*\) ()/\2/' \ | sort \ ))" \ "" \ "Options:" \ " --no-init - skip package initialization." \ " --no-overwrite - don't overwrite gpg keys automatially, ask." \ " new keys will still be written without asking." \ " -v[v] - script verbosity, equivalent to set -x and set -xv" \ "" } function main() { [[ ${#REPO_LIST[@]} -eq 0 ]] && usage && return 1 require_root _OS=$(lsb_release -is | awk '{ print tolower($0) }') [[ $_OS == 'pop' ]] && _OS='ubuntu' _REL=$(lsb_release -rs) # Release _CNM=$(lsb_release -cs) # CodeName _ARC=$(dpkg --print-architecture) # Architecture ANSI_Cyan "Initializing: Installing basic packages..."; echo if [[ -n "$SHOULD_INIT" ]]; then # Make sure the /usr/share/keyrings dir exists mkdir -p /usr/share/keyrings 2>&1 > /dev/null # Make sure all of the basic required tools are installed for the code # below to work apt-get install --yes --no-install-recommends \ curl gnupg software-properties-common apt-transport-https fi ANSI_Green "Done."; echo while read r; do ANSI_Cyan "Installing $r repository..."; echo add_repo $r && UPDATE_REQUIRED=1 || return 2 ANSI_Green "Installation of $r repository completed."; echo done < <(printf "%s\n" "${REPO_LIST[@]}") # Finally, run `apt update` to check that everything completed successfully if [[ $UPDATE_REQUIRED == 1 ]]; then ANSI_Cyan "Finalizing: running 'apt update'..."; echo MISSING_SIGNATURES=($( \ sudo apt update 2>&1 1>/dev/null \ | sed -ne 's/.*NO_PUBKEY //p' \ )) $MISSING_SIGNATURES| while read key; do if ! [[ ${keys[*]} =~ "$key" ]]; then sudo apt-key adv --keyserver hkp://pool.sks-keyservers.net:80 --recv-keys "$key"; keys+=("$key"); fi; done sudo apt update ANSI_Green "'apt update' completed."; echo fi } # Iterate through stdin or parameters if [[ $# == 0 ]]; then usage else REPO_LIST=() _YES='--yes' _SHOULD_INIT=1 UPDATE_REQUIRED=0 READCMD='printf "%s\n" "${@}"' [[ $# == 1 && $1 == '-' ]] && READCMD='cat - ' while read a; do # Switch on _YES [[ "$a" == "-v" ]] && set -x && continue [[ "$a" == "-vv" ]] && set -xv && continue [[ "$a" == "--no-overwrite" ]] && unset _YES && continue [[ "$a" == "--no-init" ]] && unset _SHOULD_INIT && continue # Add repo [[ -n "$a" ]] && REPO_LIST+=("$a") done < <( eval $READCMD ) main fi