🔨

GOのバージョン管理を公式ツールに合わせて自作する

2024/11/14に公開

はじめに

今までメインエディタはgoland、goのバージョン管理にはgvmを使っていました。

メインエディタをgolandからcursorに変更することをきっかけにgoのバージョン管理を既存のツールに頼らず、go公式に寄り添った形にした方がいいんじゃないか?と思ったのがきっかけで色々いじくりまわした結果、自作のツールでバージョン管理するに至るということをまとめています。

世間の情報も結構少なさそうで紆余曲折の中で出来ない・煩わしいと思っていることが実はそうではないということもあるかと思いますが、自分の場合はこうしたという目で見てもらえると助かります。

既存ツールをなんで使わないのか?

全てのツール共通で「設定が煩わしい」の一言になります。

それに加えて各ツールに言いたいことがあるという感じになりました。

goenv

かなり有名なバージョン管理ツールで、使い方もかなり簡単で設定ファイルの汚染もほとんどなくて最高だなって思ってたのですが、vscodeのデバッグ実行時にアーキテクチャにあったものをインストールし直してくださいという趣旨で実行できないという問題が出てきたということが問題になってきました。

Failed to launch: could not launch process:
can not run under Rosetta,
check that the installed build of Go is right for your CPU architecture

今利用しているmacのアーキテクチャはarmで、goenvでインストールできるものはamdでした。

できる限りで調べてみたのですが、arm用のgoがインストールできなかったのでボツになりました。

もしアーキテクチャの指定ができるならgoenvを使うか?と思ってはみたのですが、そもそもアーキテクチャを意識してツールを使うのが便利なのか?とか感じてしまったのでアーキテクチャ指定ができたとしても使わないと思います。

gvm

これも有名なバージョン管理ツールなのですが、これの一番のネックが様々なファイルにgvm設定の記述が分散され、いざ使わないとなった時の完全アンインストールが面倒すぎたという理由で使わないことにしました。

それに加えて、setting.jsonやlaunch.jsonにgvmのGOROOTを記述しないと実行・デバッグボタンがうまく使えないというのも、ディレクトリごとにgoのバージョンが違うし、なんなら結構な頻度でgo.modのgoバージョンも変更されるという感じなのでこの記述の追従も面倒と感じてきて使うのをやめました。

go公式

最終的にこれに合わせることにしました。

1番の決め手は公式だから。安心・安全、しかも上記の通りアーキテクチャも考える必要もないし、設定ファイルの汚染もない。やることはGOPATHとGOROOTの設定くらいなので利用方法もかなりミニマムで済むのもいいかなと思いました。

ただ、公式のものだけだと使い勝手がそれほど良くないと思ったので、自作したスクリプトはそれを補完する形になってます。

その他

awesome goのサイトを見てはみたものの、そもそもgoのバージョン管理ツール自体が少ないし、あったとしてもスターがついてないとか、インストールに別のツールが必要とか、いまいち好きになれないという理由でやめました。

あと、たとえばディレクトリごとにローカル設定のファイルが増えるというパターンのツールもありますが、余計な記述を増やしたくないのに余計なファイルを増やしていいのか?という自問自答をして、そうゆうものも使うのをやめました。

目標

とにかくやりたいのはその時必要なgoのバージョンをパパッと自分勝手に変えたいだけ、つまりGOROOTのコントロールをサッとやりたいだけです。

ある意味かなりシンプルな衝動なので、go公式を自分勝手に味付けすればOKという感じになりました。

スクリプト作成

スクリプトの概要

以下の2つの関数を含むシェルスクリプトを作成します。

  • ginstall: Goの特定バージョンをインストールする関数
  • gv: 指定されたGoバージョンに切り替える関数

ざっくりいうと公式のインストールコマンドをginstallというコマンドに落とし込んで作成、gvというコマンドでその時つかうgoのバージョンをGOROOTに設定するというものになります。

実装

.zshgo ファイルの作成

各種goに関係するコマンド達は以下の内容で.zshgoファイルに作成します。
なんとなく .zshrc に直接書きたくなかったのでファイルを分けていますが、そうでもない問い場合は直接 .zshrc に記述してもOKです。

