GitLab CI + docker-reviewを使って、Markdownをtextlintしてからpdf化するCI環境を作ってみた

最近、Markdownからpdfを作る機会がありました。

pdf化を都度行うのは手間だったため、何か良い方法がないかを探したところ、 Markdown > Re:VIEW > pdf という経路でpdfを作成できそうでした。

ただ、手動でpdfを生成するのが手間だったため、GitLab CIを使って、push後にMarkdownをtextlintしてからpdf化するCI環境を作ってみました。

 

目次

 

環境

項目 内容
CI環境 GitLab.comのCI
Lintツール textlint
Lint辞書 自作
Markdownからpdf化の方法 Markdown > Re:VIEW > pdf
MarkdownからRe:VIEWへの変換 md2review
Re:VIEWからpdfの変換 review-pdfmaker
pdfのレイアウト調整 Re:VIEWなどに詳しくないため、今回は行わない

 
また、Gitリポジトリディレクトリ構成は以下の通りです。

pdf化する対象のMarkdowntarget.md です。それ以外のファイルについては後述します。

$ tree
.
└── docs
    ├── catalog.yml
    ├── config.yml
    ├── prh.yml
    ├── sty
    │   ├── README.md
    │   ├── gentombow.sty
    │   ├── jsbook.cls
    │   ├── jumoline.sty
    │   ├── plistings.sty
    │   ├── review-base.sty
    │   ├── review-custom.sty
    │   ├── review-jsbook.cls
    │   ├── review-style.sty
    │   ├── reviewmacro.sty
    │   └── techbooster-doujin-base.sty
    ├── style-web.css
    ├── style-web.scss
    ├── style.css
    ├── style.scss
    └── target.md

 
また、対象のMarkdownファイル target.md は以下のような感じです。

(Pythonコードについては、実際はトリプルバッククォートで囲みましたが、はてなブログに貼り付ける都合上、トリプルシングルクォートにしてあります)

# タイトル

Pythonコードを書きます。

'''
print('Hello world')
'''


## りんごの時期

以下の表を参照のこと。

|項目|時期|
|---|---|
|夏あかり|お盆過ぎ|
|シナノドルチェ|9月中頃|
|シナノゴールド|10月過ぎ|

 

GitLab CIの設定ファイル .gitlab-ci.yml の作成

GitLabでCIを動かすために .gitlab-ci.yml が必要なので作成します。

 
今回は、以下のようなPipelineを作成します。

No 対象ブランチ 条件 ジョブの内容
1 全ブランチ GitLabへpush時 対象のMarkdownにtextlintを実行
2 指定ブランチ No.1成功時 Markdownをpdf化

 

ステージを定義

ジョブを順番に実行するため、ステージを2つ定義します (lintbuild)。

stages:
  - lint
  - build

 

ジョブを定義

2つのジョブ markdown_lintpdf_build を定義します。

また、ジョブの stage にて、各ジョブがどのステージで動作するかを指定します。

markdown_lint:
  stage: lint

pdf_build:
  stage: build

 

ジョブ「対象のMarkdownにtextlintを実行」の詳細を定義

ジョブ markdown_lint は、対象のMarkdownにtextlintを実行します。

以下を参考に、今回は textlinttextlint-rule-prh を使うことにします。
textlint + prhで表記ゆれを検出する | Web Scratch

 
textlintはNode.jsがあれば動作するようなので、Dockerの公式リポジトリから一番軽そうなイメージ node:11.12.0-alpine を使います。

before_script でtextlintのインストールを行い、 script でtextlintを実行します。

ジョブの全体は以下の通りです。

markdown_lint:
  image: node:11.12.0-alpine
  stage: lint
  before_script:
    - npm install -g textlint textlint-rule-prh
  script:
    - cd docs
    - textlint target.md

 

ジョブ「Markdownをpdf化する」の詳細を定義

ジョブ pdf_build は、Markdownファイルをpdf化します。

