This is my life.

There are many like it, but this one is mine.

GitHub Actions のワークフローで Go 1.11 Modules のキャッシュを扱う

GitHub Actions で Golang の vet と test を実行したいと思います。
( ※実際には Go 1.10 からは go test の前に go vet が実行されますが、2つ以上のコマンドを実行したい場合を想定しています。)

ワークフローを以下のように定義しました。

workflow "vet and test" {
  on = "push"
  resolves = [
    "vet",
    "test"
  ]
}

action "vet" {
  uses = "docker://golang:1.11"
  runs = "go"
  args = ["vet", "./..."]
}

action "test" {
  uses = "docker://golang:1.11"
  runs = "go"
  args = ["test", "./..."]
}

f:id:duck8823:20181231085723p:plain

GitHub Actions では /github/workspaceワークスペースとなります。
また、使用している golang の公式Dockerイメージでは $GOPATH/go に設定されます。
Go 1.11 では $GOPATH の外では Modules (vgo)が利用されるので、 go vet および go test のそれぞれで依存モジュールのダウンロードを行いました。
同じモジュールを二度もダウンロードする必要はありません。一度だけダウンロードするようにしてみましょう。

Modules のキャッシュは $GOPATH/pkg/mod/cache 以下に配置されるため、そのままでは GitHub Actions のアクション間でキャッシュを共有することはできません。
ですが、少し工夫をすることでこれを使い回すことができます。

vendor ディレクトリを利用する方法 (失敗)

go mod vendor は依存するモジュールをダウンロードして vendorディレクトリ に配置してくれます。
GO111MODULE=on の状態で vendorディレクトリ を作成し、次のアクションでは GO111MODULE=off にすることで、 vendorディレクトリを利用することができます。

が、 GO111MODULE=off にした場合、( インポートを github.com から始めていると )自身のパッケージが解決できず、 失敗してしまいます。
無理やりならできるかもしれませんが、vendor は使わない方法にしました。

ワークスペース以下に $GOPATH を設定する方法

ワークスペース以下に作られたファイルやディレクトリは、次のアクションに引き継がれます。
これを利用し、 $GOPATHワークスペース以下に設定することで Module のキャッシュも渡してやることができます。
環境変数actionenv で設定することができます。
出来上がったワークフローは以下の通りです。

workflow "vet and test" {
  on = "push"
  resolves = [
    "vet",
    "test"
  ]
}

action "download" {
  uses = "docker://golang:latest"
  env = {
    GOPATH = "/github/workspace/.go"
  }
  runs = "go"
  args = ["mod", "download"]
}

action "vet" {
  uses = "docker://golang:latest"
  needs = ["download"]
  env = {
    GOPATH = "/github/workspace/.go"
  }
  runs = "go"
  args = ["vet", "./..."]
}

action "test" {
  uses = "docker://golang:latest"
  needs = ["download"]
  env = {
    GOPATH = "/github/workspace/.go"
  }
  runs = "go"
  args = ["test", "./..."]
}

f:id:duck8823:20181231085540p:plain

実行した場合の test のログは以下のようになりました。
ダウンロードのログは出ていませんね。

### STARTED test 21:31:58Z

Already have image (with digest): gcr.io/github-actions-images/action-runner:latest
?       github.com/duck8823/duci    [no test files]
ok      github.com/duck8823/duci/application    0.016s
?       github.com/duck8823/duci/application/cmd    [no test files]
ok      github.com/duck8823/duci/application/context    0.008s
ok      github.com/duck8823/duci/application/semaphore  0.010s
?       github.com/duck8823/duci/application/service/docker [no test files]
?       github.com/duck8823/duci/application/service/docker/mock_docker [no test files]
ok      github.com/duck8823/duci/application/service/git    0.045s
?       github.com/duck8823/duci/application/service/git/mock_git   [no test files]
ok      github.com/duck8823/duci/application/service/github 0.006s
?       github.com/duck8823/duci/application/service/github/mock_g
...

今回のケースでは、並列でダウンロードする場合もそんなに実行時間が変わるような変更はしていません。
ですが、ダウンロードは少なくして負荷をあまりかけないようにしたいですね。

まとめ

アクション間で引き継ぎたいディレクトリやファイルはワークスペース以下になるようにしましょう。