ふぃーるどのーつ

技術系ブログ「ふぃーるどのーつ」

GitHub ActionsでCSS組版のビルドを自動化する

このエントリーは、技術同人誌 Advent Calendar 201920日目エントリーです。(またしても1日遅れ…)

adventar.org

CSS組版のビルドの自動化

Vivliostyleを用いたCSS組版のビルドにおいては、通常、以下の様な フローで成果物であるPDFを生成します。

この方法ではWebブラウザーを立ち上げて、印刷メニューから印刷を行うわけですが、入稿間際の修正が頻繁になる時などは、自動化したくなるのが人情というものです。特に共同執筆で締め切り間際のコミットラッシュの時とかそうですね。

Chrome59から導入されたHeadless Chromeを使うと、このようなCSS組版のビルドの工程を自動化することができます。本エントリーは、CSS組版のビルドの工程をGitHub Actionsを使ってビルドする過程について述べるものです。

ヘッドレスChromeによるPDF作成

緑豆はるさめ(@spring_raining)さんの「CSSではじめる同人誌制作」によると、ヘッドレスChromeでPDFを出力するには、以下の様な工程を踏みます。

画面に表示されないChromeは、Chrome DevTool ProtocolというAPIと通じてプログラムを使って操作します。 PDF出力プログラムでは、このAPIを使ってヘッドレスモードのChromeと接続し、PDFの保存を指示します。

pentapod.booth.pm

何か難しそうですが大丈夫です。vivliostyle-cliという神ライブラリーがあります。このライブラリーを使用すると、ヘッドレスChrome上で印刷メニューを呼び出して、PDFを保存する操作を savepdf というコマンドで行う事が出来るようになります。

github.com

Markdownで書かれた原稿をPandoc でビルドし、ヘッドレスChrome上で動作するVivliostyleでレイアウトして、PDF出力するスクリプト(build.sh)は次の様になります。(抜粋)

cd src

pandoc  \
  -f markdown+smart+raw_tex+citations+yaml_metadata_block+fenAced_code_blocks+inline_notes \
  -c book.css --filter pandoc-crossref  \
  -M "crossrefYaml=${PWD}/../crossref_config.yaml" \
  --toc --toc-depth=2 \
  --filter pandoc-citeproc -o ../target/${OUTPUT}.html \
  --reference-location=block \
  -B cover.html -A imprint.html \
  tmp.md  --verbose

cd ../

RET=$?

