[GitHub Actions] 自動的にnpm updateを実行してPull Requestを作成する方法

背景

npm updateを定期的に実行して依存パッケージを最新に保つ必要があったのだが、手動で実行するのは手間がかかるので、GitHub Actionsで自動化したい。

仕様

  1. 毎週月曜日にワークフローを実行
  2. npm updateを実行
  3. package-lock.jsonが更新されたか確認する
  4. package-lock.jsonをコミットしてPull Requestを作成する

実装

1. 毎週月曜日にワークフローを実行

on:
  schedule:
    - cron: '0 0 * * 1'

2. npm updateを実行

- name: Update packages
  run: npm update

3. package-lock.jsonに差分があるか確認する

- name: Check for changes
  id: git-check
  run: |
    git diff --exit-code || echo "changes=true" >> $GITHUB_OUTPUT

git diff --exit-codeで差分があるか確認。差分がある場合はexit codeが1になるので、$GITHUB_OUTPUTchanges=trueを追記する。

4. Pull Request を作成する

まず、if: ${{ steps.git-check.outputs.changes == 'true' }}で差分がある場合のみ実行するようにする。

Pull Request作成はpeter-evans/create-pull-requestというアクションを使う。このアクションは、コミットも一括で指定ができるので(add-paths)、package-lock.jsonをコミットからPull Requestまで行う。

Create Pull Request action will:

  1. Check for repository changes in the Actions workspace. This includes:
    • untracked (new) files
    • tracked (modified) files
    • commits made during the workflow that have not been pushed
  2. Commit all changes to a new branch, or update an existing pull request branch.
  3. Create a pull request to merge the new branch into the base—the branch checked out in the workflow.

定義は以下の通り。

- name: Create Pull Request
  if: ${{ steps.git-check.outputs.changes == 'true' }}
  uses: peter-evans/create-pull-request@v5
  with:
    token: ${{ secrets.BOT_TOKEN }}
    base: develop
    add-paths: package-lock.json
    commit-message: Update npm dependencies
    title: '[Automated] Update npm dependencies'
    body: 'Automated changes by GitHub Actions'
    branch: automated-npm-update
    delete-branch: true

tokenへの指定は${{ secrets.GITHUB_TOKEN }}でも良いが、別のActions workflowをトリガしないため、ほかのCIを動かしたい場合は別のトークンを用意する必要がある。

また、連続で実行された場合でも同じbranchに対してコミットされ、Pull Requestが更新される(Pull Requestの作成は失敗しない)。

完成結果

大まかに以下のようなワークフローができた。

name: Automated npm update

on:
  schedule:
    - cron: '0 0 * * 1'
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

jobs:
  npm-update:
    env:
      pr_title: '[Automated] Update NPM dependencies'

    runs-on: ubuntu-latest

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version-file: '.node-version'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Update packages
        run: npm update

      - name: Check for changes
        id: git-check
        run: |
          git diff --exit-code || echo "changes=true" >> $GITHUB_OUTPUT

      - name: Create Pull Request
        if: ${{ steps.git-check.outputs.changes == 'true' }}
        uses: peter-evans/create-pull-request@v5
        with:
          token: ${{ secrets.BOT_TOKEN }}
          base: develop
          add-paths: package-lock.json
          commit-message: Update npm dependencies
          title: ${{ env.pr_title }}
          body: 'Automated changes by GitHub Actions'
          branch: automated-npm-update
          delete-branch: true