go-taskを使ったSphinxドキュメント運用
今年参画していた案件で見て以降、go-taskを使うモチベーションが上がってきています。
「モチベーションがあるうちに」ということで、最近ではライブラリのドキュメント(Sphinx製)のタスクランナーをmakeからgo-taskに置き換える試みをしてみました。
この記事では、go-taskを使ったMakefileからTaskfileへの置き換えを紹介します。
なお、置き換え時には次の点について配慮していました。
- もともとできたことが全部できる。(自分が扱う範囲で)
- go-taskの文法を使い、簡易に書けるところは簡易に書く。
「〇〇ってそもそも何?」な人向け
- go-task
- Sphinx
実物と使用法
go-taskのタスク定義として使うTaskfile.yamlはこんな感じになっています。
version: '3'
vars:
# If you run bare environment or activated venv, set '' (blank string)
# RUN_PYTHON: ''
SPHINX_DEFAULT_BUILD: 'mini18n-dirhtml'
SPHINX_OPTIONS: ''
SPHINX_LANGUAGES:
- 'ja'
env:
SPHINXINTL_TRANSLATOR: "Kazuya Takei <myself@attakei.net>"
tasks:
intl:
desc: 'Sync i18n environment'
dir: '{{.TASKFILE_DIR}}'
cmds:
- '{{.RUN_PYTHON}} sphinx-build -M gettext . _build {{.SPHINX_OPTIONS}}'
- '{{.RUN_PYTHON}} sphinx-intl update --language={{.SPHINX_LANGUAGES | join ","}}'
dev:
desc: 'Run docs server'
dir: '{{.TASKFILE_DIR}}'
cmds:
- '{{.RUN_PYTHON}} sphinx-autobuild -b dirhtml . _build/dirhtml'
build-*:
desc: 'Make docs'
dir: '{{.TASKFILE_DIR}}'
vars:
TARGET: '{{index .MATCH 0}}'
cmds:
- '{{.RUN_PYTHON}} sphinx-build -M {{.TARGET}} . _build'
build:
desc: 'Make docs (default target)'
deps:
- 'build-{{.SPHINX_DEFAULT_BUILD}}'
help:
desc: 'Display help of docs'
deps:
- 'build-help'
clean:
desc: 'Clean build files of docs'
deps:
- 'build-clean'
-
task build-BUILDERでmake BUILDERと同じ。 -
task cleanはmake cleanと同じ。 -
task helpはmake helpと同じ。 -
task buildはSPHINX_DEFAULT_BUILDで指定したビルダーを実行するショートカット。 -
task intlはSPHINX_LANGUAGESで指定したi18nリソースを生成or更新(sphinx-intlインストール時) -
task dev(sphinx-autobuildインストール時)
Makefileが最初からあるのにgo-taskに置き換えた理由
Sphinxドキュメントの運用時において「Makefileから置き換えるメリット」は、総合的に見るとそこまで大きくありません。
というわけで、個人的な嗜好が強いのですが、go-taskを使っている理由を軽く説明してみます。
ファイルが減る
sphinx-quickstartで生成されるファイルは、Makefileの他にmake.batがあります。
後者のファイルは名前の通りWindows用なのですが、正直なところ自分の環境下で使うことがほぼありません。
とはいえ、パブリックなプロジェクトでは「Linux/macOSのmakeで使えるならmake.batでも使えるべき」となるでしょう。
つまり、「使う機会が少ないのに編集はしたほうが良い」という状況になります。
そこで、いっそのことタスクランナーを完全に統一することでファイルの削減をする方針を取りました。 [1]
インデント形式を統一できる
自分が普段扱っているコードは基本的にスペースでインデントをしています。
一方で、Makefileはタブインデントなファイルフォーマットとなっています。
エディターやIDEでなんとかなる話ではあるのですが、ターミナル等からコピペする時に事故ることがあります。 [2]
最終的に「コンテキストスイッチを減らす」目的とセットで、スペースインデントで書けるYAMLを使うことにしました。
複雑なことがしやすい
例えば、go-taskの文法では desc という要素を定義することで、 task -l でタスクの一覧を分かりやすくできます。
更に、varsでの変数定義周りの書式などを含めて、「ちょっと複雑なこと」をしやすくなっています。
やってること
今回使い始めたTaskfile.yamlを組み立てるにあたって、試したりした工夫点をまとめます。
実行環境の変数化
vars要素内に、Taskfile内で使用可能な変数としてRUN_PYTHONを定義しています。
(ただし、テンプレート内の初期構造では、コメントアウトしています)
vars:
# If you run bare environment or activated venv, set '' (blank string)
# RUN_PYTHON: ''
tasks:
intl:
desc: 'Sync i18n environment'
dir: '{{.TASKFILE_DIR}}'
cmds:
- '{{.RUN_PYTHON}} sphinx-build -M gettext . _build {{.SPHINX_OPTIONS}}'
Taskfile内では、定義の各所にて{{.<変数名>}}を記述しておくと、あらかじめvars内で宣言した変数を埋め込むことができます。
上記の例ではコメントアウトしていますが、RUN_PYTHON: 'uv runとしておくと、uvを使用している環境下であることを前手にできます。
コメントアウトして変数自体の定義がない場合は、ただの空文字列として扱われます。
上記の例ではuv等を経由せずsphinx-buildを実行しようとすることになり、「venv環境下」や「Sphinxをグローバル環境下にインストールしている」ことを前提にできます。
タスクのワイルドカード化
下記の抜粋箇所では、タスク定義に*が使われています。
これはよくあるワイルドカードの記述として機能しており、build-から始まる全ての文字をタスクとして捕捉します。
tasks:
build-*:
desc: 'Make docs'
dir: '{{.TASKFILE_DIR}}'
vars:
TARGET: '{{index .MATCH 0}}'
cmds:
- '{{.RUN_PYTHON}} sphinx-build -M {{.TARGET}} . _build'
このタスク自体はsphinx-buildコマンドで「何かしらのビルダーを指定してビルドを実行する」ことを目的としています。
cmds内の記述にもあるように、-Mというビルダー指定のオプションとして{{.TARGET}}を指定しています。
このTARGETはタスクをスコープとしたvars内で定義されており、{{index .MATCH 0}}を指定しています。
タスク定義時にワイルドカードを使用すると、MATCHという変数に文字列のリストが設定されます。 [3] [4]
さらに、リスト型の変数に対して指定したインデックスの箇所を指定する{{index}}を使用することで、
{{index .MATCH 0}}は「最初に指定したワイルドカードの箇所」をそのまま文字列として使用できます。
最終的にこの記述をすることによって、make XXXというビルダーを指定するmake処理をtask builder-XXXという形式に置換しています。
depsを使ったショートカット
Taskfileの最後にこのようなタスクを定義しています。
tasks:
help:
desc: 'Display help of docs'
deps:
- 'build-help'
clean:
desc: 'Clean build files of docs'
deps:
- 'build-clean'
depsはタスクの前提条件として、cmdsの実行前に他のタスクを実行したい時に記述します。
sphinx-buildで呼べるビルダーは通常のビルダーの他にも特殊な用途を持つものが存在します。
-
helpビルダー: 使用可能なビルダーの一覧を出力する。 -
cleanビルダー: 出力先となるフォルダを削除してクリーンアップする。
今回のTaskfileを定義するにあたって、これらの特殊用途のものに対しては「何をしたいか」を分かりやすくするべきでしょう。
よって、task help,task cleanという形式で実行できる方が望ましいものとなります。
実は、タスク自体はcmdsを宣言する必要はありません。
depsにbuild-help,build-cleanだけを宣言しておくことで、実質的にtask helpはtask builder-helpのエイリアスとして機能するようになります。 [5]
ちなみにbuildも同様となっていて、SPHINX_DEFAULT_BUILD変数で指定したビルダーを使うショートカットになっています。
留意事項的なこと
個人利用がメインのライブラリに関しては、ドキュメント用のタスクはTaskfileへの置き換えが進んでいます。
少なくとも開発に使っている端末にはgo-taskがインストールされているため、困るシーンはありません。
とはいえ、OSS上でこのアプローチを採用するということは、コントリビューターにもgo-taskの使用を少なからず強制することになります。
README上で明記したり、そもそもgo-taskが無くても平気は範囲での使用に留めるなど、気をつけていくほうが良いでしょう。
Discussion