GitHub Actionsで記事の投稿を自動化

投稿日: 2025-02-20(木)

はじめに

このブログはGitHub Pagesを使用して公開している。

記事自体はorg-modeで書いたあと、Hakyllで投稿用のHTMLに変換して投稿しているが、これを毎回手動で実施するのは手間なので、GitHub Actionsで自動化することにした。

構成は以下の通り。

自動化に際し、以下2つのリポジトリを作成した。

  • 記事のソースや静的サイトジェネレータを格納する chupaaaaaaan-blog (以下、blog-source)
  • GitHub Pagesで公開するための chupaaaaaaan.github.io (以下、pages)

blog-sourceリポジトリで記事毎にブランチを作成して下書きを執筆し、公開するタイミングで main ブランチへのPRを作る運用にしてみた。 PRがマージされたら、記事がpagesリポジトリでビルド・公開されるようになっている。 下書きについては非公開としたいので、blog-sourceリポジトリはプライベートリポジトリにしている。 記事のビルドはどちらのリポジトリでも可能だが、プライベートリポジトリでGitHub Actionsの無料利用枠を使い切ったら悲しいので、パブリックリポジトリのpagesリポジトリで行うようにした。

フロー詳細

①blog-sourceリポジトリ: 記事ブランチを main ブランチにマージ

前述の通り、 main ブランチへのPRをマージするだけ。

②③blog-sourceリポジトリ: PRのマージをpagesリポジトリに通知

blog-sourceリポジトリのPRマージ完了をpagesリポジトリ側に通知し、ビルド・公開のワークフローを実行する必要がある。 通知する方法はいくつか考えられるが、今回はPRマージ時にpagesリポジトリに対して repository_dispatch イベントを発行するワークフローを作成した。

以下のような感じになる(プライベートリポジトリのワークフローなので、URLは省略)

name: Trigger chupaaaaaaan.github.io Workflow on PR Merge

on:
  pull_request:
    types:
      - closed
    branches:
      - main

jobs:
  notify:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: Notify chupaaaaaaan.github.io workflow
        env:
          TARGET_REPO: chupaaaaaaan/chupaaaaaaan.github.io
          EVENT_TYPE: pr_merged
          PAT: ${{ secrets.PAT_NOTIFY_BLOG_SITE }}
        run: |
          curl -f -X POST \
            -H "Accept: application/vnd.github.v3+json" \
            -H "Authorization: Bearer ${PAT}" \
            https://api.github.com/repos/${TARGET_REPO}/dispatches \
            -d "{\"event_type\": \"${EVENT_TYPE}\"}"

なお、 repository_dispatch イベントを発行するためには、適切な権限を設定したパーソナルアクセストークンが必要となるので、忘れずに作成してシークレットに登録しておく。 今回は、Fine-grained personal access tokenを作成した。

④⑤pagesリポジトリ: blog-sourceリポジトリから資材を取得し、ビルド・公開

前段で通知を受け取ったら、blog-sourceリポジトリの資材を取得し、ビルド・公開する。

ワークフローは以下の通り。

update-blog-site.yml

name: Update Blog Site
on:
  workflow_dispatch:
  repository_dispatch:
    types:
      - pr_merged

jobs:
  build-pages:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout blog source
        env:
          TARGET_REPO: chupaaaaaaan/chupaaaaaaan-blog
          DK: ${{ secrets.DK_CLONE_BLOG_SOURCE }}
        uses: actions/checkout@v4
        with:
          repository: ${{ env.TARGET_REPO }}
          ssh-key: ${{ env.DK }}

      - name: Setup haskell environment
        uses: haskell-actions/setup@v2
        id: setup
        with:
          ghc-version: 9.6.6
          cabal-version: 3.12.1
          cabal-update: true

      - name: Configure the build
        run: |
          cabal configure --enable-tests --enable-benchmarks --disable-documentation
          cabal build all --dry-run

      - name: Restore cached dependencies
        uses: actions/cache/restore@v4
        id: cache
        env:
          KEY: ${{ runner.os }}-ghc-${{ steps.setup.outputs.ghc-version }}-cabal-${{ steps.setup.outputs.cabal-version }}
        with:
          path: ${{ steps.setup.outputs.cabal-store }}
          key: ${{ env.KEY }}-plan-${{ hashFiles('**/plan.json') }}
          restore-keys: ${{ env.KEY }}-

      - name: Install dependencies
        if: steps.cache.outputs.cache-hit != 'true'
        run: cabal build all --only-dependencies

      - name: Save cached dependencies
        uses: actions/cache/save@v4
        if: steps.cache.outputs.cache-hit != 'true'
        with:
          path: ${{ steps.setup.outputs.cabal-store }}
          key: ${{ steps.cache.outputs.cache-primary-key }}

      - name: Setup pages
        id: pages
        uses: actions/configure-pages@v5

      - name: Build pages
        run: cabal run site build

      - name: Upload pages artifact
        uses: actions/upload-pages-artifact@v3

  deploy-pages:
    needs: build-pages
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    permissions:
      id-token: write
      pages: write
    runs-on: ubuntu-latest
    steps:
      - name: Deploy pages
        id: deployment
        uses: actions/deploy-pages@v4  

blog-sourceリポジトリはプライベートリポジトリのため、資材をチェックアウトするためには適切な権限を設定したパーソナルアクセストークンか、デプロイキーが必要になる。 今回はblog-sourceリポジトリでデプロイキーを作成し、シークレットとして登録した。

資材チェックアウト以降の処理は、以下を参考にした。

GitHub ActionsからGitHub Pagesを公開するためには、カスタム GitHub Actions ワークフローによる公開に記載の通り、公開元を「GitHub Actions」に設定しておく必要がある(これをせずに、 deploy-pages ジョブが延々謎のエラーで失敗し続けるのに悩まされた。。。)。

終わりに

実は以前もGitHub Pagesでブログを公開していたのだが、途中で面倒になって途絶えてしまった。。。(以前公開していたもの) 今回は多少やりやすいように整備したので、またモチベを上げて書いていきたい。 また、過去の記事のいくつかは、折を見てこのブログで再度公開するつもり。

今回は静的サイトジェネレータにはほとんど触れていない(ほとんど hakyll-init で生成したものをそのまま使っている)。 過去に作った静的サイトジェネレータの機能を移植したり、他にもなにか思いついたら改良したい(今のままだと、コードハイライトすらないので・・・)。

自動化を試すにあたって、以下のウェブページを参考にさせていただきました。 Hakyllで個人ウェブページを作りましたので全体概要を紹介