64
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Hugo】GitHubをヘッドレスCMSとして使う

Last updated at Posted at 2023-12-28

2024年2月23日 追記
GitHub Actions内のコマンドにミスがあったので修正しました。

変更前
run: github-issue-cms --token=${{ secrets.GH_TOKEN }}
変更後
run: github-issue-cms generate --token=${{ secrets.GH_TOKEN }}

こんにちは。

今回はGitHubをHugoのCMSとして活用してみました。

今回作成したツールは以下のリポジトリで公開しています。

Hugo + Headless CMS

私のブログではHugoをSSGとして利用しています。

新しく記事を執筆するときは、執筆環境にサイト管理用のリポジトリやVSCode、Hugoを用意してから執筆することになります。

この作業は一度やれば済むので、別にそこまで辛くはないのですが、問題は画像を配置するときです。

VSCodeを使って執筆している時に、新規に画像を追加すると以下の手順が発生します。

これを画像を追加するたびに行うのは非常に面倒です。

そこで、ヘッドレスCMSの登場です。これを利用すれば、QiitaやZennのようにクリップボードにある画像をそのまま配置できたり、画像をWordPressのように管理できたりします。

Hugoと相性の良いヘッドレスCMSを検索してみるとForestryやNetlify CMSなどがヒットします。
しかしながら、ForestryはTina CMSというプロジェクト変わっていたり、Netflify CMSはデプロイ先が限られたりとやや面倒そうです。

実際にTina CMSについては使用してみましたが、 UIが好みではなく、設定についてもやや限られている印象でした。(私が使いこなせてない可能性も大いにありますが...)

ここで、私は欲しいヘッドレスCMSは以下のような特徴を持つものです。

  • DBMSを必要としない
  • いつでも執筆環境にアクセスできる
  • ノーコストで運用ができる

GitHubという選択肢

みなさんご存知の通り、GitHubのIssueには優秀なMarkdownエディタがあります。
このエディタはクリップボードにある画像も貼り付けることができます。
テンプレート機能を利用すれば、以下のように記事の雛形を登録しておくことも可能です。

image.png

そして、GitHubはAPIを提供しており、GitHub上のデータを自由に利用できます。
ということで、CMSに必要なフロント部分としてGitHubを活用し、記事の生成部分を自分で作ります。

作ったもの

今回作成したものは、GitHub上のIssueを記事として扱います。
実行時にCloseされているIssueの本文を記事のコンテンツとしてファイルに保持します。

タグやカテゴリについては、それぞれラベルとマイルストーンが対応します。

Hugoが提供するフロントマター機能については、Issueの先頭にコードブロックを記述することで任意のコンテンツを挿入できるようにしています。

また、画像についても、IssueをMarkdownとして保存する際にダウンロードとリンクの張り替えを行います。

既存のHugoプロジェクトに組み込む

今回作成したものは、CIに組み込むことを想定しています。この記事の場合、データをすべてGitHub上に保管しており、CIとしてGitHub Actionsを利用しました。

GitHub Actionsで利用する際に、Issueへのアクセス権を持ったトークンを発行しておきます。

https://github.com/settings/tokensから新規Classicトークンを発行します。この時にrepoスコープに権限を与えるようにしてください。

image.png

発行したトークンをブログを管理しているリポジトリのシークレットにGH_TOKENという名前で登録します。

image.png

次にリポジトリのルートディレクトリにgic.config.yamlというYAMLファイルを作成します。

github.usernameに自分のユーザ名、github.repositoryに対象のリポジトリ名を記します。

hugo.url.imagesには、Hugoで画像を配置するディレクトリ名を書きます。

gic.config.yaml
github:
  username: 'your-name'
  repository: 'your-repository'

hugo:
  url:
    images: '/your-images-directory'

最後に以下のようなワークフローを定義します。

name: Go

on:
  push:
    branches: [ "main" ]
  issues:
    types: [reopened, closed]
  pull_request:
    branches: [ "main" ]
  workflow_dispatch:

permissions: write-all

jobs:

  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Set up Go
      uses: actions/setup-go@v4
      with:
        go-version: '1.21.4'

    - name: Install Tools
      run: go install github.com/rokuosan/github-issue-cms@latest

    - name: Generate
      run: github-issue-cms generate --token=${{ secrets.GH_TOKEN }}

    - name: Auto Commit
      uses: stefanzweifel/git-auto-commit-action@v4
      with:
        commit_message: "ci: :memo: Update articles"

    - name: Clouflare Pages Webhook
      run: |
        curl -X POST ${{ secrets.CF_WEBHOOK_URL }}

私はデプロイにCloudflare Pagesを利用しているため、ジョブの最後にCloudflareのデプロイ用Webhookに対してPOSTするようにしています。

Issue を書く

ワークフローを登録しているリポジトリで、Issueを書きます。

このとき、カスタムフロントマターを利用しやすくするために、Issueのテンプレートを作成しておくことをお勧めします。

リポジトリに.github/ISSUE_TEMPLATEという名前でディレクトリを作成し、以下のようなMarkdownを作成します。

article.md
---
name: Article
about: Template for a new article
title: ''
labels: ''
assignees: ''
---

```
slug: 'your-article-slug'
# url: '/articles/your-article-url'
```

## Title

デプロイの確認

最後に正常にデプロイがされているか確認します。

image.png

image.png

GitHubとCloudflareでビルドにかかる時間はそれぞれ30秒程度なので、Issueをクローズしてから1分ほどでデプロイが完了します。

作成したツールのインストールに時間がかかっているみたいなので、ビルド済みのものをリリースすれば時間短縮ができそうですね...。

終わりに

GitHubで記事が書けるとなると、今までの心理的ハードルもさがり、筆も走るような気がします。

また、外部サービスを利用しないので料金がかからないのでお財布にも優しいですね。その代わりGitHub APIの仕様変更などがあれば対応しなければなりませんが...。

64
40
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
64
40

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?