Open13

GitHub Actions で CI 回して README に build と coverage の badge を付けるまでのメモ

KiKiKi-KiKiKiKiKi-KiKi

Jest のテストを GitHub Actions で実行する

  1. PR の最新のコミットでテストを実行する
  2. coverage をコメントに残す
環境
  • jest 29.6.3
  • eslint 8.47.0
  • typescript 5.1.6
  • ts-jest 29.1.1
  • @types/jest 29.5.4

JEST config

jest.config.js

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
  roots: ['<rootDir>/tests/'],
  transformIgnorePatterns: ['/node_modules/'],
};

npm script

package.json

"scripts": {
  "test": "jest --runInBand",
  "test:coverage": "jest test --coverage",
  "lint": "eslint --ext .js,.ts src/",
}

1. PR の最新のコミットで Jest のテストを GitHub actions で実行する

.github/workflows/ci.yml

name: "CI"
on:
  pull_request:
    branches: [main]
jobs:
  ci:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ["lts/*"]
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup
        run: npm ci
      - name: ESLint
        run: npm run lint
      - name: Run Jest
        run: npm run test:coverage

https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
https://github.com/actions/setup-node#supported-version-syntax

KiKiKi-KiKiKiKiKi-KiKi

2. coverage を PR のコメントに残す

Jest Coverage Comment を使ってみる
https://github.com/marketplace/actions/jest-coverage-comment

jest-junit をインストール

--reporters=jest-junit のオプションが使われており、jest-junit が必要なようなのでインストールする

$ npm i -D jest-junit

JEST config の修正

jest.config.js

/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx', 'node'],
  roots: ['<rootDir>/tests/'],
  transformIgnorePatterns: ['/node_modules/'],
+ reporters: [ 'default', 'jest-junit' ],
};

Summary Report を PR に追加する

サンプルを参考に GitHub actions に coverage を PR にコメントするアクションを追加する

.github/workflows/ci.yml

name: "CI"
on:
  pull_request:
    branches: [main]
jobs:
  ci:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ["lts/*"]
    steps:
      - uses: actions/checkout@v3
      # …
      - name: Run Jest
        run: npm run test:coverage
+     - name: Jest Coverage Comment
+       uses: MishaKav/jest-coverage-comment@v1.0.23
+       with:
+         coverage-summary-path: ./coverage/coverage-summary.json

https://github.com/marketplace/actions/jest-coverage-comment
https://qiita.com/takasp/items/5864d0601c41a769a2a0
https://oikawa.dev/blog/20210810_jest-coverage-report-action

KiKiKi-KiKiKiKiKi-KiKi

🙅 ./coverage/coverage-summary.json が存在しないエラー

Jest Coverage Comment の箇所で ./coverage/coverage-summary.json が存在しないとして CI が落ちてしまっていた
Jest Coverage Comment がいい感じに生成してくれる訳ではないらしい

Jest で summary レポートを出力する必要があった

Jest Coverage Comment の直前で実行される Jest コマンドで coverage-summary.json が出力されてないのが原因だった

https://stackoverflow.com/questions/60967561/jest-coverage-how-can-i-get-a-total-percentage-of-coverage
https://jestjs.io/docs/configuration/#coveragereporters-arraystring--string-options
https://zenn.dev/dove/scraps/2a0055b10cb142

--coverageReporters="json-summary" オプションで coverage-summary.json が出力できる

通常のテストを残したいので別途 json-summary を生成するための npm script を作成した

package.json

"scripts": {
  "test": "jest --runInBand",
  "test:coverage": "jest test --coverage",
+ "test:coverage:summary": "jest test --coverage --coverageReporters=\"json-summary\"", 
  "lint": "eslint --ext .js,.ts src/",
}

workflow を修正する
.github/workflows/ci.yml

name: "CI"
on:
 pull_request:
   branches: [main]
jobs:
 ci:
   runs-on: ubuntu-latest
   strategy:
     matrix:
       node-version: ["lts/*"]
   steps:
     - uses: actions/checkout@v3
     # …
     - name: Run Jest
