Edited at

gvm使ってCircleCIでGoのバージョン固定

More than 1 year has passed since last update.


目的


  • バージョンが勝手に変わると、アプリケーション動作の保障ができないので、任意のバージョンのgoをインスコして使う


対象環境


  • ビルド環境 (CircleCI)

  • 実行環境 (AWS EC2)

今回ビルド環境と実行環境を分ける予定であるため、その両方への作業が必要ですが、今回は、ひとつめの、CircleCIでの適用作業をします。


利用するライブラリ

gvm: https://github.com/moovweb/gvm


gvm使ってみる

gvmはgolangのバージョン管理ライブラリです。

あまり気に入ってないけど、他にいいやつあったらおしえてほしいです。

自分のローカルで先にやっても良かったのですが、まだVM環境になってないので、CicleCIのコンテナでためしました。

CircleCIはデバッグ用途で、30min限定でコンテナにsshできます。

ぶっ壊れてもいいので、今回の作業以外でも色々遊ぶといいです。

現在使っているシェルを確認

$ echo $SHELL


/bin/bash


現在使っているgoのバージョンを確認

$ go version


go version go1.6.2 linux/amd64


gvmをインストール

$ bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer)


Cloning from https://github.com/moovweb/gvm.git to /home/ubuntu/.gvm

Created profile for existing install of Go at "/usr/local/go"

Installed GVM v1.0.22

Please restart your terminal session or to get started right away run

source /home/ubuntu/.gvm/scripts/gvm


上記出力の通り、以下を読み込んであげましょう。

source /home/ubuntu/.gvm/scripts/gvm

インストールgo一覧の確認

$ gvm list


gvm gos (installed)

system


現状システムインストールされたgoだけが表示されています。CircleCIで使われてるイメージはデフォルトでgo入ってるので。すなわちsystemですね。

gvmで任意のバージョンのgoインストール

gvm install go1.4 -B


Installing go1.4 from binary source


ちゃんと入ったようなので、先ほどインストールしたバージョンを使います

gvm use go1.4 


Now using version go1.4


--defaultオプションをつけることもできます。たぶん基本的につけてつかうことになるでしょう。

goのバージョン確認

go version


go version go1.4 linux/amd64


ちゃんと1.4に変わってます。

元に戻す

gvm use system

ちなみにgvm経由でインストール可能なgoのバージョンの一覧は以下で得ることができます

gvm listall

バルスコマンド。インストールしたバイナリなどに加え、gvm自身も消え去ります。

けっきょく、${HOME}/.gvm (= ${GVM_ROOT}) をrm -rfすればおなじことです。

gvm implode

さて。もともと、やりたかったことは、CircleCIのビルド時のバージョン固定なので、これをcircle.ymlに落とし込みます。


CircleCIで任意のバージョンのgo使ってビルドする

https://circleci.com/docs/language-go/#environment


Since the install is in the default location, GOROOT is not set. If you install your own version of Go, make sure to set the location in GOROOT


任意のバージョン使いたければちゃんと$GOROOTセットしてねって書いてるけど、これはgvmが勝手にやってくれてました。

echo $GOROOT

これはありがたいのでしたが、しかし、とてもありがた迷惑だったのが、$GOPATHの変更まで勝手にやっちゃってくれます。

echo $GOPATH


e.g.

/Users/a13533/.gvm/pkgsets/system/global


ここで結構はまります。

このGOPATHで頑張ってみてもいいのですが、circleciでは$HOME/.go_projectをGOPATHと扱うよう推奨してあります。推奨ではなくデフォルトですが、これをぼくは推奨として受け取ります。公式ドキュメントに沿っておいた方が後続のコーダーが理解しやすいとおもうので良。

https://circleci.com/docs/language-go/#environment


a symlink is placed to your project’s directory at /home/ubuntu/.go_project/src/github.com/<USER>/<REPO_NAME>


なので、gvmが決めるGOPATHにこちらが指定したいGOPATHを追加する方法で解決しました。

そもそもどこで環境変数を上書きしているのかを探します。結論は、当該シェルのrcファイルを改変してくれていたのでした。

bashでインスコしたなら.bashrc、zshでインスコしたなら.zshrcの末尾に以下が追記されていると思います。

[[-s "$HOME/.gvm/scripts/gvm"]] && source "$HOME/.gvm/scripts/gvm"

このなかを追って読むと、デフォルトのpkgsetに書かれてある環境変数がロードされていることがわかるとおもいます。

そこで、デフォルトのpkgsetの設定ファイル末尾に追記してGOPATHをoverrideします。

注意: gvmのpkgsetやpkgenvを使ってGOPATH管理をしない前提です。

echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

これで、こちらが設定したGOPATH_ORGがGOPATHに追加されるようになります。


環境変数がセットできない、cdできない...etc

ここまで話して、export GOPATH=$GOPATH:$追加パスでよくないか?と思われるかとおもいますが、

ひっかかりポイントとして、CircleCIは、各コマンドをサブシェルとして実行します

どういうことかというと、

- export HOGE=fuga

- echo $HOGE

この結果は、設定した環境変数の中身は空になります。

なぜなら環境変数入れたあとに実行されるechoコマンドは別シェルで実行されるからです。

cdコマンドしてから何かを実行したいパターンでよくはまりそうです。以下も想定した通りにはいきません。

- cd path/to/repo

- sh ./hoge.sh


sh: ./hoge.sh: No such file or directory


流れるようにコマンドが実行されるかと思いきやここ要注意です。

さて。以下、今回やった範囲の設定です。


circle.yml

machine:

environment:
GOROOT: ""
PATH: "/usr/local/go/bin:/usr/local/go_workspace/bin:~/.go_workspace/bin:${PATH}"
GOPATH_ORG: "${HOME}/.go_workspace:/usr/local/go_workspace:${HOME}/.go_project"
GVM_GOS: "go1.6.2"
post:
- bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) && source ${HOME}/.gvm/scripts/gvm
- gvm install ${GVM_GOS} -B
- gvm use ${GVM_GOS} --default
# `gvm` arbitrarily changes GOPATH value :( so forcibly override it with its original value.
- echo "export GOPATH; GOPATH=\"${GVM_ROOT}/pkgsets/${GVM_GOS}/global:${GOPATH}:${GOPATH_ORG}\"" >> ${GVM_ROOT}/environments/default

dependencies:
pre:
- go get github.com/golang/lint/golint
- sudo add-apt-repository ppa:masterminds/glide -y && sudo apt-get update && sudo apt-get install glide -y
- mkdir -p ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}
- ln -sfnv ${HOME}/${CIRCLE_PROJECT_REPONAME} ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}
override:
- glide install
- cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go build -v -race -o build/output

test:
override:
- cd ${HOME}/.go_project/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} && go test -v -race -cover `glide nv`


今回なかなか強引に対応しましたがほかにいいやりかたないかな。

ghq使っている自分としてはGOPATH問題がなかなかめんどうなためやりづらい。

普通に単体で使えばちゃんと便利なんだけど。


参考