はじめに #
タスクランナーTaskfileをPython環境にインストールし、Ruff, mypy, pytestを実行する方法をまとめました。 タスクランナーとは、ソフトウェアの開発における作業を自動化するツールです。 また、Taskfileの正式名称はTaskですが、一般的な名称のため、この記事ではTaskfileと記載します。
検証環境は以下の通りです。
-
Windows 10 Home 22H2
-
Python 3.13.5
-
Taskfile 3.44.1
-
Ruff 0.12.8
-
mypy 1.17.1
-
pytest 8.4.1
Python向けタスクランナー比較 #
Taskfile以外にもPythonで使用できるタスクランナーがあるため、各ツールを比較しました(2025/8/12現在)。
ツール | 最終リリース日 | GitHubスター数 |
---|---|---|
Taskfile | 2025/7/24 | 13000 |
invoke | 2023/7/13 | 4597 |
poethepoet | 2025/8/12 | 1800 |
taskipy | 2024/11/27 | 620 |
最終リリース日はPyPiの「リリース日」としています。
TaskfileのGitHubスター数が最も多く、開発が活発なため採用しました。
Taskfileの概要 #
TaskfileはGo言語で実装されたタスクランナーです。 公式サイトのInstallationのページにあるように、Linuxのsnap, Node.jsのnpm, WindowsのWinGetなど様々な環境にインストール可能です。 また、Taskfileはシングルバイナリで実行されます。
Taskfileの基本的な使い方 #
インストール #
以下のコマンドを実行し、TaskfileをPython環境にインストールします(必要に応じて、uvやvenvで仮想環境を作成して下さい)。
pip install go-task-bin
インストールの完了後、以下のコマンドでTaskfileのバージョンが表示されることを確認します。
> task --version
3.44.1+pypi
また、task --help
コマンドでヘルプを表示できます。
設定ファイル #
以下のコマンドを実行し、Taskfileの設定ファイルTaskfile.yml
を作成します。
task --init
Taskfile.yml
の中身は以下のようになっています。
# https://taskfile.dev
version: '3'
vars:
GREETING: Hello, World!
tasks:
default:
cmds:
- echo "{{.GREETING}}"
silent: true
主な設定を解説します。
version
: Taskfileのバージョンを指定vars
: 変数を設定tasks
: 実行するタスクを定義default
: タスク名cmds
: 実行するコマンドを定義silent
:true
のとき、Taskfileのメタ情報を表示しない
タスクの実行 #
Taskfile.yml
と同じ階層のディレクトリでtask
コマンドを実行すると、タスクが実行されます。
> task
Hello, World!
上述のTaskfile.yml
では、echo "Hello, World!"
というコマンドが実行されます。
したがって、コンソールにはHello, World!
と表示されます。
タスクの終了ステータスと続行 #
この節では、タスクの終了ステータスに対するTaskfileの挙動について記載します。
タスクのコマンドをcmds
に続けて記述するとき、各コマンドの終了ステータスが0のときのみ実行が継続されます。
Taskfile.yml
のtasks
を以下のように変更します。
tasks:
default:
cmds:
- exit 0
- echo "{{.GREETING}}"
silent: false
Taskfileの実行結果は以下になり、echo
まで実行されていることが分かります。
> task
task: [default] exit 0
task: [default] echo "Hello, World!"
Hello, World!
次に、exit
の終了ステータスを1
に変更します。
tasks:
default:
cmds:
- exit 1
- echo "{{.GREETING}}"
silent: false
Taskfileの実行結果は以下になりました。exit 1
でfailし、echo
は実行されません。
> task
task: [default] exit 1
task: Failed to run task "default": exit status 1
この挙動を利用して、複数の開発ツールの実行を制御することができます。 例えば「Ruffによる構文チェックに失敗する場合、Pytestによる自動テストを実行しない」などです。
開発ツールの終了ステータス #
Ruff, mypy, pytestは、チェック対象のコードが正常な場合、いずれも終了ステータスは0になります。 詳細は以下になります。
Ruff #
Ruffのlinter機能では、違反がない場合、もしくは違反が全て自動的に修正された場合のみ、終了ステータスが0になります。 また、Ruffのformatter機能では、以下の場合に終了ステータスが0になります。
-
ruff format
で全ファイルを正常に整形できた場合 -
ruff format --check
で全ファイルが整形の必要がない場合
mypy #
mypyの終了ステータスは公式にドキュメント化されていないようですが、issueによるとおおむね以下になるようです。
- 0: 型エラーがない場合
- 1: 何らかの型エラーがある場合
- 2: クラッシュした場合や、引数が誤っている場合など
Document exit codes for mypy and dmypy - Issue #6003 - GitHub
pytest #
pytestの終了ステータスは、全てのテストに成功した場合のみ0になります。 それ以外の場合、終了ステータスは1~5になります。
Exit codes - pytest documentation
Taskfileの編集 #
いよいよRuff, mypy, pytestの一連の処理をTaskfile.yml
に設定します。
処理の順序は以下とします。
- Ruffによるlint
- Ruffによる整形チェック
- mypyによる型チェック
- pytestによる自動テスト
順序は好みがあると思いますが、処理時間が短いものを先にした方がよいと思います(短時間でエラーが出た方が修正のサイクルを早く回せるため)。
また、プロジェクトのフォルダ構成は以下とします。
src/ # ソフトウェア本体のスクリプト
tests/ # テスト用スクリプト
Taskfile.yml
pyproject.toml
Taskfile.yml
にコマンドを設定します。
実行対象のフォルダを変数SRC_DIR
, TEST_DIR
で定義しています。
また、Ruffにはコードの自動修正機能がありますが、その機能を使用せず、単にエラーを出力するのみに留めています。
version: '3'
vars:
SRC_DIR: "."
TEST_DIR: "./tests"
tasks:
default:
cmds:
- ruff check {{.SRC_DIR}}
- ruff format --check {{.SRC_DIR}}
- mypy {{.SRC_DIR}}
- pytest {{.TEST_DIR}}
silent: false
pyproject.toml
の中身は以下のようにして、pytest実行時のパスにsrc
フォルダを追加します。
[tool.pytest.ini_options]
pythonpath = "src"
ここまででファイルの編集は完了です。
最後にTaskfileで4つのコマンドをまとめて実行します。
プロジェクトの一番上の階層でtask
コマンドを実行するだけです。
全てのチェックに成功する(終了ステータスが0になる)場合、以下のように表示されます。
> task
task: [default] ruff check .
All checks passed!
task: [default] ruff format --check .
2 files already formatted
task: [default] mypy .
Success: no issues found in 2 source files
task: [default] pytest ./tests
=================================== test session starts ===================================
platform win32 -- Python 3.13.5, pytest-8.4.1, pluggy-1.6.0
rootdir: E:\document\Python Scripts\task_test
configfile: pyproject.toml
collected 1 item
tests\test_foo.py . [100%]
==================================== 1 passed in 0.02s ====================================
一方、最初のruff check
コマンドで失敗する場合、以下のようになります。
ruff format --check
以降のコマンドは実行されません。
> task
task: [default] ruff check .
src\foo.py:6:5: F841 Local variable `y` is assigned to but never used
|
4 | def myfunc(x: int) -> int:
5 | """func."""
6 | y = 300
| ^ F841
7 | return 2 * x
|
= help: Remove assignment to unused variable `y`
Found 1 error.
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
task: Failed to run task "default": exit status 1