-      run: npm run test:coverage
+      run: npm run test:coverage:summary
     - name: Jest Coverage Comment
       uses: MishaKav/jest-coverage-comment@v1.0.23
       with:
         coverage-summary-path: ./coverage/coverage-summary.json

これで PR を作ると coverage がコメントされるようになった

✅ Jest のテストを GitHub Actions で実行する

KiKiKi-KiKiKiKiKi-KiKi

Build と Coverage の badge を README に表示する

  1. Build 成功の badge を README に貼る
  2. Coverage の badge を README に貼る

条件

  • いずれも badge の画像が生成されて同じ URL でアクセスできる必要がある
  • Build・Coverage の badge 生成は main のブランチに PR がマージ・更新された時に実行されれば良い

1. Build 成功の badge を README に貼る

https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge

https://github.com/<OWNER>/<REPOSITORY>/actions/workflows/<WORKFLOW_FILE>/badge.svg

で workflow status badge が取得できるっぽい
なので main ブランチが更新された時にビルドとテストを実行して通れば OK とする GitHub action を作成すれば良い

main ブランチが更新された時に実行される GitHub actions を作成する

.github/workflows/build.yml

name: Build
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['lts/*']
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup
        run: npm ci
      # Test と build を実行
      - run: npm run test:coverage
      - run: npm run build

README に Build workflow の status badge を貼り付ける

README.md

