APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

goenvとdirenvでGo Modules有無両対応な開発環境を作る

はじめに

先進サービス開発事業部の山岡です。

Go ModulesはGo1.11から実験的にサポートされ1.13から正式ツール(デフォルトで有効)となる予定である依存関係管理機能です *1 。これは export GO111MODULE=on することで使えるようになり、詳細は省きますが今まで主流だった dep と比較すると

  • GOPATHの外で作業ができるようになる
  • Semantic Versioning *2 なGitタグとMinimal Version Selection *3に基づいた管理の仕組みでビルド再現性が高い

等といった点が主要なメリットになります。

関わるプロジェクト全てがGo Modulesに一本化されていれば何の問題も無いのですが、当然そうは行かないケースが大半でありその場合はプロジェクトによって環境変数や言語バージョンを切り替えたりと色々な面倒が起きます(言語バージョンはGo Modulesに限った話ではありませんが)。自分の開発環境がこの状態になりどうすれば諸々良い具合にできるのか探った結果goenvとdirenvで上手く管理する形に落ち着いたのでここに手順を残します。

Go本体の管理ツール

まずGo言語本体のバージョン管理ツールから。これには goenv を使います。他のプログラミング言語でもよくあるタイプのツールで、デフォルトで使用するバージョンと特定のディレクトリ以下で使用するバージョンを個別に設定することができます。なのでプロジェクトAではGo1.9を、プロジェクトBでは1.12を、というようなことがディレクトリを移動するだけで自動的にできるようになります。

なお前提の環境は以下の通りですがLinux系であれば基本的に同様のやり方ができるかと思います。また、既に手でGo言語をインストールしてある場合は各種環境変数等を含め削除しておいて下さい。

$ cat /etc/lsb-release 
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"

goenvのインストールと確認

まずはgoenvをインストールします。GOROOTはgoenvに自動管理してもらいますが、GOPATHについてはGo Modules非対応の開発にも対応するため export GOENV_DISABLE_GOPATH=1 で固定します。これをしないとバージョン毎に別の$HOME/.goenv配下GOPATHディレクトリで作業するハメになってしまうので非常に面倒です。

$ git clone https://github.com/syndbg/goenv.git $HOME/.goenv
$ echo 'export GOENV_DISABLE_GOPATH=1' >> ~/.bash_profile
$ echo 'export GOENV_ROOT="$HOME/.goenv"' >> ~/.bash_profile
$ echo 'export PATH="$GOENV_ROOT/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(goenv init -)"' >> ~/.bash_profile
$ source ~/.bash_profile
$ goenv -v
goenv 2.0.0beta10

Goの設定とデフォルトバージョンの設定

ここではGOPATHをホームディレクトリに設定していますが、お好みで変更しても問題ありません。

$ echo 'export GO111MODULE=on' >> ~/.bash_profile
$ echo 'export GOPATH="$HOME"' >> ~/.bash_profile
$ echo 'export PATH="$GOPATH/bin:$PATH"' >> ~/.bash_profile
$ goenv install -l
Available versions:
  1.2.2
  1.3.0
(中略)
  1.12.4
  1.12.5
$ goenv install 1.12.5
Downloading go1.12.5.linux-amd64.tar.gz...
-> https://dl.google.com/go/go1.12.5.linux-amd64.tar.gz
Installing Go Linux 64bit 1.12.5...
Installed Go Linux 64bit 1.12.5 to /home/username/.goenv/versions/1.12.5
$ goenv global 1.12.5
$ goenv rehash

完了後以下のようになっていればOKです。

$ go version
go version go1.12.5 linux/amd64
$ which go
/home/username/.goenv/shims/go

基本的な使い方

  • goenv install -l
    • インストール可能なバージョンを表示する
  • goenv install x.y.z
    • 指定したバージョンのGoをインストールする(これだけでは切り替わらない)
    • 例えば1.12.5をインストールする場合は goenv install 1.12.5
  • goenv local x.y.z
    • 現在いるディレクトリ配下で指定したバージョンを使用する
    • 設定されたバージョン情報は .go-version が生成されそこに記録される
  • goenv global x.y.z
    • goenv local で設定した場所以外で使用するデフォルトのバージョンを指定する
  • goenv rehash
    • 長くなるので割愛しますが新しいバージョンをインストールしたり「バージョン切り替わりがおかしい」と思ったらとりあえずこれ叩きましょう

環境変数の管理ツール

既に方々で紹介されているのでご存知の方も多いと思いますが、特定のディレクトリ配下で環境変数を自動的に設定してくれるツール direnv を使います。インストール手順は特に難しいことはなく、GitHubのReleasesからバイナリーをダウンロードしてPATHが通っている場所に置きevalを設定するだけです。

$ curl -LO https://github.com/direnv/direnv/releases/download/v2.20.0/direnv.linux-amd64
$ sudo mv direnv.linux-amd64 /usr/local/bin/direnv
$ sudo chmod 755 /usr/local/bin/direnv
$ echo 'eval "$(direnv hook bash)"' >> .bash_profile
$ source ~/.bash_profile
$ direnv version
2.20.0

各プロジェクトでの設定

いずれもプロジェクトのルートディレクトリで作業していることが前提です。

Go Modulesを使用する場合

Go1.11.5をインストールして使用する場合を想定します。インストール済みの場合は goenv install は省いてOK。

$ goenv install 1.11.5
$ goenv local 1.11.5
$ goenv rehash

既に GO111MODULE=on が設定されているのでこれだけでOKです。この記事には記載しませんが、あとは標準的なGo Modulesの使い方で大丈夫です。

Go Modulesを使用しない場合

Go Modules以外の部分は↑と同じとします。

$ goenv install 1.11.5
$ goenv local 1.11.5
$ goenv rehash
$ echo "export GO111MODULE=off" > .envrc
$ direnv allow
direnv: loading .envrc
direnv: export +GO111MODULE

direnv allow することで同じディレクトリにある .envrc が適用されGo Modulesが無効になります。今後はこのディレクトリに入ると自動的にGo Modulesが無効になり、出れば再度有効にすることができ便利です。

補足: 支援ツールやエディタの挙動がおかしい場合

Go ModulesではGOPATHの外でも各ライブラリや自分のローカルパッケージを参照することができますが、Goの支援ツールでGOPATH配下でないと動かないものがあります。

対応状況一覧

この影響により例えばVS CodeではFind All Referencesでパッケージ外の参照ができない等の不具合が起こるので *4 Go Modulesへ変更後に挙動がおかしい場合は確認してみましょう。