今回、Markdown > Re:VIEW > pdfの変換では、以下のツールを使います。

 
Dockerイメージは、必要なファイルがすべて含まれている vvakame/review を使います。ありがとうございます。
https://github.com/vvakame/docker-review

 

あとは、 before_script にて必要なものをインストールし、 script にて変換を行います。

pdf_build:
  image: vvakame/review
  stage: build
  before_script:
    - apt-get update -y
    - apt-get -y install build-essential ruby-dev
    - gem install md2review
  script:
    - cd docs
    - md2review target.md > target.re
    - review-pdfmaker config.yml

 
なお、変換後のpdfをダウンロードできるようにするため、 artifacts を定義します。

今回は、中間でできる target.re ファイルやpdfなど、Git管理外ファイルをダウンロードするため、 untracked: true とします。

artifacts:
  untracked: true

 
また、pdfをビルドするブランチを masterfeature/* に制限するため、 only も定義しておきます。

  only:
    - master
    - /^feature\/.*$/

 
設定ファイル .gitlab-ci.yml の全体は以下の通りです。

stages:
  - lint
  - build

markdown_lint:
  image: node:11.12.0-alpine
  stage: lint
  before_script:
    - npm install -g textlint textlint-rule-prh
  script:
    - cd docs
    - textlint target.md

pdf_build:
  image: vvakame/review
  stage: build
  before_script:
    - apt-get update -y
    - apt-get -y install build-essential ruby-dev
    - gem install md2review
  script:
    - cd docs
    - md2review target.md > target.re
    - review-pdfmaker config.yml
  artifacts:
    untracked: true
  only:
    - master
    - /^feature\/.*$/

 

textlint向けの定義

以下を参考に、textlint向けの定義を行います。
textlint + prhで表記ゆれを検出する | Web Scratch

今回は、 doc ディレクトリの中に、 prh.ymltextlintrc を作成します。

 

prh.yml

まずは prh.yml に、textlintでチェックする内容を記載します。

今回は「Python」という単語の表記ゆれはエラーとなるように設定します。

version: 1
rules:
  - expected: Python
    specs:
      - from: python
        to:   Python
      - from: PYTHON
        to:   Python

 

.textlintrc

textlint実行時に prh.yml を参照するよう設定します。

{
  "rules": {
    "prh": {
      "rulePaths": [
        "./prh.yml"
      ]
    }
  }
}

 

review-pdfmaker向けの設定

review-pdfmaker向けの設定については、以下のリポジトリを参考にしました。また、一部ファイルはそのまま利用させていただきました。ありがとうございました。
https://github.com/TechBooster/ReVIEW-Template

 

config.ymlの設定

review-pdfmaker を実行する時の定義ファイル config.ymldoc ディレクトリの中に作成します。

ReVIEW-Templateを元に、以下の内容を記載します。

# この設定ファイルでサポートするRe:VIEWのバージョン番号。
# major versionが違うときにはエラーを出す。
review_version: 3.0

# ブック名(ファイル名になるもの。ASCII範囲の文字を使用)
bookname: my_sample_book
# 記述言語。省略した場合はja
language: ja

# 書名
booktitle: {name: "サンプル本", file-as: "sample book"}

# 著者名。「, 」で区切って複数指定できる
aut: [{name: "thinkAmi", file-as: "thinkAmi"}]

# 以下はオプション
# a-edt, edt: 編集者
edt: ["my editor"]
# a-pbl, pbl: 出版社(発行所)
pbl: thinkami_pub.

# 刊行日(省略した場合は実行時の日付)
date: 2019-3-24
# 発行年月。YYYY-MM-DD形式による配列指定。省略した場合はdateを使用する
# 複数指定する場合は次のように記述する
# [["初版第1刷の日付", "初版第2刷の日付"], ["第2版第1刷の日付"]]
# 日付の後ろを空白文字で区切り、任意の文字列を置くことも可能。
history: [["2019-3-24 v0.0.1"]]
# 権利表記(配列で複数指定可)
# rights: (C) 2016 Re:VIEW Developers
rights: (C) 2019 thinkAmi

# デバッグフラグ。nullでないときには一時ファイルをカレントディレクトリに作成し、削除もしない
debug: null

# HTMLファイルの拡張子(省略した場合はhtml)
htmlext: html
#
# CSSファイル(配列で複数指定可、yamlファイルおよびRe:VIEWファイルを置いたディレクトリにあること)
stylesheet: ["style.css"]

# ePUBのバージョン (2か3)
epubversion: 3
#
# HTMLのバージョン (4か5。epubversionを3にしたときには5にする)
htmlversion: 5

# 目次として抽出する見出しレベル
toclevel: 3

# 採番の設定。採番させたくない見出しには「==[nonum]」のようにnonum指定をする
#
# 本文でセクション番号を表示する見出しレベル
secnolevel: 3

# 本文中に目次ページを作成するか。省略した場合はnull (作成しない)
toc: true

# 表紙の後に大扉ページを作成するか。省略した場合はnull (作成しない)
titlepage: true

# 奥付を作成するか。デフォルトでは作成されない。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる
colophon: true

# EPUBにおけるページ送りの送り方向、page-progression-directionの値("ltr"|"rtl"|"default")
direction: "ltr"

epubmaker:
  # HTMLファイルの拡張子
  htmlext: xhtml
  stylesheet: ["style.css","epub_style.css"]

# LaTeX用のスタイルファイル(styディレクトリ以下に置くこと)
texstyle: ["reviewmacro"]

# B5の設定(10pt 40文字×35行) - 紙版
texdocumentclass: ["review-jsbook", "media=print,paper=b5,serial_pagination=true,hiddenfolio=nikko-pc,openany,fontsize=10pt,baselineskip=15.4pt,line_length=40zw,number_of_lines=35,head_space=30mm,headsep=10mm,headheight=5mm,footskip=10mm"]

pdfmaker:
  # 奥付を作成するか。trueを指定するとデフォルトの奥付、ファイル名を指定するとそれがcolophon.htmlとしてコピーされる
  colophon: true

webmaker:
    stylesheet: ["style.css","style-web.css"]

 

pdf化するコンテンツの設定 (catalog.yml)

pdfに含まれるコンテンツの設定を行います。

本文部分のRe:VIEWファイルは、 target.md が変換された target.re になります。そのため、 target.reCHAPS に指定します。

PREDEF:

CHAPS:
  - target.re

APPENDIX:

POSTDEF:

 

デザインまわりのファイルのコピー

今回デザインまわりは何も修正しないため、リポジトリ TechBooster/ReVIEW-Template のものをそのままコピーしました。

コピーするのは以下です。

 

以上で準備が終わりました。

 

動作確認

masterブランチ

今までの作業を master ブランチにコミットし、GitLabにpushします。

すると、以下の画像のように、Lintとpdfの生成ができました。

右側にあるダウンロードボタンを押すことで、Re:VIEWとpdfのファイルがzip化されたものがダウンロードできます。

f:id:thinkAmi:20190326223859p:plain

 

featureブランチ

次に、Lintエラーが出るように以下を追加した target.md をfeatureブランチにcommitします。

- python
- PYTHON

 
GitLabにpushしてCIを確認すると、Lintチェックエラーになりました。

f:id:thinkAmi:20190326224408p:plain

また、後続のpdf作成ジョブは実行されていませんでした。

f:id:thinkAmi:20190326224054p:plain  
 

lint-onlyブランチ

最後に、Lintエラーが出るように以下を追加した target.mdlint-only ブランチにcommitします。

- Lintだけします
  - python

GitLabにpushしてCIを確認すると、Lintチェックエラーになりました。

f:id:thinkAmi:20190326224738p:plain

 
また、今までと異なり、後続ジョブ pdf_build がありません。

f:id:thinkAmi:20190326224755p:plain  

以上で、GitLab CI + docker-reviewを使って、Markdownをtextlintしてからpdf化するCI環境ができました。