VSCodeでTeXファイルを編集してGitHub Actionsでコンパイル

# はじめに

去年の年末からTeXを書きはじめ、色々調べて環境を構築したので記事にしたいと思います。
**LuaLaTeX** + **llmk**が実行できる環境をDockerfileで作成して、VSCodeで執筆、GitHubにファイルをプッシュすることで、GitHub ActionsでCI/CDを実行して、releaseにPDFファイルをアップロードするような仕組みを作りました。
リポジトリ：[pyama2000/tex2pdf: Tex files](https://github.com/pyama2000/tex2pdf)

# Step 1. (La)TeX環境をDockerで構築

なぜDockerで環境を構築したかというと、以前macOSにTeX Liveをインストールした際に作業が複雑でゴミファイルも多数残ってしまい、それがトラウマとなっているからです。そこで、インストール等に失敗してもホスト側に影響がない、また端末・OSなどの環境に左右されずにどこでも執筆できるようにDockerを用いました。

はじめの頃は[Paperist/docker-alpine-texlive-ja: Minimal TeX Live image for Japanese based on alpine](https://github.com/Paperist/docker-alpine-texlive-ja)こちらのイメージを使ってPDFファイルを生成していましたが、調べていくうちにLuaLaTeXが比較的新しいLaTeXエンジンであることがわかったので、LuaLaTeXが使える環境を作成していきたいと思います。

 **※ これから説明する手順で作成するDockerfileをビルドしたものはDocker Hubに上がっているので、特に必要がない方は以下のコマンドを実行していただいて、この章をとばすこともできます。**

```bash
$ docker pull pyama0327/lualatex-haranoaji-llmk:latest
```

## Dockerイメージ概要

Dockerfileを作成するにあたって、[TeX Live 2019 + 原ノ味フォント + llmk の Docker コンテナを作る - Qiita](https://qiita.com/doraTeX/items/f40f9597763ca63daabb)を参考にして作成しています。こちらの記事ではTeX Live 2019をフルでインストールしているのですが、できるだけイメージのサイズを減らしたかったのとLuaLaTeXのみを使用するため、ベースイメージを**alpine**、TeXの環境を**schema basic + collection-langjapanese**としました。
LaTeXのコンパイルにlatexmkを利用することが主流らしいですが、今回は参考にした記事と同様に`llmk`という比較的新しいツールを使ってコンパイルしたいと思います。また、フォントも原ノ味フォントを使用しています。

## Dockerfile

この記事では一部分しか解説しないので、完全版が見たい場合は[こちら](https://github.com/pyama2000/tex2pdf/tree/master/lualatex)を確認してください。

### Step 1-1. (Lua)LaTeXをインストール

今回はTeX Liveに含まれるすべてのパッケージをインストールする**scheme-full**ではなく、なるべくイメージサイズを小さくするために**scheme-basic**を利用します。このスキームはLaTeXを使うために必要な最小限のパッケージをインストールします。
さらに日本語に対応するために**collection-langjapanese**や**collection-latexextra**などを`tlmgr`でインストールします。

### Step 1-2. `llmk`をインストール

`llmk`は以下のコマンドを実行するだけでシンプルにインストールすることができます。

```shell
$ wget -qO /usr/local/bin/llmk https://raw.githubusercontent.com/wtsnjp/llmk/master/llmk.lua
$ chmod +x /usr/local/bin/llmk
```

### Step 1-3. 原ノ味フォントをインストール、TeXに適用

LuaLatexはすでに原ノ味フォントを利用できるプリセットに対応しているため、複雑な作業は必要ありません。

```shell
# 原ノ味フォントをダウンロード
$ git clone https://github.com/trueroad/HaranoAjiFonts.git
# TeXのフォントディレクトリーにコピー
$ mkdir -p "${FONT_PATH}/opentype/haranoaji"
$ cp -p ./HaranoAjiFonts/*.otf "${FONT_PATH}/opentype/haranoaji/"
$ rm -rf ./HaranoAjiFonts
# TeXで利用できるようにする
$ mktexlsr
```

### Step 1-4. イメージを作成、DockerHubに公開

完成したDockerfileをビルドしてイメージを作成してDockerHubに公開しましょう。
この作業は必須ではありませんが、この環境を構築するのに5分以上かかるのでビルドして公開したほうがVSCodeやGitHub Actionsで利用する際に時間短縮になります。
**※ Docker Hubにログインしている前提です。**

```shell
$ docker build -t [イメージ名(:タグ名)] [Dockerfileのあるディレクトリ]
$ docker push [イメージ名(:タグ名)]
``` 

# Step 2. VSCodeで執筆

## Step 2-1. 拡張機能をインストール

VSCodeの拡張機能である`Remote Development Extension Pack`をインストールすると、VSCodeの機能をそのままにDocker内でコードを書くことができます。
![Screenshot 2020-01-08 at 00.36.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227668/06a17b31-0cc0-8b21-d01c-b662ba7eff5c.png)

## Step 2-2. コンテナの設定

### Step 2-2-1. 利用するイメージを記述したDockerfileを準備

作業するディレクトリ直下に以下のようなDockerfileを用意してください。

```dockerfile:Dockerfile
# Step 1-4で作成したイメージの名前とタグ名
FROM イメージ名:タグ名
```

ビルドしていない場合は、上記のDockerfileの代わりに**Step 1**で作成したDockerfileをコピーしてください。
ただし、コンテナに入るたびにビルドが走るため時間がかかります。

### Step 2-2-2. コンテナに入る

VSCodeで <kbd>Ctrl + Shift + p</kbd> なりでコマンドパレットを起動して、`Remote-Conteiners: Open Folder in Container...`を実行すると、ディレクトリ直下のDockerfileを自動でビルドしてコンテナに入ることができます。

### Step 2-2-3. コンテナ内で適用する設定

`.devcontainer`ディレクトリ直下に`devcontainer.json` を配置することでコンテナ内でのみ適用される設定を記述できます。
くわしくは[こちら](https://github.com/pyama2000/tex2pdf/blob/master/.devcontainer/devcontainer.json)にあるので参考にしてください。

編集する項目は以下の通りです。

```json:devcontainer.json
{
...
    "dockerFile": "./DockerFile",
...
    "setting": {
...
        "latex-workshop.latex.recipes": [
            {
                "name": "llmk",
                "command": "llmk"
            },
            {
                "name": "clean",
                "command": "llmk",
                "args": [ "--clean" ]
            },
            {
                "name": "clobber",
                "command": "llmk",
                "args": [ "--clobber" ]
            }
        ],
        "latex-workshop.latex.recipes": [
            {
                "name": "llmk",
                "tools": [ "llmk", "claen" ]
            },
            {
                "name": "clean",
                "tools": [ "llmk", "clobber" ]
            }
        ],
        "latex-workshop.view.pds.viewer": "tab",
        "editor.renderControlCharacters": true,
        "window.zoomLevel": 0,
        "latex-workshop.message.update.show": false
    },
...
   "extensions": ["james-yu.latex-workshop"] 
```

**Step 2-2-1**で作成したDockerfileを`.devcontainer`直下に移動させて、もう一度ビルドし直すと執筆環境が出来上がります。

# Step 3. GitHub Actionsを設定

**Step 1**、**Step 2**だけでTeXの執筆環境・PDF生成の手順は揃いましたが、個人的にGitHub Actionsを使ってみたかったので、TeXファイルをGitHubにタグ付きでプッシュするとGitHubのreleaseページにアップする仕組みを作りました。

## ワークフローを作成

`.github/workflows`直下に`main.yml`を作成します。このYAMLファイルがワークフローを定義する設定ファイルです。
詳しい書き方は公式サイトや他の記事を参考にしてカスタマイズしてください。
完成したファイルは[こちら](https://github.com/pyama2000/tex2pdf/blob/master/.github/workflows/main.yml)にあります。

### ファイル構成

```
.github
├── actions
│  └── release
│     ├── Dockerfile     # 独自定義したアクションで使用するコンテナ
│     └── entrypoint.sh  # コンテナ内で実行するスクリプト
└── workflows
   └── main.yml          # ワークフローを定義
```

### Step 3-1. 公開イメージを利用する

**Step 1-4**で公開したDockerイメージを利用してPDFを生成するので、それを`main.yml`に記述します。

```yaml:.github/workflows/main.yml
name: TeX2PDF
on: push

jobs:
  create:
    runs-on: ubuntu-latest
    # ↓で利用するイメージを指定します
    container: pyama0327/lualatex-haranoaji-llmk
    steps:
    # ↓を実行しないとファイルが取得できません
    - uses: actions/checkout@v1
    - name: Print hello
      run: echo "Hello, world!"
```

上記ファイルをプッシュするとActionsが走って、`Hello, world!`と出力されます。

### Step 3-2. PDFファイルを生成

すでにTeXファイルからPDFファイルを生成する方法はイメージ内にあるので、TeXファイルと`llmk.toml`をプッシュしてPDFを生成するコマンドを上記で作成したファイルに記述します。

```yaml:.github/workflows/main.yml
    steps:
    - uses: actions/checkout@v1
#     - name: Print hello
#       run: echo "Hello, world!"
        working-directory: ./texfiles
    - name: Create PDF from TeX
      run: llmk
    - name: Set filename to $FILE_NAME
      run: echo "::set-env name=FILE_NAME::`basename *.pdf .pdf`"       
      working-directory: ./texfiles
```

`Create PDF from TeX`ステップではllmk.tomlファイルを読み込んで`llmk`を実行しています。今回は、`texfiles`ディレクトリ以下にllmk.tomlやTeXファイルがあるので、working-directoryで指定しています。
`Set filename to $FILE_NAME`ステップは後ほど使うのですが、前のステップで作成したPDFファイルの拡張子を抜いたファイル名を環境変数にセットしています。

### Step 3-3. PDFファイルをreleaseページにアップロード

`main.yml`で実行するアクションを独自に定義できるので、この機能を利用してアップロードします。

jsonを`jq`で処理したかったので、alpineに`jq`をいれるDockerfileを作成しました。

```dockerfile:.github/actions/release/Dockerfile
FROM alpine:latest

RUN apk upgrade --update && \
    apk add --no-cache jq curl

ADD entrypoint.sh /entrypoint.sh
RUN ["chmod", "+x", "/entrypoint.sh"]
ENTRYPOINT ["/entrypoint.sh"]
```

`entrypoint.sh`はreleaseを作成し、PDFをアップロードするスクリプトです。

```bash:.github/actions/release/entrypoint.sh
#!/bin/ash
set -eux

TAG_NAME=`echo ${GITHUB_REF:10}`

REQUEST=`curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -X POST https://api.github.com/repos/${GITHUB_REPOSITORY}/releases \
  -d "
  {
    \"tag_name\": \"$TAG_NAME\",
    \"target_commitish\": \"$GITHUB_SHA\",
    \"name\": \"$FILE_NAME.pdf\",
    \"draft\": false,
    \"prerelease\": false
  }"`

RELEASE_ID=`echo ${REQUEST} | jq ".id"`

curl -H "Authorization: token ${GITHUB_TOKEN}" \
  -X POST https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${TAG_NAME}.pdf \
  --header 'Content-Type: application/pdf' \
  --upload-file texfiles/${FILE_NAME}.pdf
```

`Upload the PDF to GitHub release`ステップを以下のように定義することで、上記のDockerfileをビルドし、entrypoint.shが実行されます。

```yaml:.github/workflows/main.yml
    steps:
    ...
    - name: Upload the PDF to GitHub release
      uses: ./.github/actions/release      
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

### Step 3-4. 実行される条件を変更する

ただしこのままでは、プッシュされたファイル全てに対してActionsが実行されるので、TeXファイルとllmk.tomlがプッシュされ、タグ付けされたものがプッシュされたら実行するように変更します。

```yaml:.github/workflows/main.yml
...
on:
  push:
    paths:
    - texfiles/**.tex
    - texfiles/llmk.toml
    tags:
      - v*.*.*
...
```

これで、`v*.*.*`とタグ付けされたコミットをプッシュすることでGitHub Actionsが実行されるようになり、releaseページにアップロードされます。

# おわりに

つたない文章、かつコードが多いため分かりづらい部分が多いとは思いますが、更新してきれいにし着たいと思います。
また、それぞれのトピックの情報量が少ないため、いつか記事を分割して詳しくしていきたいです。

# 参考情報

### Dockerfile
- [TeX Live 2019 + 原ノ味フォント + llmk の Docker コンテナを作る - Qiita](https://qiita.com/doraTeX/items/f40f9597763ca63daabb)
- [Paperist/docker-alpine-texlive-ja: Minimal TeX Live image for Japanese based on alpine](https://github.com/Paperist/docker-alpine-texlive-ja)
- [y-yu/new-year-letter: 年賀状](https://github.com/y-yu/new-year-letter)

### llmk
- [llmk プロジェクトが始動しました | ラング・ラグー](https://blog.wtsnjp.com/2018/08/13/llmk-launch/)
- [wtsnjp/llmk: The Light LaTeX Make](https://github.com/wtsnjp/llmk)

### Github Actions
- [GitHub Actions で TeX のコンパイルと PDF 化をさせようとしたら意外に苦戦した話 - Qiita](https://qiita.com/denkiuo604/items/63b8c34a60a2340727b1)
- [GitHub Actions で TeX をコンパイル・PDF 化してタグ付きリリースする話 - Qiita](https://qiita.com/denkiuo604/items/137a1b3fc1955cfb9c58)
- [GitHub Actions で Latexmk＆タグを使ってコンパイルする TeX ファイルを指定する話 - Qiita](https://qiita.com/denkiuo604/items/c33088b1ab542f354d22)
- [新 GitHub Actions 入門 - 生産性向上ブログ](https://www.kaizenprogrammer.com/entry/2019/08/18/205010#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%A8%E7%A7%98%E5%AF%86%E6%83%85%E5%A0%B1)
- [GitHub Actionsの使い方 | 純規の暇人趣味ブログ](https://jyn.jp/github-actions-usage/)

### その他
- [TeXでつくるプロポーショナル組日本語文書 - Qiita](https://qiita.com/zr_tex8r/items/0512dd43e9806483013a)
- [ヒラギノフォント - TeX Wiki](https://texwiki.texjp.org/?%E3%83%92%E3%83%A9%E3%82%AE%E3%83%8E%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88)
- [macOS 付属のヒラギノフォントを Docker コンテナ内の TeX Live から使えるようにする - Qiita](https://qiita.com/doraTeX/items/5df8b0cd7e53d5b30b91)
- [Atom と LuaLaTeX と BibTeX，おまけに Inkscape - 毎日もくもく](http://xartaky.hatenablog.jp/entry/2017-09-21-atom-lualatex)
- https://qiita.com/zr_tex8r/items/a924be192ecea7e6bbe4
- https://qiita.com/homines22/items/3848b0950f472ded82a8