※ 2017年10月に、anyenvで使用するgoenvは、syndbg/goenvに変更されています。
はじめに
goでGAEのプログラムを書くのって楽しいですよね。go, GAEに魅了されてしまった、saizzです。
最初は、右も左もわからず、とりあえず開発環境を作っていたのですが、今後、GAE/goのgoのバージョンが1.8に上がった時を考え、環境を見直しました。OSは、Ubuntuです。
やりたいこと
- appengine-go-sdkのバージョンをプロジェクト(のディレクトリ)毎にすみ分けできるようにする
現在の状況
/home/saizo/google/
├── go
├── gopath
├── appengine -> ./google-cloud-sdk/platform/google_appengine
└── google-cloud-sdk
こんな感じで、~/google
以下
- goディレクトリにgoのsdk
- gopathディレクトリにプロジェクト関係なくバイナリだけ利用するようなものをget getするgopath
- gcloudでappengine-go-sdkがあることに目新しさを感じ、gcloudでappengine-go-sdkを導入
としていました。
(GAE/goだけなら、goのsdkは必須ではないですよね。最初は知らなかった)
gcloud components update
でappengine-go-sdkをアップデートできるお手軽さはあるのですが、すみ分けはできないです。
goenvというものがあるらしい
qiitaでもいくつか記事が見つかります。更に調べてみると、いろんな人がgoenvを作っているのがわかります。
一部紹介すると、
goenv -gae
でGAEのapp.yaml
のファイルも作ってくれる。appengine-go-sdkは別途用意しなければいけなさそう
goenv install gae-x.x.x
でappengine-go-sdkをダウンロードでき、goと同じように管理できる
今回、ダウンロードに対応している、kaneshin/goenvを使うことにしました。
anyenvというものがあるらしい
更に_all in one for **env_という、色々な言語のバージョン切り替えツールをラップして設定の煩雑さを吸収したツールがあります。
goenvにも対応しており、kaneshin/goenvを使うようになっています。
anyenvでkaneshin/goenvを導入する
導入は簡単です。Ubuntuに導入したので、your_profileは.bashrc
です。
$ git clone https://github.com/riywo/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.your_profile
$ echo 'eval "$(anyenv init -)"' >> ~/.your_profile
$ exec $SHELL -l
$ anyenv install goenv
$ exec $SHELL -l
そして、導入可能な最新のgoを通常使うようにして、追加でgae-1.9.53を導入します。
$ goenv install 1.8.3
$ goenv global 1.8.3
$ goenv install gae-1.9.53
GAE/goのプロジェクトでは、gae-1.9.53を使用するよう指定します。
$ cd ~/workspace/gae-project
$ goenv local gae-1.9.53
カレントディレクトリに.go-version
というファイルが作られ、バージョン情報が書き込まれています。
これで、~/workspace/gae-project
配下のどこでもよいので、
$ goenv rehash
で、gae-1.9.53が使えるようになります。
また、上記以外のディレクトリでgoenv rehash
すると、globalが適用されて、go-1.8.3が使えるようになります。
ちょっと困った
goenvの仕組みは、以下のようです(他のgoenvも同じっぽそうです)。
-
~/.anyenv/envs/goenv/versions
に各バージョンのgo, appengine-go-sdkが格納される -
goenv rehash
で~/.anyenv/envs/goenv/shims
に指定バージョンのファイルがコピーされる - pathは、
~/.anyenv/envs/goenv/shims
に通っており、結果的に切り替えられる
あるシェル上でgoenv rehash
すると、他のシェルでも影響がでそうです。goenv shell
でそのシェルでのみ環境を切り替えられそうですが、この場合、gae-projectのディレクトリに結びつけはできなさそうです。
また、gae-1.9.53を使うようにしていると、goコマンドはpath上に存在しなくなります。
私は、エディタにemacs(細かくいうとspacemacs)を使っています。非常に強力ですが、go-modeの時にgoコマンドを要求してきます。
そこで、goenvはsdkのダウンロードマネージと、globalの指定のみとして、ディレクトリ毎で環境を切り替えるのは別の手段を取りました1。
direnvというものがあるらしい
bash, zsh, tcsh, fishに対応しており、ディレクトリの変更をhookして、環境をload, unloadする仕組みです。
Ubuntuのパッケージが用意されているので、インストールはapt一発です。
そして、hookするように.bashrc
に以下を追加します。
eval "$(direnv hook bash)"
emacsが要求する(笑)ので、1.6系のgoをダウンロード
$ goenv install 1.6.4
~/.direnvrc
を作成し、kaneshin/goenvで導入されているバージョンから、指定のバージョンの環境をpathに追加するスクリプトを書く。
# ~/.direnvrc
use_go() {
local version=$1
local found=0
while read line; do
name=${line}
if [ "${name}" = "${version}" ]; then
export GOROOT="${GOENV_ROOT}/versions/${name}"
path="${GOENV_ROOT}/versions/${name}/bin"
PATH_add ${path}
found=1
break
fi
done < <(goenv version-names | grep -v gae)
if [ "$found" -eq 0 ]; then
echo "go '${version}' is not installed." >&2
return 1
fi
return 0
}
use_gae() {
local version=$1
local found=0
while read line; do
name=$(echo ${line} | cut -d '-' -f 2)
if [ "${name}" = "${version}" ]; then
path="${GOENV_ROOT}/versions/gae-${name}"
PATH_add ${path}
found=1
break
fi
done < <(goenv version-names | grep gae)
if [ "$found" -eq 0 ]; then
echo "gae '${version}' is not installed." >&2
return 1
fi
return 0
}
(追記) use_goの方に、GOROOTの環境変数の設定を追加しました。
~/workspace/gae-project/.envrc
を作成し、hook時に行う処理を書く
# ~/workspace/gae-project/.envrc
use gae 1.9.53
use go 1.6.4
export GOPATH=$(pwd)
direnvは、bash以外にもzsh, tcsh, fishに対応していますが、.direnvrc
, .envrc
はbashで実行されるとのこと。
bashの文法で記述する必要があります。
また、.envrcにlayout go
と記載すると、その時点のgopathの先頭にカレントディレクトリを追加などしてくれます。
なので、gopathをカレントディレクトリだけにしたい場合、
unset GOPATH
layout go
としないといけません。わかりづらいので、layoutは使わず、export GOPATH=$(pwd)
としています。
作成したら、設定を反映します。
$ direnv allow .
もう少しシンプルな仕組みにしたかったけど、一先ず、やりたいことはできた。
最後に
やってみたけど、よくよく考えたら、deployする先のappengineはどんどんバージョンが上がっていくわけだから、プロジェクト毎にappengine-go-sdkのバージョンを固定する意味ってあまりないか。。バージョンの切り替えが簡単にできるってぐらいですかね。
大きく見直し(2018年10月追記)
GAE/goの場合、appengineのパッケージもdepなどのパッケージ管理ツールを使い、プロジェクト内のパッケージ配下のvendorディレクトリに含めてしまうことが多いです。
そうなると、~/.direnvrcに独自にスクリプト書いて切り替える必要はなく、
- goenvで使用するgoのバージョンを切り替え
- プロジェクトの.envrcは
- 環境変数GOPATHをプロジェクトルートに切り替える
- 環境変数GOROOTを参照するエディタ用に環境変数GOROOTを明示的に設定する
これだけになりました。
具体的には、
export GOROOT=$(go env GOROOT)
export GOPATH=$(pwd)
とだけになりました。
-
appengine-go-sdk内のgoappにシンボリックリンクでgoを作ってみたけど、そのgoは起動時にエラーになってしまい、goとappengine-go-sdkの両方をpath指定するように。 ↩