🔨

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がインストールできなかったため、使用を断念しました。

アーキテクチャの指定ができるとしても、そもそもアーキテクチャを意識してツールを使うことが便利なのかという疑問も感じたため、アーキテクチャ指定ができたとしても使用しないと思います。

gvm

これも有名なバージョン管理ツールですが、最大の問題点は様々なファイルにgvm設定の記述が分散され、使用しなくなった際の完全アンインストールが非常に面倒だということでした。

加えて、settings.jsonやlaunch.jsonにgvmのGOROOTを記述しないと実行・デバッグボタンがうまく使えないという点も、ディレクトリごとにGoのバージョンが異なり、さらにgo.modのGoバージョンも頻繁に変更されるため、この記述の追従も面倒に感じて使用をやめました。

Go公式

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

最大の決め手は公式であるということです。安心・安全であり、前述のようにアーキテクチャを考える必要もなく、設定ファイルの汚染もありません。必要な作業は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