![build workflow](https://github.com/<USER_NAME>/<REPOSITORY>/actions/workflows/build.yml/badge.svg)

🎖️ workflow の name に設定した名前で badge が表示されていれば OK

✅ Build の badge を README に表示する

KiKiKi-KiKiKiKiKi-KiKi

2. Coverage の badge を README に貼る (GitHubで完結したい)

https://github.com/marketplace/actions/coverage-badge
https://github.com/marketplace/actions/create-coverage-badges
https://github.com/marketplace/actions/ci-badges

いくつか方法がありそう

📦 Coverage Badge

  • 比較した中でスター数は一番多かった
  • gh-pages ブランチに badge の画像をコミットするっぽい
# .github/workflows/test.yml
name: Test
on: [push, pull_request, workflow_dispatch]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      # Your original steps
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - name: Install
        run: npm install
      - name: Test and Coverage
        run: npm run test:cov  # or npm run coverage
      # Add this
      - name: Update Coverage Badge
        # GitHub actions: default branch variable
        # https://stackoverflow.com/questions/64781462/github-actions-default-branch-variable
        if: github.ref == format('refs/heads/{0}', github.event.repository.default_branch)
        uses: we-cli/coverage-badge-action@main

cf. https://github.com/we-cli/coverage-badge-action/blob/main/README.md

📦 Create Coverage Badges

  • TypeScript 製
  • このライブラリはあくまで coverage の画像を生成するだけで、後は自分でGitHub page に badge をアップロードして使う方法っぽい
- name: Create Coverage Badges
  uses: jaywcjlove/coverage-badges-cli@main
  with:
    style: flat
    source: coverage/coverage-summary.json
    output: coverage/badges.svg

- name: Deploy
  uses: peaceiris/actions-gh-pages@v3
  with:
    github_token: ${{ secrets.GITHUB_TOKEN }}
    publish_dir: ./build

cf. https://github.com/jaywcjlove/coverage-badges-cli/blob/main/README.md

📦 CI Badges action

  • Badge のカスタマイズが豊富
  • Gist を使う方法っぽい
uses: gaelgirodon/ci-badges-action@v1
with:
  gist-id: <gist-id>
  token: ${{ secrets.GIST_TOKEN }}

cf. https://github.com/GaelGirodon/ci-badges-action/blob/main/README.md


https://engawapg.net/software-development/2393/github-pages-branch/

KiKiKi-KiKiKiKiKi-KiKi

Create Coverage Badges を選択した

  1. Gist を使う CI Badges action 設定が面倒なので除外
  2. Coverage Badge はファイルの作成もなくいい感じだが、 gh-pages ブランチが作られて勝手にコミットされるのが少し微妙に感じた
    • gh-pages を使わずに GitHub ページへのアップロードができそうだったので
  3. TypeScript 製

2-1. badges.svg を作成する

coverage-summary.json から生成できるようなので、coverage-summary.json を生成できるテストを実行するようにする

.github/workflows/build.yml

name: Build
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['lts/*']
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup
        run: npm ci
      # Test と build を実行
-     - run: npm run test:coverage
+     # npx jest test --coverage --coverageReporters="json-summary"
+     - npm run test:coverage:summary
      - run: npm run build
+     - name: Create Coverage Badges
+       uses: jaywcjlove/coverage-badges-cli@main
+       with:
+         style: flat
+         source: coverage/coverage-summary.json
+         output: coverage/badges.svg
KiKiKi-KiKiKiKiKi-KiKi

2-2. Coverge Badge を GitHub page にデプロイする

🥔 Tips

サンプルに載っていた peaceiris/actions-gh-pagesgh-pages ブランチを使って GitHub page にデプロイするものだったが、GitHub 公式の actions/upload-pages-artifact, actions/deploy-pages を用いる事で gh-pages を使うこと無く GitHub page にデプロイが可能になったらしい

サイトを公開するカスタム GitHub Actions ワークフローの作成

GitHub Actions で発行するようにサイトを構成する場合、GitHub により、一般的な公開シナリオのスターター ワークフローが提案されます。 ワークフローの一般的なフローは、次のとおりです。

  1. リポジトリの既定のブランチへのプッシュがあるたびに、またはワークフローが [Actions] タブから手動で実行されるたびに、トリガーされます。
  2. actions/checkout アクションを使用してリポジトリの内容をチェックアウトします。
  3. サイトで必要な場合、静的サイト ファイルをビルドします。
  4. actions/upload-pages-artifact アクションを使用して静的ファイルを成果物としてアップロードします。
  5. ワークフローが既定のブランチへのプッシュによってトリガーされた場合、actions/deploy-pages アクションを使用して成果物をデプロイします。 ワークフローが pull request によってトリガーされた場合、この手順はスキップされます。

cf. https://docs.github.com/ja/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site#サイトを公開するカスタム-github-actions-ワークフローの作成

cf.

2-2-1. GitHub page の準備

  1. ブランチの Settings メニューから Pages を選択
  2. Build and deployment の Source を GitHub Actions にしておく

2-2-2. badge を GitHub Page にアップロードする workflow を追加する

  1. actions/upload-pages-artifact で github storage にアップロードする
  2. アップロードされたファイルを actions/deploy-pages で GitHub page にデプロイする

cf. https://zenn.dev/jordan/articles/b6c1e905adab31

.github/workflows/build.yml

name: Build
on:
  push:
    branches: [main]
+ permissions:
+   contents: read
+   pages: write
+   id-token: write
+ concurrency:
+   group: 'pages'
+   cancel-in-progress: false
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['lts/*']
    steps:
      - uses: actions/checkout@v3
      # 略
      - npm run test:coverage:summary
      - run: npm run build
      - name: Create Coverage Badges
        uses: jaywcjlove/coverage-badges-cli@main
        with:
          style: flat
          source: coverage/coverage-summary.json
          output: coverage/badges.svg
+    - name: Upload test coverage artifact
+      uses: actions/upload-pages-artifact@v2
+      with:
+        name: coverage
+        path: ./coverage
+
+ deploy:
+   needs: build
+   environment:
+     name: github-pages
+     url: ${{ steps.deployment.outputs.page_url }}
+   runs-on: ubuntu-latest
+   steps:
+     - name: Deploy to GitHub Pages
+       id: deployment
+       uses: actions/deploy-pages@v2
+       with:
+         artifact_name: coverage

ブランチの GitHub page 直下に ./coverage の内容がデプロイされるので、badges.svg のパスは http(s)://<username>.github.io/<repository>/badges.svg となる
この URL にアクセスして Coverage の badge が表示されていれば OK

2-2-3. 🎖️ badge を README に追加する

badges.svg を画像として表示する markdown を README に貼り付ければ OK

[![coverage](https://<username>.github.io/<repository>/badges.svg)](https://github.com/<username>/<repository>/actions)

✅ Coverage の badge を README に表示する

[参考]

https://zenn.dev/jordan/articles/b6c1e905adab31
https://zenn.dev/zozotech/articles/f2509a21b768ed
https://docs.github.com/ja/pages/getting-started-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site
https://github.com/actions/upload-pages-artifact
https://github.com/actions/deploy-pages

KiKiKi-KiKiKiKiKi-KiKi

最終的な CI workflow

.github/workflows/ci.yml

name: CI
on:
  pull_request:
    branches: [main]

jobs:
  build:
    name: Test on node ${{ matrix.node_version }} and ${{ matrix.os }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]
   steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup
        run: npm ci
      - name: ESLint
        run: npm run lint
      - name: Run Jest
        run: npm run test:coverage:summary
      - name: Jest Coverage Comment
        uses: MishaKav/jest-coverage-comment@v1.0.23
        with:
          coverage-summary-path: ./coverage/coverage-summary.json

最終的な Build workflow

.github/workflows/build.yml

name: Build
on:
  push:
    branches: [main]
permissions:
  contents: read
  pages: write
  id-token: write
concurrency:
  group: 'pages'
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['lts/*']
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: setup
        run: npm ci
      # Test と build を実行
      - run: npm run test:coverage:summary
      - run: npm run build
      - name: Create Coverage Badges
        uses: jaywcjlove/coverage-badges-cli@main
        with:
          style: flat
          source: coverage/coverage-summary.json
          output: coverage/badges.svg
      - name: Upload test coverage artifact
        uses: actions/upload-pages-artifact@v2
        with:
          name: coverage
          path: ./coverage

  deploy:
    needs: build
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2
        with:
          artifact_name: coverage

Badges

Build status

![build](https://github.com/<USER_NAME>/<REPOSITORY>/actions/workflows/build.yml/badge.svg)

Coverage

![coverage](https://<USER_NAME>.github.io/<REPOSITORY>/badges.svg)
KiKiKi-KiKiKiKiKi-KiKi

🛑 actions/deploy-pages のエラー

coverage の badge を GitHub Page にデプロイする所でエラーが発生した

steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v2
        with:
          artifact_name: coverage

Error: Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.

/covarage ディレクトリなので 10GB を超えているとは考えにくい

❇️ actions/upload-pages-artifact ではなく actions/upload-artifact を使っているとエラーになる

名前が似ているが別物っぽい
https://github.com/actions/upload-artifact

actions/deploy-pages を使うときは actions/upload-pages-artifact を使う

KiKiKi-KiKiKiKiKi-KiKi

🛑 GitHub Page にデプロイした Coverage の badge が 404 になる

coverage の badge をデプロイする workflow が完了しても https://<USER_NAME>.github.io/<REPOSITORY>/badges.svg が 404 になってしまった

❇️ GitHub のリポジトリ名に大文字が含まれていると GitHub page が 404 になる

リポジトリ名に大文字が含まれている場合は変更する必要があった
リポジトリ名の変更は GitHub 上から SettingsGeneralRepository name から変更できる

リポジトリ名変更後に local の remote ブランチの名前を変更する 👇
https://chaika.hatenablog.com/entry/2020/03/09/090000

その他 README や package.json などに含まれているリポジトリ名を置換する


[参考]

KiKiKi-KiKiKiKiKi-KiKi

$default-branch が効かない

$default-branch にしておくと main / master どちらでものブランチでも OK になるように GitHub の記事 に書かれていたのですが、実際には動作しないようです…

The documentation on custom workflow templates linked in that changelog post says this:

If you need to refer to a repository’s default branch, you can use the $default-branch placeholder. When a workflow is created using your template, the placeholder will be automatically replaced with the name of the repository’s default branch.

So $default-branch doesn’t seem to be supported in the actual workflow file.
cf. $default-branch not working · community · Discussion #26597 · GitHub

つまり、GitHub上のテンプレートを使用して作成した場合は $default-branch がデフォルトブランチ名に置き換えられるけど、local で $default-branch としたファイルを作成しても置き換えられないので効かないということっぽい。
local で workflow のファイルを作成するときは自分のデフォルトブランチ名 (main / master) を直接指定する必要があるようです

https://chaika.hatenablog.com/entry/2023/09/13/083000