Think Proof, Count Two
本記事は TeX & LaTeX advent calendar 2019 の 12 日目の記事です. 11 日目の記事は doraTeX さんの “TikZ でベン図をたくさん描いてみよう” でした. 13 日目の記事は mattskala さんの予定です.
最近,ようやく研究がまとまってきたので論文を書き始めました.
人生初の論文執筆ですので指導教員に多大な迷惑をかけつつ,手探りで書きすすめています.
本記事の目的の一つは,添削をしていただくための .tex
ファイルの共有・編集履歴の管理・バックアップの作成などの,個人的なメモを残すことです.
また最近読了した『読みやすい技術書を書く技術』に触発され,GitLab を利用して LaTeX 文書を作成する試みについても述べたいと思います (あまりうまくいっていませんが). 『読みやすい技術書を書く技術』については以下を参照してください:
本記事では以下の事柄について扱いたいと思います:
このような技術に関しては全く詳しくありませんので,おかしい記述などありましたらお知らせいただけると喜びます.
筆者は,(いわゆる) 純粋数学を研究している学生です. したがって所々で数学の文書を念頭にいれた記述があると思います. 他の多くの分野では役に立ちにくい点に言及していたり,逆に役に立つであろう点に言及がない可能性があります.
また,普段は macOS または Linux1 を利用しています. Windows 利用者には向かない記述が多々ある可能性があります.
以上の点,どうかご寛恕ください.
本記事で扱った事柄についてサンプルリポジトリを用意しました. 適宜ご参照ください.
.tex
ファイルの編集履歴の管理には,git を利用するのをおすすめします.
筆者は以下のサイトで基本的な使い方を学びました:
バージョン管理ツールを利用することの利点は上記サイトを読んでいただければわかると思います.
筆者が git を使い始めた当初,コンフリクトが非常に怖かった覚えがあります. 筆者は試していませんが,コンフリクト解消の練習ができるサンドボックスが存在します:
昔は master 一本道の運用でしたが,最近は GitHub Flow を参考に運用するのが良いと考えています.
初心者のうちはうっかり git reset --hard
してしまったり,履歴を吹き飛ばすことがあります (少なくとも筆者はありました).
master ブランチへの push を禁止してトピックブランチから merge を行う,という運用にすればそのような悲劇はかなり減らせるでしょう.
GitHub Flow の詳細は適当に検索していただくとして,最近の筆者は
という運用をしています.
また,適切なタイミングでタグ (git tag
) などを利用すれば latexdiff-vc などのツールも使いやすくなります.
これらのツールについてはすでにいくつか解説記事がありますし,texdoc
でドキュメントが読めます.
他にも,指導教員が git を使えるならば直接 Merge Request (Pull Request) を送ってもらえる,ということもあります.
git ホスティングサービスには色々ありますが,ここでは GitLab.com を利用したいと思います. 理由として,プライベートリポジトリでの CI 利用が簡単そうである点が挙げられます2. GitLab.com の無料プランでは,2017 年 5 月 1 日以降,プライベートリポジトリでの CI は 1 グループあたり 2000 分/月に制限されているようです3. パブリックリポジトリならば制限はないようですが,執筆中の論文をパブリックリポジトリに上げることはないでしょう.
SSH 接続のための鍵の作成・登録はこちらを参照してください.
master ブランチを保護するには,プロジェクトページの Settings > Repository > Protected Branches
を開いて,Allowed to push
を No one
に設定すれば良いです.
共同編集者がいる場合は Allowed to merge
も適切に設定しておくのが良いでしょう.
GitLab.com で GitLab CI を利用するためにはプロジェクトルートに .gitlab-ci.yml
というファイルを置くだけで良いはずです.
.gitlab-ci.yml
の詳しい書き方はドキュメントを読むなどしてください.
本節ではいくつかの校正ツールを紹介します. 扱わないツールとしては
などがあります. textlint のプラグインは最近更新があったため,もしかしたら最新版ではうまく動くかもしれません. 『読みやすい技術書を書く技術』でこれらに関する解説を読むことができます.
reviewdog は校正ツールの結果を Merge Request にコメントしたりできるツールです.
reviewdog を利用するために REVIEWDOG_GITLAB_API_TOKEN
変数を設定しておく必要があります.
まず https://gitlab.com/profile/personal_access_tokens から GitLab Personal Access Token を取得します.
scope の範囲は api
を選択します.
プロジェクトページの Settings > CI/CD > Variables
を開き,取得したトークンを保存します.
Type
は Variable
, Key
は REVIEWDOG_GITLAB_API_TOKEN
とします.
また Masked
にチェックを入れておきましょう.
『読みやすい技術書を書く技術』ではコマンドラインオプションを用いて reviewdog を利用していましたが,.reviewdog.yml
という設定ファイルを用いることもできます.
筆者がしばらく使ってみたところ,merge-base commit を取得するのに失敗することがあります. 少なくともマージ先のコミットが,マージ元のコミットから辿れる場合は成功します (いまいちよくわかりません). GitLab CI の結果は GitLab から簡単に確認できるので,無理に reviewdog を利用する必要はないかもしれません.
スペルチェッカです.TeX Wiki に詳しい解説があるのでご存知の方も多いと思います. Emacs などのエディタから使うこともできます. 英語の文章を作成する際に大活躍します. ただしフランス語については公開されている辞書ファイルが古く,使い物になるかわかりません.
英語能力が貧弱なのはある程度仕方がないとしても,せめて簡単なスペルミスはスペルチェッカを利用して防ぎたいところです. CI で Aspell を利用するのはいくつかの理由から諦めました:
~/.aspell.conf
) を CI で利用する上手い方法が思いつかない~/.aspell.en.pws
) を CI で利用する上手い方法が思いつかないしたがって以下の方法が考えられます:
aspell
を走らせる例えば Emacs を利用しているならば before-save-hook
に ispell
を引っ掛ける,という方法が思い浮かびます4.
試していませんが,AUCTeX で .tex
文書を執筆しているならば
(add-hook 'LaTeX-mode-hook
'(lambda ()
(add-hook 'before-save-hook #'ispell-buffer nil 'local)))
とすればバッファ保存時に自動的に ispell
(あるいは aspell
や hunspell
) が走ると思います.
また git-hooks を利用する方法もあるでしょう.
次の内容を <project-root>/.git/hooks/pre-commit
という名前で保存します.
chmod a+x <project-root>/.git/hooks/pre-commit
などで実行権限を与えるのを忘れないように注意します.
#!/bin/bash
echo "Have you spell-checked? [y/N]"
exec < /dev/tty
read ANSWER
case $ANSWER in
"yes" | "Yes" | "YES" | "y" | "Y" ) echo "Ok, commit changes";;
"no" | "No" | "NO" | "n" | "N" ) echo "Stop commit"; exit 1;;
esac
exit 0
git commit
するとスペルチェック済か聞かれるので,そこで no
と答えるとコミットを中断します.
Bash 以外を使っている人は適当なスクリプトに書き直してください.
有名な LaTeX のシンタックス・チェッカです. Emacs や VS Code などのエディタから使うこともできます.
reviewdog から ChkTeX を利用するには,それぞれ .reviewdog.yml
runner:
chktex:
cmd: chktex -v0 -n8 ./main.tex
errorformat:
- "%f:%l:%c:%n:%m"
と .gitlab-ci.yml
chktex:
stage: lint
only:
- merge_requests
image:
name: mahito1594/chktex:latest
entrypoint: [""]
before_script:
- apt-get update && apt-get install -y git wget
- wget -O - -q ${INSTALL_URL} | sh -s
script:
- bin/reviewdog -runners=chktex -reporter=gitlab-mr-discussion
のように記述します.
ここで -v0
で chktex
のエラーフォーマットを指定し,-n8
で一部のエラーを抑制しています.
また mathito1594/chktex
は筆者の作成した Docker イメージです.
マージリクエストのサンプルはこちらです.
TeXtidote は LaTeX 文書のための文法チェッカです. 日本語には対応していないようですが,内部で使われている LanguageTool は日本語にも対応しているようです. プログラミングは全くできないのでよくわかりません.
Java (version 8 or later) が必要です. 詳しい使い方は README を参照してください. あまり使いこなせていないのか,特に役に立った印象が薄いです.
reviewdog だと errorformat の指定が良くないのか,うまく動いてくれません.
TeXtidote のオプション --output singleline
を指定して,reviewdog の方を -efm='%f(L%lC%c%.%#): %m'
とすれば良いと思うのですが…….
仕方がないので端末から使うようにします. 次のシェルスクリプトは,TeXtidote によるチェック結果を一時ファイルに書き込み,それをブラウザで開きます:
#!/bin/bash
readonly SCRIPT_NAME=${0##*/}
temp_file="$(mktemp -d -q)/textidote.html"
if [ $? -ne 0 ]; then
echo "${SCRIPT_NAME}: Can not create temp file, exiting..."
exit 1
fi
echo "Grammer check...start"
java -jar $(which textidote.jar) --output html "$@" > ${temp_file}
echo "Grammer check...done, open in the browser"
open ${temp_file}
exit 0
これを,例えば texreport
という名前で PATH の通ったディレクトリに保存します (忘れずに実行権限を与えてください).
texreport --check en main.tex
などのように使います.
他の TeXtidote のオプションも使えます.
ただし textidote.jar
を PATH の通ったディレクトリに置いてあることを仮定しています.
Linux を使っている方は open
を xdg-open
などに適宜置き換えてください.
また macOS を利用する場合は mktemp
コマンドの挙動に注意する必要があるかもしれません
(参考: https://rcmdnk.com/blog/2015/10/21/computer-mac-bsd-linux/).
『読みやすい技術書を書く技術』にも記述があるため,詳細はそちらに譲ります. reviewdog と併用する方法が紹介されています.
現状,数学の文書を執筆するにあたってはあまり役に立たない印象があります.
例えば,一文中のコンマの数が指定された最大数より多いと警告してくれる機能があります.しかし数式中のコンマまで数えられてしまうためあまり役に立ちません.
他にも Weak Expression と呼ばれる表現が現れると警告してくれますが,big
や some
にも反応するため数学の文章には向かない機能です.
機能一覧をよく見て,本当に必要な機能のみ利用するならば良いかもしれません.
さて,以前次のツイートを見かけました:
(La)TeX文書をgit(もしくは他のVCS)で管理している皆さんに質問ですが,できあがりのpdfもgit(VCS)に入れますか?
— Iwao KIMURA (@iwaokimura) October 23, 2019
たしかに .tex
ファイル等は git 管理で問題ないのですが,生成物である PDF をどのように管理するべきかは意見の分かれるところだと思います.
PDF はバイナリなので git で差分がわかりません5.
筆者は PDF を git 管理するご利益があまりないため git 管理していません.
一方で次のような意見もあります:
私はpdfも管理下に置く方に投票しました。原理的には同じものが生成されるとしても現実的には環境のためにそうならない場合がありますし、純粋にバックアップ代わりにしているからでもあります。
— 結城浩 (@hyuki) October 24, 2019
そこで GitLab CI & Docker で .tex
ファイルをコンパイルし,PDF を作成することにします.
この件に関しても先駆者はたくさんいらっしゃるので,詳細は検索してください.
今年の重点テーマは「やっぱり Lua(La)TeX しよう」なので LuaLaTeX を用いて PDF を作成してみたいと思います.
最近は源ノフォントを利用することが多いので,源ノ角明朝・源ノ角ゴシックを含んだ Docker イメージを作ります6.
作成した Dockerfile はこちらです.
またイメージはこちらから利用できます.
Noto フォントは apt
や apk
などからインストールできるため,Noto フォントを利用するほうが簡単かもしれません.
フォントファイルの配置については @zr_tex8r さんの記事 を参考にしたため,
uplatex + dvipdfmx
でもちゃんと pxchfon
パッケージを用いて源ノフォントが埋め込まれるはずです.
一通りのパッケージ・フォント類を使えるようにした結果,イメージサイズが大きくなってしまいました. 小さいイメージが好ましい方は他のイメージを利用するか,使わない collection 類を削ったりしてください.
以下を .gitlab-ci.yml
に記述することで,master ブランチ上の main.tex
を latexmk
を用いてコンパイルできます.
プロジェクトルートに latexmkrc
を置いておけば,ローカルでも CI 上でも同じようにコンパイルができます.
サンプルリポジトリで作成された PDF には源ノフォントが埋め込まれているはずです.
compile:
only:
- master
image:
name: registry.gitlab.com/mahito1594/sample-latex-project:latest
entrypoint: [""]
script:
- latexmk main
artifacts:
paths:
- main.pdf
expire_in: 1 day
また pipeline から作成した PDF をダウンロードできます.
次節で説明するように,作成した PDF はクラウドストレージサービスにアップロードするため,1 日で消去されるように設定します.
artifacts:expire_in
を設定しなかった場合,手動で削除されるまでダウンロードが可能です.
arXiv は TeX Live 2016 を利用しているようですので, arXiv への投稿を念頭にいれるならば TeX Live 2016 の Docker イメージを利用したほうがいいかもしれません.
前節で PDF を作成し,artifacts としてダウンロードができるようになりました. しかし以下の観点から作成した PDF を Dropbox などのクラウドストレージにも保存することは有効です:
例えば push 前にローカルで PDF を作成し,それを pre-push フックなどで同期フォルダへコピーする方法が挙げられます. 本記事では GitLab CI を利用してクラウドストレージにファイルをアップロードする方法について述べます.
皆さんご存知の Dropbox です.
curl
と API を利用して,生成した main.pdf
をアップロードすることができます.
詳細は Dropbox API のドキュメントを参照してください.
具体的には次のコマンドで実行できます:
curl -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer $DROPBOX_API_TOKEN " \
--header "Dropbox-API-Arg: {\"path\": \"/main.pdf\",\"mode\": \"overwrite\"}" \
--header "Content-Type: application/octet-stream" \
--data-binary @main.pdf
DROPBOX_API_TOKEN
は次のようにして取得できます:
Generated access token
の欄の Generate
ボタンを押す.取得したトークンをプロジェクトページの Settings > CI/CD > Variables
から DROPBOX_API_TOKEN
という名前で保存します.
繰り返しますが Masked にチェックを入れるのを忘れないようにしましょう.
.gitlab-ci.yml
には次のように記述します:
upload_dropbox:
stage: upload
image:
name: curlimages/curl:latest
entrypoint: [""]
script:
- |-
curl -X POST https://content.dropboxapi.com/2/files/upload \
--header "Authorization: Bearer $DROPBOX_API_TOKEN " \
--header "Dropbox-API-Arg: {\"path\": \"/main.pdf\",\"mode\": \"overwrite\"}" \
--header "Content-Type: application/octet-stream" \
--data-binary @main.pdf
また,生成した PDF だけでなく .tex
や .bbl
などのファイルもアップロードしたいときもあると思います.
このように複数ファイルをアップロードするには for
文を利用する方法があります.
(参考: https://stackoverflow.com/a/46634228)
Dropbox の無料プランでは連携できる端末に上限があるため,最近は pCloud を利用しています.
pCloud も API を利用してファイルをアップロードすることができます.
ただし,ログインに2要素認証を利用していると uploadfile
メソッドを用いたアップロードができないようです.
ここでは uploadtolink
メソッドを利用してみます.
本稿執筆時点 (2019 12 08) でドキュメントに不備があるようです.
こちらのコメントを参照してください.
具体的には次のコマンドで main.pdf
と main.tex
をアップロードすることができます:
curl -F file=@main.pdf \
-F file=@main.tex \
"https://api.pcloud.com/uploadtolink?names=GitLabCI&code=${PCLOUD_UPLOAD_CODE}"
アップロードされたファイルはこちらから確認できます.
CI 変数 PCLOUD_UPLOAD_CODE
には my.pcloud.com からアップロードしたいディレクトリ (フォルダ) を選択し,
Share Upload Link
をクリックして取得したものを保存します9.
三度目になりますが変数は Masked にしておきましょう.
.gitlab-ci.yml
には次のように記述します:
upload_pcloud:
stage: upload
image:
name: curlimages/curl:latest
entrypoint: [""]
script:
- |-
curl -F "file=@main.pdf" \
-F "file=@main.tex" \
"https://api.pcloud.com/uploadtolink?names=GitLabCI&code=${PCLOUD_UPLOAD_CODE}"
あまり TeX & LaTeX advent calendar らしからぬ記事になってしまいました. git-tag を打つと 1 つ前の tag との latexdiff をとってコンパイル → pCloud へアップロード,ということもできると思ったのですが力尽きたので試していません. 後で追記するかもしれません.
最近は Debian を使っています ↩︎
プライベートリポジトリで他の CI サービスを利用したことがないので比較はできません ↩︎
https://about.gitlab.com/blog/2017/04/11/introducing-subscriptions-on-gitlab-dot-com/ ↩︎
特定のバッファだけで有効になるように工夫が必要です ↩︎
その代わり latexdiff や latexdiff-vc などのツールを用いて,TeX ファイルの差分から PDF の差分を見やすくすることができます ↩︎
ライセンスを読んだ限り問題はないと思うのですが,ライセンス難しいのでよくわかりません ↩︎
git を知らない人に「GitLab.com のアカウントを作ってこれこれして」と頼むのは難しいものがあります ↩︎
既存のアプリを利用しても構わないと思います ↩︎
表示される URL 末尾の code=xxxxxxxxxxxx
となっている部分です ↩︎