LoginSignup
3
0

More than 3 years have passed since last update.

GitHub Actionsでcacheに転んでいた

Last updated at Posted at 2020-04-14

はじめに

前回の記事のまとめを読んでからだと、雰囲気がつかみやすいかも?

実現したかったこと

GitHub Actionsを用いてgoの静的解析、テスト/ビルドを行うCIを構築したい。
その際、毎回モジュールのインストールが挟まると時間がかかっちゃうので、キャッシュを使って短縮したい。という話の続き。

前回の記事の段階では、キャッシュヒット、と通知されたが、go vetgo testを行う際にモジュールのインストールが発生する。保存したはずのキャッシュが使えてねえ...

起きていたこと

中身がキャッシュされていなかった事件

キャッシュ自体はされていましたが、モジュールがキャッシュされていませんでした。なんのためのキャッシュや...

原因を調べる

静的解析、テスト/ビルドをわけた、問題のyamlは以下。ちょっと長め。

github/workflows/work.yml
... 省略

jobs:
  # cacheが関係ないジョブ
  static-check:
    name: StaticCheck
    runs-on: ubuntu-latest
    container: golang:1.12
    steps:
      # set go version
      - name: Set up Go 1.12
        uses: actions/setup-go@v1
        with:
          go-version: 1.12
        id: go

      - name: Check out code into the Go module directory
        uses: actions/checkout@v2

      # cache保存用にディレクトリを作っておく
      - name: before cache
        run: |
          mkdir -p ~/go/pkg/mod

      # cache
      - name: cache
        uses: actions/cache@v1
        id: cache-go
        with:
          path: ~/go/pkg/mod
          key: ${{ env.cache-version }}-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
          ${{ runner.os }}-go-

      # cacheがヒットしたか確認
      - name: after cache
        run: |
          echo "-->${{steps.cache-go.outputs.cache-hit}}<--"

      # キャッシュがヒットしなかったら install module
      - name: Get dependencies
        if: steps.cache-go.outputs.cache-hit != 'true'
        run: |
          ... 省略

      # Run fmt
      ... 省略 fmt, lint, vetを行う

  build:
    name: Test-Build
    runs-on: ubuntu-latest
    needs: [static-check]
    services:
        ... 省略

    steps:
    - name: Set up Go 1.13
      uses: actions/setup-go@v1
      with:
        go-version: 1.12
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

      ... 省略

    # cache保存用にディレクトリを作っておく
    - name: before cache
      run: |
        mkdir -p ~/go/pkg/mod

    # cache
    - name: cache
      uses: actions/cache@v1
      id: cache-go
      with:
        path: ~/go/pkg/mod
        key: ${{ env.cache-version }}-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
        restore-keys: |
          ${{ runner.os }}-go-

    # cacheがヒットしたか確認
    - name: after cache
      run: |
        echo "-->${{steps.cache-go.outputs.cache-hit}}<--"

    # キャッシュがヒットしなかったら install module
    - name: Get dependencies
      if: steps.cache-go.outputs.cache-hit != 'true'
      run: |
        ... 省略

    # Run test
    ... 以下省略。テストとビルドを行います。

原因はここ。

container: golang:1.12

はい。これだけです。static-checkのjobには設定されていますが、buildの方にはないですね...。

container指定の何があかんかったかというと、GOPATHに違いがでてしまうことでした。
containerを使用した時と、使用なかった時で、GOPATHが変わっているんですね...。

それぞれ、pathを比較してみました。

container

まず、containerを使用した時のPATH

container を指定していた時のPATH
// $GOPATH
/go

// $HOME
/github/home

// $PATH
/go/bin:/usr/local/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

goのモジュールは、/go/pkg/modに入ります。こいつをキャッシュしたいわけですね。

actions/setup-go

次に、actions/setup-goを使用したのPATH。

actions/setup-go だけの時のPATH
// $GOPATH


// $HOME
/home/runner

// $PATH
/opt/hostedtoolcache/go/1.12.17/x64/bin:/usr/share/rust/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/home/runner/.dotnet/tools:/home/runner/.config/composer/vendor/bin

$GOPATHが表示されない!ですが、go envで確認してみると、$GOPATH=/home/runner/goに設定されていることが確認できます。
goのモジュールは、/home/runner/go/pkg/modに入ります。

つまり?

前日の、ディレクトリを作ったらいけた!という現象は。。。

  • ~/go/pkg/modを作成したが、
  • 実際にモジュールがインストールされているのは/go/pkg/mod

