こんにちは、なんぞーです!
この記事は Go5 Advent Calendar 2019 の 3 日目の記事です!
日頃、業務や趣味で Golang を書いている皆様方の中には、CircleCI や Github Actions などの CI で自動化をなさっている方も多いと思います!
僕もその一人なのですが、Go modules の vendoring を CI でキャッシュした際にハマってしまったことがあったので、記事にしたいと思います。
何をしようとしたか
utility 系のライブラリ(中にサブパッケージがいくつかある)をプロジェクトや会社単位で作って、様々なプロジェクトで使い回すといったことは多々あると思います!
その utility 系のライブラリを使用した開発を行っている際に、vendoring のキャッシュを CI 上で行い、CI の高速化を測ろうとしました
実際どんな感じで開発していたか
今回は開発プロジェクトを「project1」、utility ライブラリを「go-utility」とすることにします。
このような構成の utility ライブラリだとします。
開発初期、project1@v1.0
では example1
と example2
を使っていました。
その時の go.mod と go.sum は
1
2
3
4
5
6
7
| module github.com/nandehu0323/project1
go 1.12
require (
github.com/nandehu0323/go-utility v1.0.0
)
|
1
2
| github.com/nandehu0323/go-utility v1.0.0 h1:[hash]
github.com/nandehu0323/go-utility v1.0.0/go.mod h1:[hash]
|
このようになっていました。
また CI の設定ファイルでは、Tag を付けるとgo mod vendor
を行い、依存パッケージをキャッシュしテストに通過後、キャッシュされた依存パッケージを用いてビルドを行うという処理を設定していました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| version: 2
reference:
work_dir: &work_dir /go/src/tmp
golang_container: &golang_container
docker:
- image: circleci/golang:1.12
environment:
GO111MODULE: "on"
working_directory: *work_dir
test_go: &test_go
run:
name: Testing Go
command: |
go test ./...
build_docker_image: &build_docker_image
run:
name: Build Docker Image
command: |
[ビルド処理]
jobs:
test:
<<: *golang_container
steps:
- checkout
- run: go mod vendor
- save_cache:
name: Saving Go Vendor Cache
key: go-vendor-cache-{{ checksum "go.sum" }}
paths:
- /go/src/tmp/vendor
- *test_go
build:
<<: *build_container
steps:
- checkout
- restore_vendor_cache:
name: Restoring Go Vendor Cache
keys:
- go-vendor-cache-{{ checksum "go.sum" }}
- go-vendor-cache-
- *build_docker_image
workflows:
version: 2
test_-->_build:
jobs:
- test:
filters:
branches:
only: /.*/
tags:
only: /^v.*/
- build:
requires:
- test
filters:
branches:
ignore: /.*/
tags:
only: /^v.*/
|
次に開発が進み project1@v1.1
になり、 example3
のパッケージを使う必要が出てきました。
その他の依存関係の変更はなく、go-utility の使用パッケージが増えました。
そこで、いつも通りタグを付けると CI が Build の時点で落ちてしまうようになりました。
1
| build github.com/nandehu0323/project1/cmd/app: cannot load github.com/nandehu0323/go-utility/example3: open /app/vendor/github.com/nandehu0323/go-utility/example3: no such file or directory
|
go.mod
や go.sum
にはちゃんと go-utility@v1.0.0
を使用するように記述されているし、 go mod tidy
を行っても差分は見つかりません。
原因
原因としては、
go mod vendor
は、サブパッケージが含まれている依存パッケージでは必要なサブパッケージのみを vendor ディレクトリに保存するproject1@v1.0
で必要な example1
と example2
しか cache に含まれていない。(初歩的なミスで Go 使いの方からは笑われそうです笑)
- 依存パッケージの追加および削除は行われていないので、go.mod および go.sum の差分はない
- よって、
{{ checksum "go.sum" }}
が変化しないので cache の更新が行われない
このような utility系のパッケージを使用していて
、それ以外の依存関係のアップデートが無く
、utility系のパッケージ内の新しいサブパッケージをimportする
環境下では CI の設定に気をつけないとこのようなことが起こるみたいです。
ここで、僕は CI(CircleCI)の Cache を削除しようとしましたが、version2 ではRerun without cache
ができないらしく、困っていました。
そこで、プロジェクトキャッシュのクリアを参考に設定ファイルを変更することにしました。
変更後
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| jobs:
test:
<<: *golang_container_config
steps:
- checkout
- run: go mod vendor
- save_cache:
name: Saving Go Vendor Cache
key: go-vendor-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "go.sum" }}
paths:
- /go/src/tmp/vendor
- *test_go
build:
<<: *cloud-sdk_container_config
steps:
- checkout
- restore_vendor_cache:
name: Restoring Go Vendor Cache
keys:
- go-vendor-cache-{{ .Environment.CACHE_VERSION }}-{{ checksum "go.sum" }}
- go-vendor-cache-{{ .Environment.CACHE_VERSION }}-
- *build_docker_image
|
以降、同じような環境で再現した場合はCACHE_VERSION
を変えてキャッシュクリアをするように変更しました。
まとめ
今回は、かなり特殊な環境下で起こる現象だと思いますが、go mod vendor
の動作などをちゃんと理解していなかったために起こったものでした。
なかなかこのような事象に遭遇することはないし、Gopher の皆さんなら余裕で解決できると思いますが、もしいつか困っている人がこの記事をみて解決の糸口にしてもらえたらなと思いこの記事を書き残します 👍
最後まで読んで頂きましてありがとうございました!
Twitter(@nandehu0323)もやっておりますのでぜひご感想やアドバイスお待ちしております!