Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,3 @@ wget -qO- https://raw.githubusercontent.com/DieTime/go-up/master/go-up.sh | bash
- Works with either curl or wget on the system
- Automatically determines the path to install a new version
- Restores the GOPATH folder if it is inside GOROOT
- Does not use temporary files
176 changes: 123 additions & 53 deletions go-up.sh
Original file line number Diff line number Diff line change
@@ -1,113 +1,183 @@
#!/bin/bash


set -euo pipefail

SUDOCMD=""
WGETCMD="wget -qO-"
CURLCMD="curl -sL"
EXTRACTCMD="tar -xzf - --strip-components=1 -C"

GOBIN=$(which go 2> /dev/null)
cleanup() {
[[ -n "${TEMP_DIR:-}" ]] && [[ -d "$TEMP_DIR" ]] && rm -rf "$TEMP_DIR"
[[ -n "${TEMP_FILE:-}" ]] && [[ -f "$TEMP_FILE" ]] && rm -f "$TEMP_FILE"
}
trap cleanup EXIT

if [[ -z "$GOBIN" ]]
then
set +e
GOBIN=$(which go 2>/dev/null)
set -e

if [[ -z "$GOBIN" ]]; then
echo "❌ Go not installed, nothing to update"
exit 1
fi

GOROOT=$($GOBIN env GOROOT 2> /dev/null)
GOPATH=$($GOBIN env GOPATH 2> /dev/null)
GOROOT=$($GOBIN env GOROOT 2>/dev/null) || true
GOPATH=$($GOBIN env GOPATH 2>/dev/null) || true

if [[ -z "$GOROOT" ]]
then
echo "❌ Could't find \$GOROOT environment variable"
if [[ -z "$GOROOT" ]]; then
echo "❌ Couldn't find \$GOROOT environment variable"
exit 1
fi

GOVERSION=$($GOBIN env GOVERSION 2> /dev/null)
GOVERSION=$($GOBIN env GOVERSION 2>/dev/null) || true
GOVERSIONPRETTY=${GOVERSION#"go"}

if [[ -z "$GOVERSION" ]]
then
echo "❌ Could't find \$GOVERSION environment variable"
if [[ -z "$GOVERSION" ]]; then
echo "❌ Couldn't find \$GOVERSION environment variable"
exit 1
fi

if [[ -z "$(which wget 2> /dev/null)" ]] && [[ -z "$(which curl 2> /dev/null)" ]]
then
echo "✅ Found Go $GOVERSIONPRETTY installed at $GOROOT"

if [[ -z "$(which wget 2>/dev/null)" ]] && [[ -z "$(which curl 2>/dev/null)" ]]; then
echo "❌ Unable to update go to latest version, please install wget or curl"
exit 1
fi

echo "🌐 Checking for latest Go version..."

GOLATESTURL="https://go.dev/VERSION?m=text"
GOLATEST=$($WGETCMD "$GOLATESTURL" 2> /dev/null || $CURLCMD "$GOLATESTURL" 2> /dev/null)
set +e
GOLATEST=$($WGETCMD "$GOLATESTURL" 2>/dev/null | head -n1 || $CURLCMD "$GOLATESTURL" 2>/dev/null | head -n1)
set -e
GOLATESTPRETTY=${GOLATEST#"go"}

if [[ -z "$GOLATEST" ]]
then
echo "❌ Could't fetch latest golang version"
# Validate we got a version
if [[ -z "$GOLATEST" ]]; then
echo "❌ Couldn't fetch latest golang version"
echo " Please check your internet connection"
exit 1
fi

if [[ "$GOVERSION" == "$GOLATEST" ]]
then
if [[ "$GOVERSION" == "$GOLATEST" ]]; then
echo "👍 You already have the latest go with version $GOVERSIONPRETTY, no update required"
exit 0
fi

echo "📋 Update available: Go $GOVERSIONPRETTY → Go $GOLATESTPRETTY"

# Detect GOROOT ownership for permission handling
case $(uname) in
Linux) GOROOTOWNER=$(stat -c "%U" $GOROOT 2> /dev/null) ;;
Darwin) GOROOTOWNER=$(stat -f "%Su" $GOROOT 2> /dev/null) ;;
Linux) GOROOTOWNER=$(stat -c "%U" $GOROOT 2>/dev/null) || true ;;
Darwin) GOROOTOWNER=$(stat -f "%Su" $GOROOT 2>/dev/null) || true ;;
esac

if [[ -z "$GOROOTOWNER" ]]
then
echo "❌ Could't find owner of the \$GOROOT directory"
if [[ -z "$GOROOTOWNER" ]]; then
echo "❌ Couldn't find owner of the \$GOROOT directory"
exit 1
fi

if [[ "$USER" != "$GOROOTOWNER" ]]
then
echo "🔥 Requires "$GOROOTOWNER" user access rights ..."
if [[ "$USER" != "$GOROOTOWNER" ]]; then
echo "🔥 Requires $GOROOTOWNER user access rights ..."

