DependabotとGithub Actionsで自動バージョンアップを実現する話


クラウドワークス Advent Calendar 2020の10日目の記事となります。

今回は「DependabotとGithub Actionsで自動バージョンアップを実現する話」を書いていきます。

Dependabot について

Dependabot は Gemfile や package.json など、言語が持つパッケージ管理のマニフェストファイルを見て古かったり、安全ではないライブラリを発見してくれます。そして発見した依存関係を元に Pull Request(以下PR) を作成して通知してくれるツールです。

導入に関してはDependabot がGithubに買収されたことをきっかけにPreview 版から現在はGitHub公式のDependabot(Beta)が出来上がっているため今後はこれを使用していくことが推奨されます。

しかしGithub上でもまだBetaアイコンがついているためこれから・・・という感じかもしれませんが今の所問題なく使用できています。

なぜ使うのか

近年アプリケーション開発では、依存するライブラリの機能追加やセキュリティのアップデートに追従しておかないと気づいた時にはEOLになっていたり、致命的な脆弱性がでており個人情報が流出してしまうなど様々な問題が発生する可能性があります。

しかし、これらを全て把握し管理・運用していくのは並大抵の努力ではありません。
そこでDependabotを使うことで依存関係を管理し通知してもらい運用者の負荷を下げ、できるだけ早くバージョンアップを実現させていく事が重要となってきます。

Github Dependabot 導入の手順

Dependabot有効化の手順は下記に書いてありますが実際にやってみた結果や気になった点について触れてみます。

基本的には、.github/dependabot.yml をリポジトリ上に設置すると動き出すと書かれていますが、ForkしたリポジトリでDependabotを有効にする場合はGUI上からも Enable Dependabot を押して有効化を行わないといけません。(おそらくFork専用にGUI上に実装している感じもしますが初見だと少し紛らわしい感じがします)

さらに2020年12月現在GUI上でDependabotは、InsightDependency graph の下にあるので、SettingSecurity & analysisDependency graphEnable
Dependency graph を押せば使えるのかなと思っていたのですがこれも関係ありません。もう一度言いますが基本的にはファイルを設置するだけで使えます。

  • Dependency graph の下に存在するが
  • Dependency graph を有効化してもDependabot(Beta)は有効にならない

もう少し補足しておくと Dependency graph はprivate repositoryでは default が Disable ですが、public repository では default が Enable になっていたりrepositoryによっても挙動が違っていますがまとめるとこんな感じです。

きちんとドキュメントを読めば簡単に理解できるのですが、Github上に思いの外Depend〇〇の機能が増えていてこれらの関連性がいまいちわかっていませんでした。
この辺りは最後のApendixにまとめておりますのでよければ見てください。

Dependabotの設定について

.github/dependabot.yml を設置後、package-ecosystem を使用して、監視するパッケージマネージャーを指定する必要があります。

選択できる対象はこちらにのっていますが今回はDockerを対象として実践していきます。

.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "daily"
      time: "14:00"
      timezone: "Asia/Tokyo"
    target-branch: "develop"

パッケージマネージャーごとに、directoryschedule.interval を設定する必要があるため今回はdevelop branchを対象にdailyでチェックするようにファイルを作成しました。
その他、使用できるオプションはこちらに記載されていますのでご参考ください。

また、Dependabotはdefault branch にmergeしないと動かないのでファイルが完成したらPRを作成してください。

PR作成時、Github Actionsでのvalidationのcheckが入るのでおかしなところがあれば修正しておかないと動かない可能性があります。この辺りはうまく連携できていてすごくよさげです。

mergeすると設定したスケジュールでcheckが始まります。言語が持つパッケージ管理のマニフェストファイルなどを見て、新しいバージョンを見つけた場合は以下のようにPRを作成してくれます。

Dependabot Previewからの移行について

現在Dependabot Previewを使っていて移行したいという方はこちらにあるように下記2つの設定を有効にする必要があります。

  • security updates
  • version updates

security updates は SettingSecurity & analysisDependabot security updatesEnable を押して有効化を(この時Dependabot alertsも有効化するように促されるのでこれも有効化する必要があります)

version updates は Dependabot Previewのコンソール上から Update config file を押してPRを作成する必要がありますが自分で .github/dependabot.yml を作成して作り直しても構いません。

また、security updatesについてGithub上に取り込まれたことで設定箇所が分かれてしまいましたが、これから新規にDependabotを入れる方がいる場合は合わせて入れておく事をお勧めします。

Github Actionsと連携してCIを回していく

次に設定したDependabotをGithub Actionsと連携していきます。
と、言ってもPRを検知したらGithub Actionsでbuild&testを実行できるようにしておけば簡単に連携が可能となります。

こうすることで作業者はCHANGELOGやCommitを見て影響がなさそうであればmergeを押すだけの作業となり、作業時間の短縮につながります。

ただ以下のようなものも存在する場合があり全てが同じ仕組みで動かせるわけではありません。

  • テストコードがないもの
  • 開発(staging)環境のないもの
  • テストが複雑でCI上でのテストが難しいもの

