1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ClaudeとGitHub ActionsでブローシャPDF自動生成パイプラインを作った

1
Posted at

TL;DR

  • HTMLでブローシャを作り、GitHub Actionsで自動的にA4横PDFを生成・コミットするパイプラインを構築した
  • DockerイメージはChromeやNode.js込みのghcr.io/puppeteer/puppeteerを使うことでインストール工程をほぼゼロにした
  • 絵文字はDockerコンテナ内で文字化けするのでSVGアイコンに置き換えた
  • Actions内でgit pushするときはstash → pull rebase → pop → pushの順番が重要
  • HTMLを更新してpushするだけでPDFが自動再生成・コミットされる

やったこと・背景

行政向けのブローシャ(A4横・1ページ)をHTMLで作っていた。毎回ローカルでPDFを生成してコミットするのが面倒だったので、HTMLを更新してpushするだけでPDFが自動生成される仕組みを作った。

ブローシャのHTMLはClaudeと対話しながら設計・文言を磨いていった。「このコピーは担当者目線になっているか」「行政機関に補助金という言葉は正しいか」といった議論をしながら、HTMLを直接編集してpushする流れだ。

最終的な構成:

ブローシャHTML更新 → git push
  ↓
GitHub Actions起動
  ↓
Puppeteer(Docker)でA4横PDF生成
  ↓
asset/brochure.pdf をコミット・push
  ↓
WEBページのダウンロードボタンからDL可能に

ワークフローの全体像

generate-brochure-pdf.yml
name: Generate Brochure PDF

on:
  push:
    paths:
      - 'brochure-government-dx.html'
  workflow_dispatch:

permissions:
  contents: write

jobs:
  generate-pdf:
    runs-on: ubuntu-latest
    container:
      image: ghcr.io/puppeteer/puppeteer:latest
      options: --user root

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install puppeteer locally
        run: npm install puppeteer --prefix /tmp/pdf

      - name: Serve HTML locally
        run: |
          npx http-server . -p 8080 --silent &
          sleep 2

      - name: Generate PDF
        run: |
          node - << 'EOF'
          const puppeteer = require('/tmp/pdf/node_modules/puppeteer');
          const fs = require('fs');

          (async () => {
            const browser = await puppeteer.launch({
              headless: true,
              args: ['--no-sandbox', '--disable-setuid-sandbox']
            });
            const page = await browser.newPage();
            await page.goto('http://localhost:8080/brochure-government-dx.html', {
              waitUntil: 'networkidle0',
              timeout: 30000
            });
            const pdf = await page.pdf({
              landscape: true,
              width: '297mm',
              height: '210mm',
              printBackground: true,
              margin: { top: 0, bottom: 0, left: 0, right: 0 }
            });
            fs.mkdirSync('asset', { recursive: true });
            fs.writeFileSync('asset/brochure-government-dx.pdf', pdf);
            console.log('PDF generated:', fs.statSync('asset/brochure-government-dx.pdf').size, 'bytes');
            await browser.close();
          })();
          EOF

      - name: Commit and push PDF
        run: |
          git config --global --add safe.directory /__w/lp/lp
          git config user.email "your@email.com"
          git config user.name "GitHub Actions"
          git add asset/brochure-government-dx.pdf
          if git diff --staged --quiet; then
            echo "No changes to commit"
          else
            git stash
            git pull --rebase origin main
            git stash pop
            git add asset/brochure-government-dx.pdf
            git commit -m "Auto-generate brochure PDF [skip ci]"
            git push
          fi
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

ハマりポイントと解決策

1. Dockerコンテナ内でPuppeteerのモジュールが見つからない

ghcr.io/puppeteer/puppeteerイメージにはPuppeteerが含まれているが、コンテナ内のパスが通っておらずCannot find module 'puppeteer'エラーが出た。

解決策: /tmp/pdfに明示的にインストールして絶対パスでrequireする。

const puppeteer = require('/tmp/pdf/node_modules/puppeteer');

2. 絵文字が文字化けする

📊📋⚖️ などの絵文字をHTMLに使っていたが、DockerコンテナのChromeには絵文字フォントがなく、PDFで文字化けした。

解決策: 絵文字をインラインSVGに置き換える。

