やらなイカ?

たぶん、iOS/Androidアプリの開発・テスト関係。

diff-pdfでPDFファイルの視覚的差分を取るGitHub Action

diff-pdfは、オープンソースのPDFファイル比較ツールです。 これをGitHub Actionsワークフローから使用するためのDockerイメージ及びActionを作りましたので紹介します。

github.com

diff-pdfには、指定するオプションによって大きく2通りの用途があります。

  1. PDFを出力するシステムの検証のため、出力されたPDFが期待するものと等しいものかを検証する(ビジュアルリグレッションテスト、ゴールデンテスト)
  2. Re:VIEWMarpのようなテキストベースの原稿をPDFなどにレンダリングするツールを使用して執筆している場合に、テキストでなくレンダリング結果の差分*1を確認するためのPDFを生成する

以下、それぞれの用途に向けたワークフローの記述例を紹介します。

PDFが期待するものと等しいかを検証する

あらかじめ用意したexpected.pdfと、システムで出力したactual.pdfを次のように指定します。

- uses: nowsprinting/diff-pdf-action@v1
  with:
    file1: expected.pdf
    file2: actual.pdf

PDF同士が不一致のとき、このステップはエラーとなります。

差分PDFを生成する

例えばPull Request(以下PR)における差分を確認したいときは、PRのHEADコミットとBASEコミットそれぞれでレンダリングを実行し、得られたhead.pdfbase.pdfを次のように指定します。

- uses: nowsprinting/diff-pdf-action@v1
  with:
    file1: base.pdf
    file2: head.pdf
    options: --output-diff=diff.pdf
    suppress-diff-error: true

suppress-diff-error: trueと指定することにより、2つのPDFが不一致であってもこのステップはエラーになりません。 そして--output-diffオプションで指定したパスに、次のような赤青に色付けされた差分PDFファイルが出力されます。

なお、--skip-identicalオプションを付けると差分のないページを省略したPDFファイルが出力されて便利です。 また--mark-differencesを付けると上図のようにページ左に差分箇所を示す赤いマークが出力されます。

PRの差分PDFを出力するワークフロー全文

参考までに、PRにラベルdiff-pdfを付けたときのみ差分PDFファイルを出力するワークフローの例を貼っておきます。 このままコピペすれば動きます。

name: Diff pdf

on:
  pull_request:
    types: [ opened, synchronize, reopened, labeled ] # Note: labelを外した契機では動かない

env:
  REVIEW_CONFIG_FILE: 'config.yml'  # 代表的な設定ファイルを指定

jobs:
  diff-pdf:
    if: |
      contains(github.event.pull_request.labels.*.name, 'diff-pdf') ||
      ((github.event.action == 'labeled') && (github.event.label.name == 'diff-pdf'))
    runs-on: ubuntu-latest
    container:
      image: vvakame/review:5.3 # 原稿のRe:VIEWバージョンと一致するイメージを指定

    steps:
      - name: Checkout head
        uses: actions/checkout@v3
        with:
          fetch-depth: 100

      - name: Build head PDF
        run: |
          REVIEW_CONFIG_FILE=${{ env.REVIEW_CONFIG_FILE }} rake pdf
          mv ./*.pdf ../head.pdf
        working-directory: articles

      - name: Checkout base
        run: |
          git config --system --add safe.directory /__w/REPO_NAME/REPO_NAME # REPO_NAMEにはリポジトリ名を記述
          git checkout ${{ github.event.pull_request.base.sha }}

      - name: Build base PDF
        run: |
          REVIEW_CONFIG_FILE=${{ env.REVIEW_CONFIG_FILE }} rake pdf
          mv ./*.pdf ../base.pdf
        working-directory: articles

      - name: Diff PDFs
        uses: nowsprinting/diff-pdf-action@v1
        with:
          file1: base.pdf
          file2: head.pdf
          options: --verbose --skip-identical --mark-differences --output-diff=diff.pdf --dpi=100
          suppress-diff-error: true
        # Note: `--skip-identical`は差分が無い場合でも、空白1ページのpdfが出力される

      - uses: actions/upload-artifact@v3
        with:
          name: Output diff PDF
          path: diff.pdf

コンテナで使用する

diff-pdf-actionで使用しているDockerイメージはGitHub Container Registryに公開しています*2

コンテナは次のように使用できます。 diff-pdfを何度も実行するワークフローであれば、コンテナを使用するほうがオーバーヘッドが抑えられて高速になるはずです。

jobs:
  diff:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/nowsprinting/diff-pdf:latest

    steps:
      - uses: actions/checkout@v3
      - run: diff-pdf --verbose expected1.pdf actual1.pdf
      - run: diff-pdf --verbose expected2.pdf actual2.pdf
      - run: diff-pdf --verbose expected3.pdf actual3.pdf
      :

*1:PDF比較ツールには、テキストを比較するものが比較的多くありますが、diff-pdfは視覚的に(画像として)比較してくれるツールです

*2:Docker Hubには置いていないので注意