Last active
April 22, 2023 11:20
-
-
Save alexjoedt/9c61f9cc4ce211430257b2febd68be9f to your computer and use it in GitHub Desktop.
This file contains 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 | |
function install_go_tools { | |
if go version >/dev/null 2>&1 && curl -fLo "/tmp/go-tools.go" https://gist.githubusercontent.com/alexjoedt/9c61f9cc4ce211430257b2febd68be9f/raw/f45048da7b29d9037579bbf726db1ff39cfaeef3/tools.go; then | |
go run "/tmp/go-tools.go" | |
fi | |
} | |
function install_go { | |
if [[ "$1" == "--only-tools" ]]; then | |
echo "Installing only Go-Tools..." | |
install_go_tools | |
exit 0 | |
fi | |
[ -z "$GOROOT" ] && GOROOT="/usr/local/go" | |
[ -z "$GOPATH" ] && GOPATH="$HOME/go" | |
force="false" | |
if [ "$1" == "-f" ]; then | |
force="true" | |
fi | |
OS="$(uname -s)" | |
ARCH="$(uname -m)" | |
case $OS in | |
"Linux") | |
if ! sudo apt-get -qq install jq -y >/dev/null 2>&1; then | |
echo "failed to install jq. Abort." | |
exit 1 | |
fi | |
case $ARCH in | |
"x86_64") | |
ARCH=amd64 | |
;; | |
"aarch64") | |
ARCH=arm64 | |
;; | |
"armv6" | "armv7l") | |
ARCH=armv6l | |
;; | |
"armv8") | |
ARCH=arm64 | |
;; | |
.*386.*) | |
ARCH=386 | |
;; | |
esac | |
PLATFORM="linux-$ARCH" | |
;; | |
"Darwin") | |
if ! command -v brew >/dev/null 2>&1; then | |
echo "please install brew and re-run the script" | |
exit 1 | |
fi | |
if ! command -v jq >/dev/null 2>&1; then | |
brew install jq >/dev/null 2>&1 | |
fi | |
case $ARCH in | |
"x86_64") | |
ARCH=amd64 | |
;; | |
"arm64") | |
ARCH=arm64 | |
;; | |
esac | |
PLATFORM="darwin-$ARCH" | |
;; | |
esac | |
if [ -z "$PLATFORM" ]; then | |
echo "Your operating system is not supported by this script." | |
exit 1 | |
fi | |
GOLANG_INSTALLED="" | |
if go version >/dev/null 2>&1; then | |
GOLANG_INSTALLED=$(go version | { | |
read -r _ _ v _ | |
echo "${v#go}" | |
} | cut -d. -f1,2,3) | |
fi | |
echo "Fetch latest Go version...." | |
GOLANG_LATEST=$(curl -s "https://go.dev/dl/?mode=json" | jq -r '.[0].version') | |
GO_PACKAGE="${GOLANG_LATEST}.${PLATFORM}.tar.gz" | |
if [ $force = "false" ] && [ "$GOLANG_INSTALLED" == "$(echo "${GOLANG_LATEST#go}" | cut -d. -f1,2,3)" ]; then | |
echo "Latest Go version ($GOLANG_INSTALLED) already installed...." | |
exit | |
fi | |
TEMP_DIRECTORY=$(mktemp -d) | |
echo "Downloading $GO_PACKAGE ..." | |
if wget "https://storage.googleapis.com/golang/$GO_PACKAGE" -O "$TEMP_DIRECTORY/$GO_PACKAGE"; then | |
echo "download complete" | |
elif curl -o "$TEMP_DIRECTORY/$GO_PACKAGE" "https://storage.googleapis.com/golang/$GO_PACKAGE"; then | |
echo "Download complete" | |
else | |
echo "Cant download Go package. Aborting." | |
echo "Please install 'wget' or 'curl' and try again." | |
exit 1 | |
fi | |
if [ $? -ne 0 ]; then | |
echo "Download failed! Exiting." | |
exit 1 | |
fi | |
echo "Create Go root directory: $GOROOT" | |
test -d "$GOROOT" && sudo rm -rf "$GOROOT" | |
test -d "$GOROOT" || sudo mkdir -p "$GOROOT" | |
echo "Extracting Go to $GOROOT" | |
if ! sudo tar -C "$(dirname $GOROOT)" -xzf "$TEMP_DIRECTORY/$GO_PACKAGE"; then | |
echo "Failed to extract go to $GOROOT" | |
exit 1 | |
fi | |
rm -rf "$TEMP_DIRECTORY" | |
if [ -n "$($SHELL -c 'echo $ZSH_VERSION')" ]; then | |
shell_rc="$HOME/.zshrc" | |
elif [ -n "$($SHELL -c 'echo $BASH_VERSION')" ]; then | |
shell_rc="$HOME/.bashrc" | |
else | |
echo "Your shell is not supported within this script." | |
echo "Make sure to export $GOROOT in your PATH." | |
exit | |
fi | |
test -f "$shell_rc" || touch "$shell_rc" | |
if ! grep -q "/usr/local/go/bin" "$shell_rc"; then | |
{ | |
echo '# Golang' | |
echo 'if [ -d /usr/local/go/bin ]; then' | |
echo -e '\texport PATH=$PATH:/usr/local/go/bin' | |
echo -e '\texport PATH=$PATH:$(go env GOPATH)/bin' | |
echo 'fi' | |
echo '' | |
} >>"$shell_rc" | |
echo "Export Go root in $shell_rc" | |
source "$shell_rc" | |
else | |
echo "Go path alredy exported in $shell_rc" | |
fi | |
install_go_tools | |
} | |
install_go $1 |
This file contains 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