# ~/.zshgo

# Go version installer
ginstall() {
    local version=$1
    
    if [ -z "$version" ]; then
        echo "Usage: ginstall <version>"
        return 1
    fi

    # Check if the version is available
    if ! go install "golang.org/dl/go$version@latest" &>/dev/null; then
        echo "Error: Go version $version is not available for installation."
        return 1
    fi

    # Install the go binary for the specified version
    echo "Installing go$version..."
    go install "golang.org/dl/go$version@latest"

    # Download the Go SDK for the specified version
    echo "Downloading Go SDK version $version..."
    if ! "go$version" download; then
        echo "Error: Failed to download Go SDK version $version."
        return 1
    fi

    # Set up GOROOT for the new version
    local new_goroot="$HOME/sdk/go$version"
    export GOROOT="$new_goroot"
    export PATH="$GOROOT/bin:$PATH"

    echo "Go $version has been installed and set as the current version."
    go version
}

# Go version switcher
gv() {
    local version=$1
    local sdk_dir="$HOME/sdk"
    local new_goroot="$sdk_dir/go$version"

    if [ ! -d "$new_goroot" ]; then
        echo "Go version $version is not installed. Attempting to install..."
        if ginstall "$version"; then
            echo "Successfully installed and switched to Go $version"
        else
            echo "Failed to install Go $version"
            return 1
        fi
    else
        export GOROOT="$new_goroot"
        export PATH="$GOROOT/bin:$PATH"
        echo "Switched to Go $version"
        go version
    fi
}

.zshrc.zshgo を読み込み

次に、.zshrcファイルを編集して、.zshgoファイルを読み込むようにします。

# ~/.zshrc

# Load Go version management functions
if [ -f "$HOME/.zshgo" ]; then
    source "$HOME/.zshgo"
fi

実装はこれで終わりです。

使い方

実際に使った時のログがこちらです。

> ginstall 1.23.1
Installing go1.23.1...
Downloading Go SDK version 1.23.1...
Downloaded   0.0% (    3121 / 71612504 bytes) ...
Downloaded   8.8% ( 6294523 / 71612504 bytes) ...
Downloaded  12.9% ( 9256896 / 71612504 bytes) ...
Downloaded  23.4% (16760720 / 71612504 bytes) ...
Downloaded  32.5% (23281488 / 71612504 bytes) ...
Downloaded  41.7% (29835040 / 71612504 bytes) ...
Downloaded  49.0% (35110656 / 71612504 bytes) ...
Downloaded  58.8% (42139328 / 71612504 bytes) ...
Downloaded  67.8% (48529040 / 71612504 bytes) ...
Downloaded  77.1% (55246432 / 71612504 bytes) ...
Downloaded  86.5% (61914672 / 71612504 bytes) ...
Downloaded  94.0% (67321344 / 71612504 bytes) ...
Downloaded 100.0% (71612504 / 71612504 bytes)
Unpacking /Users/tk/sdk/go1.23.1/go1.23.1.darwin-arm64.tar.gz ...
Success. You may now run 'go1.23.1'
Go 1.23.1 has been installed and set as the current version.
go version go1.23.1 darwin/arm64
> gv 1.23.1      
Switched to Go 1.23.1
go version go1.23.1 darwin/arm64
> go version
go version go1.23.1 darwin/arm64
> gv 1.23.3 
Switched to Go 1.23.3
go version go1.23.3 darwin/arm64

こんな感じで欲しいかったり使いたいバージョンを指定するだけでいいので、ほとんど既存ツールと使い勝手は変わらないです。

また、バージョンを変更する時にインストールされていない場合はインストールした上で変更するということもしています。

このコマンドを使う前提としてGOPATHの設定はしてください。

最後に

あまり考えたことはなかったのですが、使うエディタによってgoのバージョンを単純に変えたいというだけでも悩みが違ってくるものだなと思いました。

また、コマンドを自作してみるとほどんどのことはGOROOTを変えるだけで解決できるので、そこにどうアプローチしていくのがいいのか調べつつ、最終的には公式に寄り添った方がいいという結論に至るまでの調べ物が結構楽しかったです。

何かのお役に立てば幸いです。

Discussion