環境が不十分な場合はGithub Actionsの Comment on PR などを使用してPR作成時にどのように検証や確認を行うかなどコメントしてくれるようにしておくと便利になります。(Readmeに記載でも問題ないとは思いますが作業者が必ず全てを読んでくれているとは限りません)

.github/workflows/commentonpr.yml
name: Add checkout commands
on:
  pull_request:
jobs:
  comment:
    name: Add checkout commands
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@master
      - uses: harupy/comment-on-pr@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          filename: template.md
.github/workflows/template.md
マージ後に以下の確認を行ってください。

- [ ] テスト1
- [ ] テスト2

稼働確認は別途Readmeを参照してください。

さらにmerge後deployの仕組みも入れておけばさらに自動化できるのでより便利になります。
簡単にこれらのSampleを載せておきます。

  • PRがきたらgoとdockerのbuild&testを動かす
    • Github Actions だと dcoker-composeがそのまま使えるのでこの辺りもすごく便利だなと感じました。
.github/workflow/buildtest.yml
name: Test
on:
  pull_request:

jobs:
  gotest:
    name: Go test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup go
        uses: actions/setup-go@v2
        with:
          go-version: 1.14.3
      - uses: actions/cache@v2
        id: cache
        with:
          path: |
            ~/go/pkg/mod/cache
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-
      - name: Install dependencies
        run: go mod download
      - name: Verify modules
        run: go mod verify
      - name: Tidy modules
        run: go mod tidy
      - name: go test
        run: go test -v -cover ./...
  connect_test:
    name: Connect test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Docker pull
        run: docker-compose pull
      - name: Docker build
        run: docker build -t hogehoge .
      - name: Connect test
        run: make test
  • develop branchへのmerge時にAWSのECRにpushする
.github/workflow/ecrpush.yml
name: ECRPush
on:
  push:
    branches:
      - develop

jobs:
  ecr_push:
    name: ECRPush
    runs-on: ubuntu-latest
    env:
      AWS_REGION: ap-northeast-1
      AWS_ACCOUNT: xxxxxxx
      ECR_REPOSITORY: sample
      IMAGE_TAG: latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ env.AWS_REGION }}
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      - name: Build, tag, and push image to Amazon ECR
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: ${{ env.ECR_REPOSITORY }}
          IMAGE_TAG: ${{ env.IMAGE_TAG }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      - name: Logout of Amazon ECR
        if: always()
        run: docker logout ${{ steps.login-ecr.outputs.registry }}

そしてこれらを組み合わせることでDependabotとGithub Actionsで自動バージョンアップを実現させていきます。

自分が考えているフロー図を下記にまとめておきます。もちろんシステムや環境によって一概には言えませんが参考にしてみてください。
※青色の部分が人の手を介して作業を行う部分になります。

ということで今回DependabotとGithub Actionsで自動バージョンアップを実現する話を書きました。
Github Actionsはマーケットプレイスを見ると色んなworkflowが出てきているので、これから導入する方は必要なものを探し組み合わせよりよいCIを行なって行くことをお勧めします。

運用していく上での注意点について

Dependabotは非常に便利ですが、対応するリポジトリの量や依存するライブラリが多い場合はDependabotの通知ばかりきてしまい本来の業務に支障が出る場合もあります。最悪そのまま放置されるということもありがちなのでそれはあまり良くありません。
そのため入れるシステムの重要度や対象のパッケージマネージャーによってどういった頻度やバージョン単位(メジャーなのかマイナーなのかなど)でチェックしていくかも重要になってきます。

Appendix

始めに少し触れましたがDependa系は他にも色んな設定がありこれらも有効化しておくとさらに便利になりますが、ドキュメントなどをみてもひとつにまとまっている記事がなくそれぞれの関連性や機能を理解をするのに苦労をしました。

そのためここで簡単に紹介させていただきます。

Dependency graph

サポートされているファイル形式を使用して、サポートされているパッケージエコシステムの依存関係を定義するもの。サポート対象はこちら

【Dependencies】

  • Dependency graphによって、サポートされているエコシステムのマニフェストファイルとロックファイル、またはそれらに相当するものに詳細が示されている
  • Dependency graph を有効化することで使用可能

【Dependents】

  • public repositryの場合、リポジトリまたはパッケージに依存するリポジトリが報告される。 なお、private repositoryでは報告されない。

【Dependabot(Beta)】

  • 本編で書いたので省略

Dependabot alerts

コードにセキュリティ脆弱性が発見されると、該当するコードがどのプロジェクトで利用されているかを追跡し、そのコードを利用しているユーザーに通知するもの

Dependabot security updates

Dependabotのセキュリティ更新により、リポジトリの依存関係グラフで脆弱な依存関係に対してDependabotアラートが発生すると、Dependabotは自動的にそれを修正しようとし、PRを発生させて、依存関係をパッチを含む最小バージョンに更新し、PRをDependabotアラートにリンクするか、アラートのエラーを報告する。

ただしこれはDependency graphとDependabot alertsを有効にしている場合のみ使用可能となる。

さいごに

GithubにはDependabotだけでなくDependenciesやDependabot security updatesなど様々な便利機能が備わっていました。
入れるのは比較的簡単なので、もし入れてないリポジトリがあれば入れてみて日々の運用に生かしてください。

ここまで読んでいただき、ありがとうございました。