depからgo modulesへの移行と、移行時にTravis CI & GoReleaserでハマる(かもしれない)ポイント

PUBLISHED ON 2019-02-25 — GO, GOLANG, GORELEASER, TRAVIS CI

Summary

  • depで管理していた既存のGo製ツールをdepからgo moduleに移行してみたが、移行自体は特にハマりどころも無くスンナリいった
  • go moduleへの移行後に依存パッケージのupgradeを実施、これもスンナリいった
  • Travis CI + GoReleaser で、ビルド成果物をGithub Releasesに登録する作業 & GoアプリのHomeBrewパッケージ作成・公開作業 を自動化しようと試みたが、そのCIが git is currently in a dirty state というコケ方をしてちょっとハマった
  • GoReleaserは本当に便利

前提条件

# goのバージョン
$ go version
go version go1.11.5 darwin/amd64

切り替え手順

depを使っていたので、go mod init して go build の成功を確認できたらdep関連のファイルを削除する、という手順で完了します(対応例

# go mod利用時に必要な環境変数、direnvでプロジェクトディレクトリ下のみ適用されるようにしても良さそう
$ export GO111MODULE=on

# go modulesの初期設定
$ go mod init

# go runしてスタンドアロンで実行すれば、そのタイミングでgo.sumファイルが作成される
$ go run *.go --help

# dep関連ファイルを削除する
$ rm Gopkg.toml Gopkg.lock
$ rm -fr vendor/

# Makefileを修正
# CIの設定ファイル(今回の場合は .travis.yml)を修正

依存パッケージをupgradeしてみる

自作ツールで使用しているaws-sdk-goがv1.12.39だったのですが、最新がv1.16.36と乖離が発生していたのでupgradeしてみました。手順としては下記で問題ありません。

# go.mod を修正してバージョンを変更
$ vi go.mod
module github.com/goldeneggg/lsec2

require (
  github.com/aws/aws-sdk-go v1.16.36
# バージョンを修正したら go mod tidy を実行して反映
$ go mod tidy

# tidy実行後は、go build, go test, go runあたりを実行して問題ないか動作確認

CIの設定変更(Travis CI)

test CI

Travis CIの場合は.travis.ymlを下記の通り修正すればOKです。

  • envセクションで GO111MODULE=on を追加
  • before_install か install かいずれかのセクションで go mod download を行うようにする
env:
  - GO111MODULE=on

install:
  - go mod download

GoReleaserでGoアプリのdeploy CIを行う際の注意点

冒頭にTravis CI + GoReleaserによる成果物自動公開を試みてハマったと書きましたが、調査した結果こういう結論でした。

  • go mod 移行前に、「ライブラリ本体では使ってないがCIや開発補助目的でインストールしたパッケージ」が存在していた
    • 自分の場合は github.com/golang/lint。これをcode validation目的のCI jobで使用していた
  • 該当パッケージは、自分の開発マシン(Mac)ではライブラリの開発とは無関係に go get で過去にインストールされていた、つまりgo.modには反映されていない状態だった。一方でTravis CIでは before_install セクションでCIの度にインストールを実行していた
  • go mod への移行によって go get の実行結果がgo.modとgo.sumに反映されるようになった為、Travis CIで before_install セクションが実行されるとCI実行環境上でgo.modとgo.sumのgit diffが発生するようになった
  • GoReleaserはdeployの際に「git diffが発生していないかどうか」をチェックしていて、上記経緯でgo.modとgo.sumのdiffが発生した事によりチェックに引っかかり、deploy CIがエラーになっていた

回避策としては、

  1. GoReleaserのdeploy処理前に、go.modとgo.sumの差分を(強制的に)解消しておく
  2. Travis CIの before_install で実行している go get をやめる(依存していたCI jobの設定も修正・削除が必要)
  3. go.modに「CIや開発補助目的でインストールしたいパッケージ」も反映しておく

のいずれかの対応が必要そうです(とはいえ本来取るべき対応策はNo.3ではないかと)。

go modules対応版、Travis CI & GoReleaserの設定

go modulesを使用しているGoプロジェクトをgoreleaserでデプロイする場合、公式サイトで紹介されている ように .goreleaser.ymlのbefore hookの設定で go mod download を実行するようにyamlを修正します。

before:
  hooks:
    - go mod download

そして.travis.ymlにあるdeployフェーズの設定を(無ければ追加して)、これも公式サイトで紹介されている通りGoReleaserを実行するように修正します。

deploy:
  - provider: script
    skip_cleanup: true
    script: curl -sL http://git.io/goreleaser | bash
    on:
      tags: true
      condition: $TRAVIS_OS_NAME = linux

こう設定しておけば、該当プロジェクトをmasterにmerge => tagを打ったタイミングで、Travis CIの deploy フェーズで設定したGoReleaserによるデプロイも実行されるようになります。

補足: .goreleaser.ymlの動作確認について

GoReleaser公式のQuick Start にも書いてるのですが、goreleaserコマンドには --skip-publish という公開(release)をskip出来るオプションと、--snapshot というバージョンタグのチェックを行わず現状のコードベースの成果物を出力するオプションがあり、これらを組み合わせて.goreleaser.ymlの妥当性検証を行うことが可能です。

$ goreleaser release --snapshot --skip-publish --rm-dist

   • releasing using goreleaser 0.101.0...
   • loading config file       file=.goreleaser.yml
   • RUNNING BEFORE HOOKS
      • running go mod download

   :

   • release succeeded after 7.35s

とはいえ実際にタグを打って成果物deploy用のCIを実行するとローカルで検証した際には見つからなかったエラーが発生する事もあり、そうなるとそのエラー解消の為にさらにreleaseを行って本チャンCIを走らせる、、、という「CIの為にrelease重ねる症候群」が起こりがちです(自分は今回ハマった件の調査の為に5つ6つはreleaseを重ねてrelease historyがだいぶ切ない事に orz…)。

公式提供のシェススクリプトをカスタマイズする

GoReleaserの場合、成果物公開用のジョブでは curl -sL http://git.io/goreleaser | bash というスクリプトを実行するように設定しますが、http://git.io/goreleaser の実体はタダのシェルスクリプトなので、これを丸っとコピーしてチェック処理やログ出力を追加したオリジナルのスクリプトに置き換えれば、いくらかは調査コストを下げる事が出来そうです。

deploy:
  - provider: script
    skip_cleanup: true
    script: my-goreleaser.sh  # オリジナルをコピーして処理を追加したスクリプトファイル
    on:
      tags: true
      condition: $TRAVIS_OS_NAME = linux
# オリジナルのスクリプトではこういう実装になっているが、
download
tar -xf "$TAR_FILE" -C "$TMPDIR"
"${TMPDIR}/goreleaser" "$@"

# goreleaser実行直前でgit diffが発生していないかチェックする処理を追加する(例)
download
tar -xf "$TAR_FILE" -C "$TMPDIR"

git diff > /tmp/diff.log
cat /tmp/diff.log

"${TMPDIR}/goreleaser" "$@"

ブックマーク