<!-- Before -->
<span class="wall-icon">📊</span>

<!-- After -->
<svg class="wall-icon" viewBox="0 0 20 20" fill="none">
  <rect x="2" y="10" width="3" height="8" rx="1" fill="#2563EB"/>
  <rect x="8" y="6" width="3" height="12" rx="1" fill="#2563EB"/>
  <rect x="14" y="2" width="3" height="16" rx="1" fill="#1E3A5F"/>
</svg>

フォントに依存しないのでどの環境でも確実に表示される。

3. git pushがリジェクトされる

ActionsでPDFを生成している間に別のcommitが入ると、pushがfetch firstエラーになる。

解決策: stash → pull rebase → pop → commit → pushの順番にする。

git stash
git pull --rebase origin main
git stash pop
git add asset/brochure.pdf
git commit -m "Auto-generate PDF [skip ci]"
git push

[skip ci]をcommitメッセージに入れることで、PDF生成のcommitがActionsを再トリガーするループを防ぐ。

4. Dockerコンテナ内でgitが「not in a git directory」になる

--user rootでコンテナを起動しているため、gitがリポジトリを別ユーザーのディレクトリと判断してブロックする。

解決策:

git config --global --add safe.directory /__w/lp/lp

パスはリポジトリ名によって変わる(/__w/{repo}/{repo}の形式)。

5. 日本語フォントがなく文字化けする(aptインストール方式の場合)

ubuntu-latestapt-get install fonts-noto-cjkする方式だと毎回60MB以上のダウンロードが発生して遅い。

解決策: ghcr.io/puppeteer/puppeteerイメージに切り替える。このイメージにはNotoフォント含む日本語フォントが同梱されており、インストール不要。


Dockerイメージ選定の比較

方式 所要時間 日本語対応 絵文字対応
ubuntu-latest + apt install 4〜5分(毎回) ✅ apt installで可 ❌ 別途フォント必要
ghcr.io/puppeteer/puppeteer 1〜2分 ✅ 同梱 ❌ 絵文字フォントなし

絵文字問題はどちらの方式でも発生するため、SVGアイコンへの置き換えが根本的な解決策になる。


HTML側のprint CSS設計

ブローシャをWEBページとPDF両方に対応させるため、@media printでレイアウトを切り替えている。

/* 通常表示 */
.body-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
}

/* 印刷時 */
@media print {
    body { font-size: 11px; line-height: 1.42; }
    .body-grid { gap: 0.55rem; }
    .br-nav, .pdf-download, .br-footer { display: none; }
}

A4横1ページに収めるコツ:

  • ベースフォントは10〜11px(これ以上大きくすると2ページになりやすい)
  • 行間は1.4〜1.5(1.6以上は危険)
  • セクション間のmarginは2〜3mm
  • 絵文字はSVGアイコン(幅・高さ固定)に置き換える

WEBページからのダウンロードボタン

PDFが生成されたかどうかをfetchで確認して、存在する場合だけダウンロードボタンを表示する。

<a id="btn-dl" href="asset/brochure.pdf" download style="display:none;">
  ⬇ PDFダウンロード
</a>

<script>
fetch('asset/brochure.pdf', { method: 'HEAD' })
  .then(r => {
    if (r.ok) document.getElementById('btn-dl').style.display = 'inline-block';
  })
  .catch(() => {});
</script>

まとめ

やりたいこと 解決策
HTMLからA4横PDFを生成 Puppeteer + page.pdf({ landscape: true, width: '297mm' })
日本語フォント対応 ghcr.io/puppeteer/puppeteerイメージを使う
絵文字の文字化け インラインSVGアイコンに置き換える
Actions内でgit push stash → pull rebase → pop → commit → push
push競合ループ防止 commitメッセージに[skip ci]を入れる
gitのsafe.directory git config --global --add safe.directory /__w/{repo}/{repo}

HTMLを更新してpushするだけでPDFが自動再生成される構成が完成した。ブローシャの文言を磨くたびに手動でPDFを生成する手間がなくなり、常に最新版がダウンロードできる状態を維持できる。


この記事は TokiStorage の行政DXブローシャ開発の過程で得た知見をまとめたものです。ブローシャの設計思想についてはこちらのエッセイで詳しく書いています。

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?