Help us understand the problem. What is going on with this article?

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

More than 3 years have 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問題がなかなかめんどうなためやりづらい。
普通に単体で使えばちゃんと便利なんだけど。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away