つまり、空っぽのディレクトリをキャッシュしてたんですね。なんというアホ。

試したこと

双方、containerを使用してみる

キャッシュをtarで展開するときに、permission deniedにハマりまくった。/goにアクセスすることが困難なので、chmodなりで対抗してみましたが、流石にきびしい。。。

4/15追記: そもそもpathの設定をミスっていた気がする

片方だけcontainerを使用

static-checkcontainerを指定し、buildでは指定しない、という方法。
actions/cacheのpathに違いが出る以外は何も変わらないが、actionsの実行結果をみていると、コンテナの初期化に時間がかかっていることがわかりました...。

containerを使用しない

一瞬でうまくいきました...golintgoimportsはPATHの関係で、少しいじりましたが、それ以外はとても快適。

どのくらい時間に差が出るのか

static-checkbuildの2つのjobを完了するまでに、どれだけの時間がかかるかみてみました。

  • キャッシュを一切使わない: 4m15s
  • 片方だけcontainerを使用、キャッシュを使う: 3m20s
  • 双方containerを使用せず、キャッシュを使う: 2m36s

containerの初期化が凄まじい。最終的にはキャッシュを使うことで、1m45sの短縮。やるじゃん...。

まとめ

containerを指定したのは理由が、actions/setup-goだと/go/binにpathが通っていなかったことでした。
golintや、goimportsをPATHの追加や、~/go/bin/golintなどと書かなくてよくなるので、、、、。
いけるじゃん!って思う前に、中身がどうなってるのか確認しないとダメだなあ...

最終的なyaml

※ testにmysqlが必要なので環境を用意しています。

github/workflows/work.yml
name: Go

on:
  pull_request:
    branches:
      - '*'

env:
  cache-version: v10

jobs:

  static-check:
    name: StaticCheck
    runs-on: ubuntu-latest

    steps:
      # set go version
      - name: Set up Go 1.12
        uses: actions/setup-go@v1
        with:
          go-version: 1.12
        id: go

      - name: Check out code into the Go module directory
        uses: actions/checkout@v2

      # cache store
      - name: cache
        uses: actions/cache@v1
        id: cache-go
        with:
          path: ~/go/pkg/mod
          key: ${{env.cache-version}}-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: after cache
        run: |
          echo "-->${{steps.cache-go.outputs.cache-hit}}<--"

      - name: Install go modules
        if: steps.cache-go.outputs.cache-hit != 'true'
        run: |
          echo "run install go module"
          go get -v -t -d ./...
          if [ -f Gopkg.toml ]; then
              curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
              dep ensure
          fi

      # Run fmt
      - name: Go fmt
        run: |
          go get -u golang.org/x/tools/cmd/goimports
          gofmt -s -w cmd/
          ~/go/bin/goimports -w cmd/

      # Run lint
      - name: Go Lint
        run: |
          go get -u golang.org/x/lint/golint
          ~/go/bin/golint ./...

      # Run vet
      - name: Go vet
        run: |
          go vet ./...

  build:
    name: Test-Build
    runs-on: ubuntu-latest
    needs: [static-check]
    services:
      mysql:
        image: mysql:5.7
        ports:
          - 3306:3306
        options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10
        env:
          MYSQL_ROOT_PASSWORD: pass
          MYSQL_DATABASE: sample

    steps:
    - name: Set up Go 1.12
      uses: actions/setup-go@v1
      with:
        go-version: 1.12
      id: go

    - name: Check out code into the Go module directory
      uses: actions/checkout@v2

    - name: SetUp ddl
      run: |
        mysql --protocol=tcp -u root -ppass sample < ./ddl.sql

    # cache store
    - name: cache
      uses: actions/cache@v1
      id: cache-go
      with:
        path: ~/go/pkg/mod
        key: ${{env.cache-version}}-${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
        restore-keys: |
          ${{ runner.os }}-go-

    # cacheがヒットしたか確認
    - name: after cache
      run: |
        echo "-->${{steps.cache-go.outputs.cache-hit}}<--"

    - name: Get dependencies
      if: steps.cache-go.outputs.cache-hit != 'true'
      run: |
        go get -v -t -d ./...
        if [ -f Gopkg.toml ]; then
            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
            dep ensure
        fi

    - name: Test
      run: go test -v ./...

    - name: Build
      run: go build -v ./cmd/main.go
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0