#!/bin/sh
# script/install-go: install the Go toolchain pinned by go.mod into the
# repo-local tool directory, verified against a committed sha256. Our
# own extension to scripts-to-rule-them-all. Idempotent: exits at once
# when the pinned toolchain is already installed.
#
# Only .gitea/workflows/release.yml calls this. goreleaser is not a
# compiler: it shells out to `go` for the `before:` hook and for every
# one of the four cross-compiles, so the release runner needs a Go
# toolchain on PATH. check.yml never does -- it builds inside the
# digest-pinned Dockerfile images -- so this is the release path's only
# host Go, and per REPO_POLICIES.md it must be pinned by hash.
# actions/setup-go exposes no checksum input, so Go is installed the way
# script/install-goreleaser installs goreleaser: download the exact
# archive from go.dev and refuse it unless its sha256 matches the value
# committed below.
#
# The version is go.mod's `go` directive, the single source of truth for
# the toolchain. GO_VERSION below MUST equal it, and this script fails
# when they disagree -- so bumping Go is one reviewed change touching
# go.mod, the checksum here, and the Dockerfile golang digest together.
#
# Linux only, because that is what the release runner is. A darwin dev
# building a snapshot uses their own Go; supporting an OS means adding
# its checksums.
set -eu

ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"

# Go 1.26.1, 2026-09-21. Checksums are the sha256 values go.dev publishes
# for each archive at https://go.dev/dl/ (also in its ?mode=json
# manifest).
GO_VERSION="1.26.1"
SHA256_LINUX_AMD64="031f088e5d955bab8657ede27ad4e3bc5b7c1ba281f05f245bcc304f327c987a"
SHA256_LINUX_ARM64="a290581cfe4fe28ddd737dde3095f3dbeb7f2e4065cab4eae44dfc53b760c2f7"

GOROOT_DIR="$ROOT/.tool/go"
GOCMD="$GOROOT_DIR/bin/go"

# The `go` directive in go.mod, e.g. "1.26.1" from `go 1.26.1`.
gomod_go_version() {
    sed -n 's/^go \([0-9][0-9.]*\).*/\1/p' "$ROOT/go.mod" | head -n 1
}

# Print the version of the go at $1 as "1.26.1", or nothing if it is not
# usable. `go version` prints "go version go1.26.1 linux/amd64".
go_version() {
    [ -x "$1" ] || return 0
    "$1" version 2>/dev/null |
        sed -n 's/^go version go\([0-9][0-9.]*\) .*/\1/p' |
        head -n 1
}

verify_sha256() {
    file="$1"
    want="$2"
    if command -v sha256sum >/dev/null 2>&1; then
        got="$(sha256sum "$file" | cut -d' ' -f1)"
    elif command -v shasum >/dev/null 2>&1; then
        got="$(shasum -a 256 "$file" | cut -d' ' -f1)"
    else
        echo "install-go: no sha256sum or shasum available" >&2
        return 1
    fi
    if [ "$got" != "$want" ]; then
        echo "install-go: checksum mismatch for $file" >&2
        echo "  expected: $want" >&2
        echo "  actual:   $got" >&2
        return 1
    fi
}

# On a Gitea/GitHub Actions runner, put the toolchain on PATH for the
# steps that follow by appending to the file named by $GITHUB_PATH. A
# no-op off CI, where the caller manages its own PATH.
export_ci_path() {
    [ -n "${GITHUB_PATH:-}" ] || return 0
    echo "$GOROOT_DIR/bin" >>"$GITHUB_PATH"
}

main() {
    cd "$ROOT"

    want="$(gomod_go_version)"
    if [ "$want" != "$GO_VERSION" ]; then
        echo "install-go: go.mod says go $want but this script pins" \
            "$GO_VERSION." >&2
        echo "  Update GO_VERSION and the checksums in this script to" \
            "match go.mod." >&2
        exit 1
    fi

    # Already installed from a previous run? Then just fix PATH and stop.
    if [ "$(go_version "$GOCMD")" = "$GO_VERSION" ]; then
        echo "go $GO_VERSION already installed in .tool/go"
        export_ci_path
        return 0
    fi

    os="$(uname -s)"
    arch="$(uname -m)"
    case "$os" in
        Linux) os="linux" ;;
        *)
            echo "install-go: unsupported OS $os (release runner is Linux)" >&2
            exit 1
            ;;
    esac
    case "$arch" in
        x86_64 | amd64)
            arch="amd64"
            sum="$SHA256_LINUX_AMD64"
            ;;
        arm64 | aarch64)
            arch="arm64"
            sum="$SHA256_LINUX_ARM64"
            ;;
        *)
            echo "install-go: no pinned checksum for architecture $arch" >&2
            exit 1
            ;;
    esac

    archive="go${GO_VERSION}.${os}-${arch}.tar.gz"
    url="https://go.dev/dl/${archive}"

    if ! command -v curl >/dev/null 2>&1; then
        echo "install-go: curl is required" >&2
        exit 1
    fi

    dl="$(mktemp -d)"
    mkdir -p "$ROOT/.tool"
    stage="$(mktemp -d "$ROOT/.tool/.go-install.XXXXXX")"
    # shellcheck disable=SC2064 # expand the paths now, not at trap time
    trap "rm -rf '$dl' '$stage'" EXIT INT TERM

    echo "installing go $GO_VERSION for ${os}-${arch}"
    curl -fsSL --retry 3 -o "$dl/$archive" "$url"
    verify_sha256 "$dl/$archive" "$sum"

    # The archive unpacks to a top-level `go/` directory. Extract it into
    # a staging directory on the same filesystem as the destination, then
    # rename it into place so a concurrent run never observes a
    # half-written toolchain.
    tar -xzf "$dl/$archive" -C "$stage"
    rm -rf "$GOROOT_DIR"
    mv "$stage/go" "$GOROOT_DIR"

    installed="$(go_version "$GOCMD")"
    if [ "$installed" != "$GO_VERSION" ]; then
        echo "install-go: installed toolchain reports '$installed'," \
            "expected '$GO_VERSION'" >&2
        exit 1
    fi

    echo "go $GO_VERSION installed to .tool/go"
    export_ci_path
}

main "$@"