mkdir -p target/img
cp -f src/*.css src/*.otf src/*.ttf target/
cp -rf src/img/* target/img/

npm install
node rewrite.js target/${OUTPUT}.html target/${OUTPUT}-rewrite.html

savepdf --no-sandbox -s JIS-B5  -o target/${OUTPUT}.pdf target/${OUTPUT}-rewrite.html

exit $RET

このスクリプトを呼び出す Dockerfile を記述します。

FROM (Azure Container Registryのドメイン)/pandoc-viola-savepdf-ja:2.6.1

RUN npm install -g npm@latest
RUN mkdir -p /tmp/code
ADD . /tmp/code/
RUN rm -rf /tmp/code/target
RUN mkdir -p /tmp/code/target

WORKDIR /tmp/code

CMD bash /tmp/code/build.sh

このDockerfileを使って、Docker上でビルドするスクリプト build-local.sh は次の通りです。

なお、Azure Container Registry上に格納しているDockerイメージを生成するDockerfileは、以下のGitHubレポジトリーで公開しています。*1

github.com

#!/bin/bash

set -eux
docker build -t tf2 .
docker run tf2

# ビルドしたPDFをコンテナーから取り出す
ID=$(docker ps -a |grep tf2 |awk '{print $1}')
docker cp ${ID}:/tmp/code/target . 

GitHub ActionsによるCSS組版のビルド

GitHub Actionsでは、Gitレポジトリーの .github/workflows ディレクトリー配下にビルドの設定を格納します。

レポジトリーのWebページ上の「Actions」タブを選択し、「Set up a workflow yourself」のボタンを選択すると、設定ファイルの編集画面に遷移します。この画面でyamlファイルを編集し、保存することで、Gitレポジトリーに設定ファイルをコミットすることができます。

f:id:setoazusa:20191221165512p:plain

f:id:setoazusa:20191221165540p:plain

GitHub Actionsで、VivlitstyleによるCSS組版のビルドを行うyamlの設定の例は次の通りです。

name: CI

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    steps:
     - name: Docker Login
       uses: Azure/docker-login@v1
       with:
         login-server: '(Azure Container Registryのドメイン)'
         username: '(Azure Container Registryのユーザー名)'
         password: ${{ secrets.crpassword }}
     - uses: actions/checkout@v1
     - name: Run build
       run: bash ./build-local.sh
     - name: Upload
       uses: actions/upload-artifact@v1.0.0
       with:
         name: sweetmudic-vol10.pdf
         path: target/sweetmusic-vol10.pdf
     - name: upload to dropbox
       run: bash ./deploy.sh
       env:
         DROPBOX_TOKEN: ${{ secrets.dropbox_token }}

ここではビルドを行うイメージをAzure Conrainer Registryに格納しているため、Docker Login を使ってDocker Registry にログインしています。

GitHub Actionsで外部リソースへの認証情報などを設定する場合は、レポジトリーの「Settings」→「 Secrets」の項目から、認証トークンなどの情報を保存します。yamlの設定ファイル内からは ${{ secrets.crpassword }} の形式で呼び出します。

f:id:setoazusa:20191221165646p:plain

最後の upload to dropbox の項目についてはこの次で説明します。

GitHub ActionsからDropboxへのアップロード

共同で執筆している場合などに生成したPDFへのアクセスを容易にするために、PDFをDropboxにアップロードし、Slackで通知することを考えます。

まず、DropboxのアクセストークンをCreate a new app on the DBX Platform にアクセスします。

f:id:setoazusa:20191221165727p:plain

最初のメニューでは「Dropbox API」を選択します。「Choose the type of access you need 」の項目は「App folder」を選択します。

メニューの指示に従って入力し、画面内の「Generated access token」の下の「Generate」をクリックしてトークンを作成します、取得したトークンをGitHubのSecretsに設定します。

f:id:setoazusa:20191221170757p:plain

先にあげたGitHub Actionsの設定例の中では、upload to dropbox のステップでDropboxへのアップロードを起こっています。アップロードを行うスクリプト deploy.sh の記述は次の通りです。

set -eux

export TZ="Asia/Tokyo"

TIMESTAMP=$(date "+%Y%m%d-%H%M%S")

TARGET="sweetmusic-vol10"

curl -X POST -f -H "Authorization: Bearer ${DROPBOX_TOKEN}" \
    -D - -H "Dropbox-API-Arg: {\"path\": \"/${TARGET}-${TIMESTAMP}.pdf\",\"mode\": \"overwrite\",\"mute\": false}" \
    -H "Content-Type: application/octet-stream" \
    --data-binary @target/${TARGET}.pdf \
    https://content.dropboxapi.com/2/files/upload

ここまでの設定を行った上で、作成した原稿をGitレポジトリーにコミットしてpushすると、GitHub Actionsによるビルドが行われ、Dropboxに作成したPDFがアップロードされます。

f:id:setoazusa:20191221165953p:plain

Slackへの通知

最後に、DropboxへのURLをSlackに通知します。

IFTTHZapier 等のサービスでDropboxとSlackを連携することにより、Dropboxへのファイルのアップロードをトリガーにして、SlackにダウンロードのURLの通知を行う事ができます。以下はZapierにより、DropboxとSlackのアカウントの連携を行っている例です。

f:id:setoazusa:20191221170103p:plain

ここまで設定を行うと、原稿をGitレポジトリーにpushするとビルドが行われ、作成したPDFへのリンクがSlackに通知されます。

f:id:setoazusa:20191221170529p:plain

まとめ

GitHub ActionsでCSS組版のビルドを自動化する工程について取り上げました。ここでは自動化のプラットフォームとしてGitHub Actionsを用いましたが、WerckerBitbucket Pipelines など、Dockerイメージをビルドに使用できるプラットフォームでも、同じ考えでビルドの自動化を組み込むことができます。

*1:ビルドすると5.8GBほどあるイメージができます