ヘッドレスCMSのContentfulをHugoと連携してGithub Pagesで公開する(5)

# 概要

ContentfulとHugoを連携したコンテンツ作成の最終回。
ラストの今回はContentfulでのデータ更新をイベントトリガーとしてGithub Actionsを発火させる方法について解説する。

最終的には以下のような構成となる。

![contentful-hugo-Copy of Page-1.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/d6a2fec8-81d7-004d-965f-b32f2f928756.png)

注目してほしいのはワークフローのピンクのブロックで、投稿者がブログを更新したら自動的に公開されるということ。
つまり途中のゴチャゴチャした過程は意識しなくてよい。

最終的なソース → [higebobo/hugo\-contentful\-blog: Blog sample with Hugo and Contentful](https://github.com/higebobo/hugo-contentful-blog)
完成したブログ → [Hugo Contentful Blog](https://higebobo.github.io/hugo-contentful-blog/)

# 準備

既に説明してあるのでContentfulのトークンは取得済みとする(メニューからSettings>API Keys>Add API keyを実行)。

## Githubのトークン取得

GithubのAPIを利用するためには`Persoanl access tokens`と呼ばれるトークンが必要になる。
ダッシュボード画面右上の`Developper settings`>`Persoanl access tokens`>`Generate new token`を順次クリック。

![Screenshot at 2021-09-09 07-21-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/898defd0-02d3-b950-af41-4335593e7ea9.png)

名前は適当につけて`Expiration`は無期限設定の`No expiration`とし(もちろん期限付きで設定してもよい)、`Select scopes`はとりあえず`repo`をチェック。

![FireShot Capture 103 - New personal access token - github.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/436301a5-3a67-4a48-ba3d-847f88079bc5.png)

最後に`Generate token`をクリックすると発行される。二度とお目にかかれないと警告が出ているのでコピーしておくこと。
もし忘れたら再発行するしかない。

![FireShot Capture 104 - Personal Access Tokens - github.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/16c8f50a-5d46-3ac1-ad6b-4d185a42dad7.png)

ちなみにこのトークンはレポジトリではなくアカウントに紐付いているので既に発行済で別レポジトリで使用しているトークンがあればそちらをつかってもよい。

### 確認

curlが実行できる環境ならこの時点で設定確認ができる。

```shell
curl -X POST \
-H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token <取得したトークン>" \
-d '{"event_type": "Wehook test"}' \
https://api.github.com/repos/<アカウント名>/<レポジトリ名>/dispatches
```

これを実行して**何も返ってこなければ成功**。とやや紛らわしい。
なので上記に-iオプションをつけてみるとヘッダ情報が取得できる。

```
HTTP/2 204 
server: GitHub.com
...
```

204を返せばOK。
ちなみに例えばわざと間違ったトークンを渡すとこんなエラーが返ってくる。

```json
{
  "message": "Bad credentials",
  "documentation_url": "https://docs.github.com/rest"
}
```

## ContentfulのWebhook設定

次にコンテンツ更新をGithubにお知らせするためWebhookをContentfulに設定する。
ダッシュボード上部メニューの`Settings`>`Webhook`>`Add Webhook`をクリック。
そして設定を行う。
基本的には上記curlでの確認テストの項目を渡すように設定してやれば良い。

まず`Name`は適当で`URL`は`https://api.github.com/repos/<アカウント名>/<レポジトリ名>/dispatches`とする。
`Name`はなんでもよいがわかりやすくしたほうがよいので私の場合は`<アカウント名>/<レポジトリ名>`としている。

`Triggers`は`Select specific triggering events`にチェック。
`CONTENT EVENTS`は下図参照。ここまでの設定はこんな感じ。

![FireShot Capture 105 - New Webhook — Webhooks — The example project — Contentful_ - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/171f5d45-339a-dbbd-706f-04c5d9095e8f.png)

画面をスクロールして`Headers`のセクションまで行く。
ここで`Add custom header`をクリックしてヘッダーを追加する。ただしトークンのようにセキュリティ関連の情報は非表示にしたいのでその場合は`Add secret header`を使う。
設定するヘッダは以下

* Accept: application/vnd.github.everest-preview+json
* User-Agent: Contentful Webhook (値は適当でよい)
* Authorization: token <上記で取得したGithubのPersonal access tokens>

Authorizationの値は先頭に`token `を加えるのを忘れないように。
例えばトークンの値が`abc`だったら`token abc`とすること。

![FireShot Capture 106 - New Webhook — Webhooks — The example project — Contentful_ - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/75e6fa99-9e9e-9a60-53ed-4e88401fe75e.png)

更にスクロールして`Payload`まで行く。
GithubのAPIを叩くためにはリクエストボディに`event_type`という値が必要となる。
なので`Customize the webhook payload`を選択し以下のように適当に値を設定する。

![FireShot Capture 107 - New Webhook — Webhooks — The example project — Contentful_ - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/ada21d94-a29a-e259-73ea-4ef5c1d9330f.png)

最後に`Save`をクリックして完了。
このまま適当にコンテンツを更新してWebhookの起動を試してもよいが、受け側のGithubの設定ができていないのでそれを次に行う。

## Github Actionsの設定変更

Github Actionsは`repository_dispatch:`を追加するだけである。

`.github/workflows/gh-pages.yaml`(全文)

```yaml
name: GitHub Pages

on:
  push:
    branches:
      - main
  repository_dispatch: # ここ

jobs:
  deploy:
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}

    steps:
      - uses: actions/checkout@v2
        with:
          submodules: true
          fetch-depth: 0
      - name: Setup Node.js
        uses: actions/setup-node@v1
        with:
          node-version: 14.x
      - run: npm ci

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: 'latest'
          extended: true

      - name: Build
        # run: npm run build
        run: ./node_modules/.bin/contentful-hugo && hugo --minify --baseUrl="https://higebobo.github.io/hugo-contentful-blog/"
        env:
          CONTENTFUL_SPACE: ${{ secrets.CONTENTFUL_SPACE }}
          CONTENTFUL_TOKEN: ${{ secrets.CONTENTFUL_TOKEN }}

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public
```

上記修正を加えてレポジトリにプッシュすればブログのコンテンツは再ビルドされるが、これはワークフローでいうところの開発者のフローである。

## Contentfulに投稿して動作確認する。

実際にContentfulでのコンテンツ更新でビルドプロセスが走るか試してみる。
今現在は2つの投稿しか無い。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/fef55fc6-5654-7caa-6dbe-1768f17be79e.png)

新規投稿をしてみる。

![FireShot Capture 109 - contentful-hugoを用いた自動ビルドおよび公開 — Content — The example project — Conte_ - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/80da66b1-8a7f-7b5a-fa5a-e58eba96c91f.png)

書き終わったら`Publish`状態にする。

### Contenful側の確認

ダッシュボード>`Settings`>`Webhooks`をクリック。

![FireShot Capture 110 - Webhooks — The example project — Contentful - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/b0f92524-6f14-51ad-a6ee-8207fe688b42.png)

こんなふうになっていたら成功。念の為`view details`で下っていくと最終的なリクエストとレスポンスのログが確認できる。

![FireShot Capture 111 - higebobo _ hugo-contentful-blog — Webhooks — The example project — Co_ - app.contentful.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/2a0d6a95-78fb-0000-3306-15877043d420.png)

### Github側の確認

ダッシュボード>`Actions`をクリック。

![FireShot Capture 112 - Actions · higebobo_hugo-contentful-blog - github.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/d854faed-2375-d655-a247-748759e61a20.png)

最下行の`accespt webhook`（タイポでスマソ)がレポジトリのmainブランチにプッシュしたときのコミットログ。
その上にある`Webhook from Contentful`がContenful側からのWebhook。
上記payloadのevent_typeで設定した値が表示される。
3つあるのはコンテンツ作成時にブログ本文と画像とタグを新規登録したからそのたびにWebhookが走っている。

