Last active
December 14, 2025 18:15
-
-
Save alexjoedt/9c61f9cc4ce211430257b2febd68be9f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # shellcheck disable=SC2016 | |
| set -euo pipefail | |
| #============================================================================== | |
| # Go Installation Script | |
| #============================================================================== | |
| # This script installs Go to $HOME/.local/go by default (user-local). | |
| # Use --system-install for traditional /usr/local/go installation. | |
| # | |
| # Quick Install (from GitHub Gist): | |
| # curl -fsSL https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/a565fb60eae2b5a4c255890ef1cf4ce579d33b29/install_go.sh | bash | |
| # curl -fsSL https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/a565fb60eae2b5a4c255890ef1cf4ce579d33b29/install_go.sh | bash -s -- --with-tools | |
| # curl -fsSL https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/a565fb60eae2b5a4c255890ef1cf4ce579d33b29/install_go.sh | bash -s -- --system-install | |
| # curl -fsSL https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/a565fb60eae2b5a4c255890ef1cf4ce579d33b29/install_go.sh | bash -s -- --system-install --with-tools | |
| # | |
| # Flags: | |
| # --with-tools Install Go development tools after installing Go | |
| # --only-tools Only install tools (skip Go installation) | |
| # --system-install Install to /usr/local/go (requires sudo) | |
| # -y, --yes Skip all interactive prompts (auto-yes) | |
| # -f, --force Force reinstall even if latest version exists | |
| # -h, --help Show help message | |
| #============================================================================== | |
| #------------------------------------------------------------------------------ | |
| # Helper Functions | |
| #------------------------------------------------------------------------------ | |
| # Print error message and exit | |
| error_exit() { | |
| echo "ERROR: $1" >&2 | |
| exit 1 | |
| } | |
| # Print warning message | |
| warn() { | |
| echo "WARNING: $1" >&2 | |
| } | |
| # Print info message | |
| info() { | |
| echo "INFO: $1" | |
| } | |
| # Show usage information | |
| show_help() { | |
| cat << EOF | |
| Usage: $(basename "$0") [OPTIONS] | |
| Install or update Go programming language. | |
| OPTIONS: | |
| --with-tools Install Go development tools after installing Go | |
| --only-tools Only install tools (requires Go to be installed) | |
| --system-install Install to /usr/local/go instead of ~/.local/go | |
| -y, --yes Skip all interactive prompts (auto-accept) | |
| -f, --force Force reinstall even if latest version is installed | |
| -h, --help Show this help message | |
| EXAMPLES: | |
| $(basename "$0") # Install Go to ~/.local/go | |
| $(basename "$0") --with-tools # Install Go and development tools | |
| $(basename "$0") --system-install # Install to /usr/local/go (requires sudo) | |
| $(basename "$0") --only-tools # Only install development tools | |
| EOF | |
| exit 0 | |
| } | |
| # Check if required dependencies are available | |
| check_dependencies() { | |
| local missing_deps=() | |
| # Check for curl or wget | |
| if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then | |
| missing_deps+=("curl or wget") | |
| fi | |
| # Check for jq (required for parsing Go version JSON) | |
| if ! command -v jq >/dev/null 2>&1; then | |
| case "$(uname -s)" in | |
| Linux) | |
| info "Installing jq..." | |
| if command -v apt-get >/dev/null 2>&1; then | |
| sudo apt-get -qq install jq -y >/dev/null 2>&1 || missing_deps+=("jq") | |
| elif command -v pacman >/dev/null 2>&1; then | |
| sudo pacman -S --noconfirm --quiet jq >/dev/null 2>&1 || missing_deps+=("jq") | |
| elif command -v yum >/dev/null 2>&1; then | |
| sudo yum install -q -y jq >/dev/null 2>&1 || missing_deps+=("jq") | |
| elif command -v dnf >/dev/null 2>&1; then | |
| sudo dnf install -q -y jq >/dev/null 2>&1 || missing_deps+=("jq") | |
| else | |
| missing_deps+=("jq") | |
| fi | |
| ;; | |
| Darwin) | |
| if command -v brew >/dev/null 2>&1; then | |
| info "Installing jq via Homebrew..." | |
| brew install jq >/dev/null 2>&1 || missing_deps+=("jq") | |
| else | |
| missing_deps+=("jq (and Homebrew)") | |
| fi | |
| ;; | |
| *) | |
| missing_deps+=("jq") | |
| ;; | |
| esac | |
| fi | |
| if [[ ${#missing_deps[@]} -gt 0 ]]; then | |
| error_exit "Missing required dependencies: ${missing_deps[*]}" | |
| fi | |
| } | |
| # Detect operating system | |
| detect_platform() { | |
| local os | |
| os="$(uname -s)" | |
| case "$os" in | |
| Linux) | |
| echo "linux" | |
| ;; | |
| Darwin) | |
| echo "darwin" | |
| ;; | |
| *) | |
| error_exit "Unsupported operating system: $os" | |
| ;; | |
| esac | |
| } | |
| # Detect system architecture and convert to Go naming | |
| detect_architecture() { | |
| local arch | |
| arch="$(uname -m)" | |
| case "$arch" in | |
| x86_64) | |
| echo "amd64" | |
| ;; | |
| aarch64|arm64) | |
| echo "arm64" | |
| ;; | |
| armv6*|armv7*) | |
| echo "armv6l" | |
| ;; | |
| armv8*) | |
| echo "arm64" | |
| ;; | |
| i386|i686) | |
| echo "386" | |
| ;; | |
| *) | |
| error_exit "Unsupported architecture: $arch" | |
| ;; | |
| esac | |
| } | |
| # Check if system Go exists and warn user | |
| check_system_go() { | |
| local auto_yes="${1:-false}" | |
| if [[ -d "/usr/local/go" ]]; then | |
| warn "Go is already installed in /usr/local/go" | |
| echo "" | |
| echo "This script installs to $HOME/.local/go by default." | |
| echo "Both installations can coexist, but may cause confusion." | |
| echo "" | |
| echo "To remove system Go first:" | |
| echo " sudo rm -rf /usr/local/go" | |
| echo "" | |
| # Skip prompt if auto-yes or non-interactive | |
| if [[ "$auto_yes" == true ]] || [[ ! -t 0 ]]; then | |
| info "Auto-accepting: Proceeding with local installation..." | |
| return 0 | |
| fi | |
| read -r -p "Continue with local installation anyway? (y/N): " response | |
| case "$response" in | |
| [yY]|[yY][eE][sS]) | |
| info "Proceeding with local installation..." | |
| ;; | |
| *) | |
| info "Installation cancelled." | |
| exit 0 | |
| ;; | |
| esac | |
| fi | |
| } | |
| # Check if $HOME/.local/go/bin is in PATH | |
| check_local_bin_in_path() { | |
| if [[ ":$PATH:" != *":$HOME/.local/go/bin:"* ]]; then | |
| warn "\$HOME/.local/go/bin is not in your \$PATH" | |
| echo "" | |
| echo "Go will be installed to $HOME/.local/go, but you won't be able to use it" | |
| echo "until you add the Go bin directory to your shell configuration file." | |
| echo "This will be done automatically at the end of installation." | |
| echo "" | |
| fi | |
| } | |
| # Get currently installed Go version | |
| get_installed_version() { | |
| if command -v go >/dev/null 2>&1; then | |
| go version 2>/dev/null | awk '{print $3}' | sed 's/go//' || echo "" | |
| else | |
| echo "" | |
| fi | |
| } | |
| # Get latest Go version and checksum from official API | |
| # Outputs: version|sha256 | |
| get_latest_version_and_checksum() { | |
| local platform="$1" | |
| local arch="$2" | |
| local api_response | |
| local version | |
| local sha256 | |
| local filename | |
| # Fetch API data | |
| api_response=$(curl -s "https://go.dev/dl/?mode=json" 2>/dev/null) | |
| if [[ -z "$api_response" ]]; then | |
| error_exit "Failed to fetch Go version information from API" | |
| fi | |
| # Get the first stable version and its checksum | |
| version=$(echo "$api_response" | jq -r '.[0].version' 2>/dev/null) | |
| if [[ -z "$version" || "$version" == "null" ]]; then | |
| error_exit "Failed to parse Go version from API response" | |
| fi | |
| # Find the matching file for our platform/arch | |
| filename="${version}.${platform}-${arch}.tar.gz" | |
| sha256=$(echo "$api_response" | jq -r \ | |
| ".[0].files[] | select(.filename==\"$filename\") | .sha256" 2>/dev/null) | |
| # Fallback: try other stable versions if checksum not found | |
| if [[ -z "$sha256" || "$sha256" == "null" ]]; then | |
| warn "Latest version ${version} not available for ${platform}-${arch}, trying fallback..." | |
| local versions_data | |
| versions_data=$(echo "$api_response" | jq -r \ | |
| '.[] | select(.stable==true) | "\(.version)|\(.files[] | select(.os=="'"$platform"'" and .arch=="'"$arch"'" and .kind=="archive") | .sha256)"' 2>/dev/null) | |
| while IFS='|' read -r ver chksum; do | |
| if [[ -n "$chksum" && "$chksum" != "null" ]]; then | |
| version="$ver" | |
| sha256="$chksum" | |
| break | |
| fi | |
| done <<< "$versions_data" | |
| if [[ -z "$sha256" || "$sha256" == "null" ]]; then | |
| error_exit "Could not find available Go version with checksum for ${platform}-${arch}" | |
| fi | |
| fi | |
| echo "${version}|${sha256}" | |
| } | |
| # Download Go package | |
| download_go() { | |
| local url="$1" | |
| local dest="$2" | |
| info "Downloading $(basename "$dest")..." | |
| if command -v wget >/dev/null 2>&1; then | |
| wget -q --show-progress "$url" -O "$dest" || return 1 | |
| elif command -v curl >/dev/null 2>&1; then | |
| curl -# -L -o "$dest" "$url" || return 1 | |
| else | |
| error_exit "Neither wget nor curl is available" | |
| fi | |
| info "Download complete" | |
| return 0 | |
| } | |
| # Verify SHA256 checksum of downloaded file | |
| verify_checksum() { | |
| local file="$1" | |
| local expected_sha256="$2" | |
| local actual_sha256 | |
| info "Verifying checksum..." | |
| # Use appropriate SHA256 tool based on OS | |
| if command -v sha256sum >/dev/null 2>&1; then | |
| actual_sha256=$(sha256sum "$file" | awk '{print $1}') | |
| elif command -v shasum >/dev/null 2>&1; then | |
| actual_sha256=$(shasum -a 256 "$file" | awk '{print $1}') | |
| else | |
| warn "No SHA256 tool found (sha256sum/shasum), skipping checksum verification" | |
| return 0 | |
| fi | |
| if [[ "$actual_sha256" != "$expected_sha256" ]]; then | |
| error_exit "Checksum verification failed! | |
| Expected: $expected_sha256 | |
| Actual: $actual_sha256" | |
| fi | |
| info "Checksum verified successfully" | |
| return 0 | |
| } | |
| # Detect shell and return RC file path | |
| get_shell_rc() { | |
| # Check current shell | |
| if [[ -n "${ZSH_VERSION:-}" ]] || [[ "$SHELL" == *"zsh"* ]]; then | |
| echo "$HOME/.zshrc" | |
| elif [[ -n "${BASH_VERSION:-}" ]] || [[ "$SHELL" == *"bash"* ]]; then | |
| echo "$HOME/.bashrc" | |
| else | |
| warn "Unknown shell: $SHELL" | |
| echo "$HOME/.profile" | |
| fi | |
| } | |
| # Print shell configuration instructions (does not modify files) | |
| print_shell_config_instructions() { | |
| local go_root="$1" | |
| local shell_rc | |
| shell_rc="$(get_shell_rc)" | |
| # Check if already configured | |
| if grep -q "$go_root" "$shell_rc" 2>/dev/null; then | |
| info "Go appears to already be configured in $shell_rc" | |
| return 0 | |
| fi | |
| echo "" | |
| info "Shell configuration required" | |
| echo "" | |
| echo "Add the following to your $shell_rc:" | |
| echo "" | |
| echo "# Go programming language" | |
| echo "export GOROOT=\"$go_root\"" | |
| echo "export GOPATH=\"\${GOPATH:-\$HOME/go}\"" | |
| echo "export PATH=\"\$GOROOT/bin:\$GOPATH/bin:\$PATH\"" | |
| echo "" | |
| echo "Then reload your shell:" | |
| echo " source $shell_rc" | |
| echo "" | |
| } | |
| # Install Go development tools | |
| install_go_tools() { | |
| info "Installing Go development tools..." | |
| if ! command -v go >/dev/null 2>&1; then | |
| error_exit "Go is not installed or not in PATH. Install Go first." | |
| fi | |
| local tools_url="https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/f45048da7b29d9037579bbf726db1ff39cfaeef3/tools.go" | |
| local temp_file="/tmp/go-tools.go" | |
| if curl -fLo "$temp_file" "$tools_url" 2>/dev/null; then | |
| go run "$temp_file" || warn "Some tools may have failed to install" | |
| rm -f "$temp_file" | |
| info "Go tools installation complete" | |
| else | |
| error_exit "Failed to download tools installer" | |
| fi | |
| } | |
| #------------------------------------------------------------------------------ | |
| # Main Installation Function | |
| #------------------------------------------------------------------------------ | |
| install_go() { | |
| local force=false | |
| local system_install=false | |
| local with_tools=false | |
| local only_tools=false | |
| local auto_yes=false | |
| # Parse command line arguments | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| -h|--help) | |
| show_help | |
| ;; | |
| -f|--force) | |
| force=true | |
| shift | |
| ;; | |
| -y|--yes) | |
| auto_yes=true | |
| shift | |
| ;; | |
| --system-install) | |
| system_install=true | |
| shift | |
| ;; | |
| --with-tools) | |
| with_tools=true | |
| shift | |
| ;; | |
| --only-tools) | |
| only_tools=true | |
| shift | |
| ;; | |
| *) | |
| error_exit "Unknown option: $1. Use --help for usage information." | |
| ;; | |
| esac | |
| done | |
| # Handle --only-tools flag | |
| if [[ "$only_tools" == true ]]; then | |
| install_go_tools | |
| exit 0 | |
| fi | |
| # Check dependencies | |
| check_dependencies | |
| # Determine installation directory | |
| local goroot | |
| local use_sudo=false | |
| if [[ "$system_install" == true ]]; then | |
| goroot="/usr/local/go" | |
| use_sudo=true | |
| info "Installing to system directory: $goroot" | |
| else | |
| goroot="${GOROOT:-$HOME/.local/go}" | |
| info "Installing to user directory: $goroot" | |
| # Check if system Go exists (only for local install) | |
| check_system_go "$auto_yes" | |
| # Verify ~/.local/go/bin is in PATH | |
| check_local_bin_in_path | |
| fi | |
| # Set GOPATH if not already set | |
| export GOPATH="${GOPATH:-$HOME/go}" | |
| # Detect platform and architecture | |
| local platform | |
| local arch | |
| platform="$(detect_platform)" | |
| arch="$(detect_architecture)" | |
| local platform_arch="${platform}-${arch}" | |
| info "Detected platform: $platform_arch" | |
| # Get version information | |
| local installed_version | |
| local version_data | |
| local latest_version | |
| local expected_sha256 | |
| installed_version="$(get_installed_version)" | |
| info "Fetching latest Go version..." | |
| version_data="$(get_latest_version_and_checksum "$platform" "$arch")" | |
| latest_version="${version_data%%|*}" | |
| expected_sha256="${version_data##*|}" | |
| info "Latest Go version: ${latest_version#go}" | |
| if [[ -n "$installed_version" ]]; then | |
| info "Installed Go version: $installed_version" | |
| fi | |
| # Check if update is needed | |
| if [[ "$force" == false ]] && [[ -n "$installed_version" ]] && [[ "$installed_version" == "${latest_version#go}" ]]; then | |
| info "Latest Go version ($installed_version) is already installed" | |
| if [[ "$with_tools" == true ]]; then | |
| install_go_tools | |
| fi | |
| exit 0 | |
| fi | |
| # Prepare download | |
| local go_package="${latest_version}.${platform_arch}.tar.gz" | |
| local download_url="https://dl.google.com/go/$go_package" | |
| local temp_dir | |
| temp_dir="$(mktemp -d)" | |
| local temp_file="$temp_dir/$go_package" | |
| # Set up cleanup trap | |
| trap "rm -rf \"$temp_dir\"" EXIT | |
| # Download Go | |
| if ! download_go "$download_url" "$temp_file"; then | |
| error_exit "Failed to download Go package" | |
| fi | |
| # Verify checksum | |
| if ! verify_checksum "$temp_file" "$expected_sha256"; then | |
| error_exit "Checksum verification failed" | |
| fi | |
| # Prepare installation directory | |
| info "Preparing installation directory: $goroot" | |
| if [[ -d "$goroot" ]]; then | |
| if [[ "$use_sudo" == true ]]; then | |
| sudo rm -rf "$goroot" || error_exit "Failed to remove old installation" | |
| else | |
| rm -rf "$goroot" || error_exit "Failed to remove old installation" | |
| fi | |
| fi | |
| # Create parent directory if needed | |
| local parent_dir | |
| parent_dir="$(dirname "$goroot")" | |
| if [[ ! -d "$parent_dir" ]]; then | |
| if [[ "$use_sudo" == true ]]; then | |
| sudo mkdir -p "$parent_dir" || error_exit "Failed to create directory: $parent_dir" | |
| else | |
| mkdir -p "$parent_dir" || error_exit "Failed to create directory: $parent_dir" | |
| fi | |
| fi | |
| # Extract Go | |
| info "Extracting Go to $goroot..." | |
| if [[ "$use_sudo" == true ]]; then | |
| if ! sudo tar -C "$parent_dir" -xzf "$temp_file"; then | |
| error_exit "Failed to extract Go" | |
| fi | |
| else | |
| if ! tar -C "$parent_dir" -xzf "$temp_file"; then | |
| error_exit "Failed to extract Go" | |
| fi | |
| # For user install, handle the extracted 'go' directory | |
| if [[ "$goroot" != "$parent_dir/go" ]] && [[ -d "$parent_dir/go" ]]; then | |
| mv "$parent_dir/go" "$goroot" || error_exit "Failed to move Go to final location" | |
| fi | |
| fi | |
| info "Go installation complete!" | |
| # Print shell configuration instructions | |
| print_shell_config_instructions "$goroot" | |
| # Verify installation | |
| if [[ -x "$goroot/bin/go" ]]; then | |
| local new_version | |
| new_version="$("$goroot/bin/go" version | awk '{print $3}' | sed 's/go//')" | |
| info "Installed Go version: $new_version" | |
| fi | |
| # Install tools if requested | |
| if [[ "$with_tools" == true ]]; then | |
| # Temporarily add to PATH for tools installation | |
| export PATH="$goroot/bin:$PATH" | |
| install_go_tools | |
| fi | |
| echo "" | |
| info "Installation successful!" | |
| echo "" | |
| } | |
| #------------------------------------------------------------------------------ | |
| # Script Entry Point | |
| #------------------------------------------------------------------------------ | |
| install_go "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Copyright 2021 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Binary installtools is a helper that installs Go tools extension tests depend on. | |
| package main | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "os" | |
| "os/exec" | |
| "path" | |
| "path/filepath" | |
| "runtime" | |
| "strings" | |
| ) | |
| // finalVersion encodes the fact that the specified tool version | |
| // is the known last version that can be buildable with goMinorVersion. | |
| type finalVersion struct { | |
| goMinorVersion int | |
| version string | |
| } | |
| var tools = []struct { | |
| path string | |
| dest string | |
| preferPreview bool | |
| // versions is a list of supportedVersions sorted by | |
| // goMinorVersion. If we want to pin a tool's version | |
| // add a fake entry with a large goMinorVersion | |
| // value and the pinned tool version as the last entry. | |
| // Nil of empty list indicates we can use the `latest` version. | |
| versions []finalVersion | |
| }{ | |
| // TODO: auto-generate based on allTools.ts.in. | |
| {"golang.org/x/tools/gopls", "", true, nil}, | |
| {"github.com/acroca/go-symbols", "", false, nil}, | |
| {"github.com/cweill/gotests/gotests", "", false, nil}, | |
| {"github.com/davidrjenni/reftools/cmd/fillstruct", "", false, nil}, | |
| {"github.com/haya14busa/goplay/cmd/goplay", "", false, nil}, | |
| {"github.com/stamblerre/gocode", "gocode-gomod", false, nil}, | |
| {"github.com/mdempsky/gocode", "", false, nil}, | |
| {"github.com/ramya-rao-a/go-outline", "", false, nil}, | |
| {"github.com/rogpeppe/godef", "", false, nil}, | |
| {"github.com/sqs/goreturns", "", false, nil}, | |
| {"github.com/uudashr/gopkgs/v2/cmd/gopkgs", "", false, nil}, | |
| {"github.com/zmb3/gogetdoc", "", false, nil}, | |
| {"honnef.co/go/tools/cmd/staticcheck", "", false, []finalVersion{{16, "v0.2.2"}, {18, "v0.3.3"}}}, | |
| {"golang.org/x/tools/cmd/gorename", "", false, nil}, | |
| {"github.com/go-delve/delve/cmd/dlv", "", false, []finalVersion{{16, "v1.8.3"}, {17, "v1.9.1"}}}, | |
| } | |
| // pickVersion returns the version to install based on the supported | |
| // version list. | |
| func pickVersion(goMinorVersion int, versions []finalVersion, defaultVersion string) string { | |
| for _, v := range versions { | |
| if goMinorVersion <= v.goMinorVersion { | |
| return v.version | |
| } | |
| } | |
| return defaultVersion | |
| } | |
| func main() { | |
| ver, err := goVersion() | |
| if err != nil { | |
| exitf("failed to find go version: %v", err) | |
| } | |
| if ver < 1 { | |
| exitf("unsupported go version: 1.%v", ver) | |
| } | |
| bin, err := goBin() | |
| if err != nil { | |
| exitf("failed to determine go tool installation directory: %v", err) | |
| } | |
| err = installTools(bin, ver) | |
| if err != nil { | |
| exitf("failed to install tools: %v", err) | |
| } | |
| } | |
| func exitf(format string, args ...interface{}) { | |
| fmt.Fprintf(os.Stderr, format, args...) | |
| os.Exit(1) | |
| } | |
| // goVersion returns an integer N if go's version is 1.N. | |
| func goVersion() (int, error) { | |
| cmd := exec.Command("go", "list", "-e", "-f", `{{context.ReleaseTags}}`, "--", "unsafe") | |
| cmd.Env = append(os.Environ(), "GO111MODULE=off") | |
| out, err := cmd.Output() | |
| if err != nil { | |
| return 0, fmt.Errorf("go list error: %v", err) | |
| } | |
| result := string(out) | |
| if len(result) < 3 { | |
| return 0, fmt.Errorf("bad ReleaseTagsOutput: %q", result) | |
| } | |
| // Split up "[go1.1 go1.15]" | |
| tags := strings.Fields(result[1 : len(result)-2]) | |
| for i := len(tags) - 1; i >= 0; i-- { | |
| var version int | |
| if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil { | |
| continue | |
| } | |
| return version, nil | |
| } | |
| return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags) | |
| } | |
| // goBin returns the directory where the go command will install binaries. | |
| func goBin() (string, error) { | |
| if gobin := os.Getenv("GOBIN"); gobin != "" { | |
| return gobin, nil | |
| } | |
| out, err := exec.Command("go", "env", "GOPATH").Output() | |
| if err != nil { | |
| return "", err | |
| } | |
| gopaths := filepath.SplitList(strings.TrimSpace(string(out))) | |
| if len(gopaths) == 0 { | |
| return "", fmt.Errorf("invalid GOPATH: %s", out) | |
| } | |
| return filepath.Join(gopaths[0], "bin"), nil | |
| } | |
| func installTools(binDir string, goMinorVersion int) error { | |
| installCmd := "install" | |
| if goMinorVersion < 16 { | |
| installCmd = "get" | |
| } | |
| dir := "" | |
| if installCmd == "get" { // run `go get` command from an empty directory. | |
| dir = os.TempDir() | |
| } | |
| env := append(os.Environ(), "GO111MODULE=on") | |
| for _, tool := range tools { | |
| ver := pickVersion(goMinorVersion, tool.versions, pickLatest(tool.path, tool.preferPreview)) | |
| path := tool.path + "@" + ver | |
| cmd := exec.Command("go", installCmd, path) | |
| cmd.Env = env | |
| cmd.Dir = dir | |
| fmt.Println("go", installCmd, path) | |
| if out, err := cmd.CombinedOutput(); err != nil { | |
| return fmt.Errorf("installing %v: %s\n%v", path, out, err) | |
| } | |
| loc := filepath.Join(binDir, binName(tool.path)) | |
| if tool.dest != "" { | |
| newLoc := filepath.Join(binDir, binName(tool.dest)) | |
| if err := os.Rename(loc, newLoc); err != nil { | |
| return fmt.Errorf("copying %v to %v: %v", loc, newLoc, err) | |
| } | |
| loc = newLoc | |
| } | |
| fmt.Println("\tinstalled", loc) | |
| } | |
| return nil | |
| } | |
| func binName(toolPath string) string { | |
| b := path.Base(toolPath) | |
| if runtime.GOOS == "windows" { | |
| return b + ".exe" | |
| } | |
| return b | |
| } | |
| func pickLatest(toolPath string, preferPreview bool) string { | |
| if !preferPreview { | |
| return "latest" // should we pick the pinned version in allTools.ts.in? | |
| } | |
| out, err := exec.Command("go", "list", "-m", "--versions", toolPath).Output() | |
| if err != nil { | |
| exitf("failed to find a suitable version for %q: %v", toolPath, err) | |
| } | |
| versions := bytes.Split(out, []byte(" ")) | |
| if len(versions) == 0 { | |
| exitf("failed to find a suitable version for %q: %s", toolPath, out) | |
| } | |
| return string(bytes.TrimSpace(versions[len(versions)-1])) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment