🎟️

Github ActionsとIssuesでブログを書く

2022/06/21に公開

Github Issuesをブログとして使うアイデアは太古の昔からある。ここで言っているのはIssueを使ってブログ記事を書き、タグをつけ、フィルタや検索して管理しながら、そこからHTMLを生成するようなやつのことだ。ちょっと検索しただけでもいくつかの実装を見かける。

また、実装を解説したブログSimple GitHub Issues Powered Blogもある。いずれも基本的にはGithub Issuesをバックエンドとして使い、フロントのJavaScriptがIssuesのAPIから情報をとってHTMLを生成している。例えばこれなんかはシンプルでいい感じ。

Github Actionsを使う

今回書くのは、これのGithub Actions版だ。似たようなSPA版実装がたくさんあるのでGithub Actions版を作ってバランスをとることにする。Issuesの作成・更新をトリガーにGithub ActionsがサーバーサイドでHTMLをレンダリングしてWeb1.0な感じのサイトをつくってみる。Github Pagesでホストするので全てGithub内で完結することになる。

仕組み

Issueの作成からサイトのビルドまでの流れは以下のようになる[1]。ビルドに関してはGithub Actionsがさまざまなコンテキスト(例えばIssue Id、Issue Title、Account Nameなど)を持っているのでそれをHTMLへ埋め込むだけの処理になる。こういう処理にはテンプレートエンジンが便利だ。今回は懐かしのMustacheを使った。またIssuesに書き込まれた本文のMarkdownをHTMLに変換する部分はGithubにAPIがある。変換のライブラリを探してきてActions内で動かすつもりでいたのでこれは助かる[2]

Github Actions

Github Actionsの中身はこんな感じになる。このYAMLはあまり処理をしていなくて実態は後述するJavascriptのコード build-pages.js である。YAMLは環境変数をjsに渡すのとGithub Pagesへのpushを行なっているだけ。

build-pages.yaml
name: Build Pages
on: 
  issues:
    types: [opened, edited]

env:
  owner: ${{github.repository_owner}}
  repo: ${{github.event.repository.name}}
  target_issue_id: ${{github.event.issue.id}}

jobs:
  build-pages:
    runs-on: ubuntu-latest
    steps:
      - name: View the github context
        run: echo "$GITHUB_CONTEXT"
        env:
          GITHUB_CONTEXT: ${{ toJson(github) }}
          
      - name: View github-script context
        uses: actions/github-script@v6
        with:
          script: console.log(context)

      - name: Checkout blog repo
        uses: actions/checkout@v3

      - name: Install Octokit and Mustache
        run: npm install @octokit/rest mustache
      
      - name: Env test
        uses: actions/github-script@v6
        with:
          script: |
            console.log(process.env)
            
      - name: Build pages
        run: node src/build-pages.js

      - name: Git setting
        run: |
          git config --local user.email "your_email_address"
          git config --local user.name "${{github.actor}}"
          
      - name: Commit and push pages
        run: |
          git add index.html posts/${{github.event.issue.id}}.html
          git commit -m "update" -a
          git pull
          git push origin main
build-pages.js
const { Octokit } = require("@octokit/rest");
const octokit = new Octokit({});
const Mustache = require('mustache');
const fs = require("fs");
const df = require("./date-format.js")


// Build
octokit.rest.issues.listForRepo({
	owner: process.env.owner,
	repo: process.env.repo,
})
.then(issues => {
	issues.owner = process.env.owner
	issues.repo = process.env.repo

	// Format issue object
	issues.data.map(issue => issue.updated_at_short = df.shortDate(issue.updated_at))
	console.log("issues: ", issues)

	// Build index
	const index_template = fs.readFileSync("template/index.template.html", "utf8").toString();
	const index_html = Mustache.render(index_template, issues)
	fs.writeFileSync("index.html", index_html, "utf8");

	// Build post
	target_issue = issues.data.filter((ti) => {
		return ti.id == process.env.target_issue_id
	});
	target_issue = target_issue[0]

	markdown = target_issue.body
	const issue_template = fs.readFileSync("template/post.template.html", "utf8").toString();
	octokit.rest.markdown.render({"text": markdown, "mode": "gfm"})
	.then(issue_html => {
		target_issue.issue_html = issue_html
		console.log("target_issue: ", target_issue)

		const issue_page = Mustache.render(issue_template, target_issue)
		fs.writeFileSync("posts/" + process.env.target_issue_id + ".html", issue_page, "utf8");
	})

});

build-pages.js はHTMLのテンプレートを読み込んでそこにGithub Actionsのcontextの変数をMustacheを使って埋め込み、index.html<issue_id>.html を作るだけのシンプルな処理。ローカルでデバッグする際には以下のように環境変数を渡して実施していた。

export owner=your_account
export repo=blog
export target_issue_id=1198469729

Mustache

あとは好きなindex.html<post_id>.htmlのテンプレートファイルを用意してMustacheを使って<h1>{{title}}</h1>という感じで好きなHTMLに変数を埋め込むだけだ。APIから帰ってくるマークダウンから変換したHTMLをそのままHTML内に埋め込みたい場合は{{{html}}}と髭を3つにしてやればよい。

まとめ

またもやGithub Issueからブログをつくるやつを増やしてしまった。今回のはサーバー側で静的サイトを作るのでこれまでのSPAなサイトと違って、軽快でシンプルなサイトを作るのに向くと思う。また静的サイトジェネレータと比べてもビルドが自動化されていているので、その点でも楽ができるのではないだろうか。次は誰が書く?

脚注
  1. Github Pagesにホストするのであれば、マークダウンファイルを直接pushして、Jekyllでビルドする方法もある。Jekyllのやり方に合わせればこっちの方が簡単かもしれない。別途やってみたい。 ↩︎

  2. はじめいろいろと探して評価していたが必要なかった ↩︎

Discussion