### そして

![FireShot Capture 113 - Hugo Contentful Blog - higebobo.github.io.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691494/bcc26dbc-f480-082f-e7f1-551558537d4e.png)

はい。できました。

# まとめ

五回に分けてHugoとContenfulの連携およびGithub Pagesでの自動公開を説明した。
スタティックサイトジェネレータとしてHugoに魅了されたのだが、その仕様ゆえヘッドレスCMSとの連携が難しいのを歯がゆく思っていた。
しかし、工夫次第でなんとでもなることを示すことができた。
仕様が固まりソース自体の変更が無い状態になれば、ContentfulとGithub間だけのサービスに完結する完全なサーバレス環境となる。
つまり、極端に言えばローカル環境にHugoやNode.jsは不要になるのだ。

最終的なソース → [higebobo/hugo\-contentful\-blog: Blog sample with Hugo and Contentful](https://github.com/higebobo/hugo-contentful-blog)
完成したブログ → [Hugo Contentful Blog](https://higebobo.github.io/hugo-contentful-blog/)

# 謝辞

Hugoの開発チーム、GithubやContenfulの運営者は当然ながら、このすばらしいライブラリを提供してくれた[ModiiMediaさん](https://github.com/ModiiMedia/contentful-hugo)、また[HugoのGithub Actions](https://github.com/peaceiris/actions-hugo)を作成していただいた[peaceirisさん](https://qiita.com/peaceiris)(いつもLGTMありがとうございます)に多大なる感謝を申し上げます。
これを機に少しでもHugoユーザが増えれば幸いです。