SUDOCMD="sudo -u $GOROOTOWNER"
$SUDOCMD echo -n ""
if [[ "$GOROOTOWNER" == "root" ]]; then
SUDOCMD="sudo"
else
SUDOCMD="sudo -u $GOROOTOWNER"
fi

if ! $SUDOCMD true; then
echo "❌ Cannot execute commands as $GOROOTOWNER"
exit 1
fi

if ! $SUDOCMD touch "$GOROOT/.test_write"; then
echo "❌ Cannot write to $GOROOT even with sudo"
exit 1
fi
$SUDOCMD rm -f "$GOROOT/.test_write"
fi

GOARCH=$($GOBIN env GOARCH 2> /dev/null)
GOARCH=$($GOBIN env GOARCH 2>/dev/null) || true

if [[ -z "$GOARCH" ]]
then
echo "❌ Could't find \$GOARCH environment variable"
if [[ -z "$GOARCH" ]]; then
echo "❌ Couldn't find \$GOARCH environment variable"
exit 1
fi

GOOS=$($GOBIN env GOOS 2> /dev/null)
GOOS=$($GOBIN env GOOS 2>/dev/null) || true

if [[ -z "$GOOS" ]]
then
echo "❌ Could't find \$GOOS environment variable"
if [[ -z "$GOOS" ]]; then
echo "❌ Couldn't find \$GOOS environment variable"
exit 1
fi

GOTARBALL="https://go.dev/dl/$GOLATEST.$GOOS-$GOARCH.tar.gz"

$SUDOCMD rm -rf "$GOROOT"/*
$WGETCMD $GOTARBALL 2> /dev/null | $SUDOCMD $EXTRACTCMD $GOROOT 2> /dev/null
echo "📥 Downloading Go $GOLATESTPRETTY..."

if [[ "$?" != "0" ]]
then
$SUDOCMD rm -rf "$GOROOT"/*
$CURLCMD $GOTARBALL 2> /dev/null | $SUDOCMD $EXTRACTCMD $GOROOT 2> /dev/null
TEMP_DIR=$(mktemp -d)
TEMP_FILE=$(mktemp)

if [[ "$?" != "0" ]]
then
echo "❌ Couldn't download and unpack go with version $GOLATESTPRETTY"
exit 1
download_success=false
if command -v wget >/dev/null 2>&1; then
echo "Using wget to download..."
if wget --progress=bar:force -O "$TEMP_FILE" "$GOTARBALL" 2>&1; then
download_success=true
fi
elif command -v curl >/dev/null 2>&1; then
echo "Using curl to download..."
if curl --progress-bar -L -o "$TEMP_FILE" "$GOTARBALL"; then
download_success=true
fi
fi

if [[ -n "$GOPATH" ]] && [[ "$GOPATH" == "$GOROOT"* ]]
then
$SUDOCMD mkdir -p $GOPATH
if [[ "$download_success" != "true" ]]; then
echo "❌ Failed to download Go $GOLATESTPRETTY"
exit 1
fi

echo "👍 Go successfully upgraded from version $GOVERSIONPRETTY to version $GOLATESTPRETTY"
echo "✅ Download completed successfully!"

echo "📦 Extracting and verifying Go $GOLATESTPRETTY..."

if ! tar -xzf "$TEMP_FILE" -C "$TEMP_DIR"; then
echo "❌ Failed to extract Go tarball"
exit 1
fi

if ! "$TEMP_DIR/go/bin/go" version >/dev/null 2>&1; then
echo "❌ Downloaded Go installation is not working"
exit 1
fi

echo "🔄 Installing Go $GOLATESTPRETTY..."

# Atomic replacement with rollback capability
if [[ -d "$GOROOT" ]]; then
OLD_GOROOT="${GOROOT}.old.$$"
$SUDOCMD mv "$GOROOT" "$OLD_GOROOT"
else
OLD_GOROOT=""
fi

if $SUDOCMD mv "$TEMP_DIR/go" "$GOROOT"; then
# Verify new installation works
if "$GOROOT/bin/go" version >/dev/null 2>&1; then
[[ -n "$OLD_GOROOT" ]] && $SUDOCMD rm -rf "$OLD_GOROOT"
echo "✅ Go successfully upgraded from version $GOVERSIONPRETTY to version $GOLATESTPRETTY"
else
# Rollback on failure
echo "❌ New Go installation failed verification, rolling back..."
$SUDOCMD rm -rf "$GOROOT"
[[ -n "$OLD_GOROOT" ]] && $SUDOCMD mv "$OLD_GOROOT" "$GOROOT"
echo "❌ Rollback completed, Go $GOVERSIONPRETTY restored"
exit 1
fi
else
# Restore old installation if move failed
echo "❌ Failed to install new Go version, restoring previous installation..."
[[ -n "$OLD_GOROOT" ]] && $SUDOCMD mv "$OLD_GOROOT" "$GOROOT"
exit 1
fi