Skip to content

Instantly share code, notes, and snippets.

@ianfoo
Last active January 21, 2024 02:58
Show Gist options
  • Save ianfoo/32f78b615d35bf581d4709f514409554 to your computer and use it in GitHub Desktop.
Save ianfoo/32f78b615d35bf581d4709f514409554 to your computer and use it in GitHub Desktop.
Install/switch version of Go on a Linux host
#! /usr/bin/env bash
#
# This script downloads and installs Go from http://go.dev. If no version is
# provided, it will install the latest released version as shown by
# https://go.dev/VERSION. If there is already a Go installed, the script will
# replace that installation with the new one (though it will not delete the old
# installation). If a directory exists in BASE_DIR indicating that the
# requested version is already present, the download will be skipped.
#
# Requirements: curl, GNU stow
#
# Assumptions
# ===========
# Go is (or will be) installed in a protected directory that requires sudo to
# write. Specifically, /usr/local. The actual directory can be changed easily,
# by changing the variable, but the sudo'd commands are interspersed. It would
# be possible to conditionally execute the commands with sudo by determining
# whether the current user had write privileges in BASE_DIR, but that's beyond
# the scope of this little script.
#
# CHANGELOG
# =========
# 20-Jan-2024
# * Update latest_released_go_version to restrict output to one line,
# since this page now includes a second line with the time of the
# release.
set -eEuo pipefail
PREFIX="/usr/local"
BASE_DIR="$PREFIX/go"
WORK_DIR=""
FILE_SUFFIX="linux-amd64.tar.gz"
latest_released_go_version () {
echo "$(curl -sL 'https://go.dev/VERSION?m=text')" | head -n 1
}
go_version_from_filename () {
echo $1 | sed -E "s|${WORK_DIR}/(.+)\.${FILE_SUFFIX}|\1|"
}
download_latest_go_distribution () {
local latest_go_version=$1
local download_filename="${latest_go_version}.${FILE_SUFFIX}"
local download_url="https://go.dev/dl/${download_filename}"
curl -LsO --output-dir "$WORK_DIR" "$download_url"
echo "$WORK_DIR/$download_filename"
}
unpack_and_move_distribution () {
local distribution_file=$1
local downloaded_version=$(go_version_from_filename "$distribution_file")
local installed_directory="${BASE_DIR}/${downloaded_version}"
# Unpack the archive in WORK_DIR, next to the archive file.
tar xzf "$distribution_file" -C "$WORK_DIR"
# Untarring produces a "go" directory, so we need to account for that
# when relocating the unpacked Go distribution.
sudo mv "$WORK_DIR/go" "$installed_directory"
}
get_currently_installed_go_version () {
local go_executable="$PREFIX/bin/go"
if [[ -f $go_executable ]]; then
stat -c%N $go_executable | awk -F '->' '{ print $2 }' | awk -F/ '{ print $3 }'
fi
}
remove_current_go_installation () {
local current_version="$1"
if [[ -z $current_version ]]; then
return 1
fi
cd "$BASE_DIR"
if [[ ! -d "$current_version" ]]; then
echo >&2 "No Go installation found at $BASE_DIR/$current_version: skipping stow deletion"
return 1
fi
sudo stow -D "$current_version"
}
install_new_go_version () {
local new_version=$1
cd "$BASE_DIR"
if [[ ! -d "$new_version" ]]; then
echo >&2 "error: expected new go version unpacked at $BASE_DIR/$new_version"
return 1
fi
sudo stow "$new_version"
}
cleanup () {
if [[ -d "$WORK_DIR" ]]; then
rm -rf "$WORK_DIR"
fi
}
main () {
sudo mkdir -p "$BASE_DIR"
trap cleanup EXIT
local requested_version=$1
if [[ -z $requested_version ]]; then
# User has not requested a particular version, so get the latest.
requested_version="$(latest_released_go_version)"
echo "Latest version is $requested_version"
elif [[ ! $requested_version =~ "^go" ]]; then
# Ensure version is in "goN.NN" format.
requested_version="go${requested_version}"
fi
local current_version="$(get_currently_installed_go_version)"
if [[ $requested_version == $current_version ]]; then
echo >&2 "Version $requested_version is already the currently installed version"
return 1
fi
if [[ ! -d "$BASE_DIR/$requested_version" ]]; then
WORK_DIR="$(mktemp --tmpdir -d golang-get.XXXX)"
echo "Downloading $requested_version to $WORK_DIR"
local downloaded_file="$(download_latest_go_distribution $requested_version)"
unpack_and_move_distribution "$downloaded_file"
fi
remove_current_go_installation "$current_version" && echo "Deactivated $current_version"
install_new_go_version "$requested_version" && echo "Installed $requested_version"
}
main "${1:-}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment