MakefileからmiseのTasks機能に移行できるか検討してみた

MakefileからmiseのTasks機能に移行できるか検討してみた

2026.03.13

どうも!オペ部の西村祐二です!

開発プロジェクトでタスクランナーとしてMakefileを使っている方は多いと思います。make lintmake testmake buildのように、よく使うコマンドをまとめておけるのは便利ですよね。

ただ、ランタイムのバージョン管理にmiseを使っている場合、タスクランナーもmiseに統一できると管理がシンプルになります。miseにはTasks機能が用意されており、Makefileの代替として十分に使えます。

この記事では、MakefileからmiseのTasks機能に移行するサンプル手順、miseのTask機能の使い方を紹介します。

結論

Makefileで定義していた開発タスクは、mise.toml[tasks]セクションにTOML形式で移行できそう。miseでランタイム管理とタスク管理を統一することで、プロジェクトの設定ファイルがシンプルになり、.PHONYやMakefile特有の記法から解放される。

この記事で分かること:

  • MakefileのタスクをmiseのTOML形式に書き換える方法
  • Makefileでは面倒だった引数サポート、ファイル監視、実行前確認などをmiseで実現する方法
  • タスク専用ツールバージョン指定、高度な依存関係制御など、miseならではの機能の使い方
  • mise taskのつまずきポイントと対処法

前提条件

  • miseがインストール済み(mise --versionで確認)
  • 移行元のMakefileがある

サンプル手順

1. 移行元のMakefileを確認する

まず、現在のMakefileで定義しているタスクを整理します。今回は以下のような、実際のプロジェクトでありがちなMakefileを例にします。

Makefile
.PHONY: dev build lint lint-fix test test-watch test-ci clean setup db-up db-down db-reset migrate seed deploy deploy-prod help

# 変数定義
APP_NAME := myapp
NODE_ENV ?= development
AWS_PROFILE ?= default
S3_BUCKET_STAGING := $(APP_NAME)-staging
S3_BUCKET_PROD := $(APP_NAME)-prod

# 環境チェック
check-env:
	@test -f .env || (echo "Error: .env file not found. Run 'make setup' first." && exit 1)

# =====================
# 開発
# =====================
dev: check-env db-up
	NODE_ENV=development npm run dev

build:
	NODE_ENV=production npm run build

# =====================
# コード品質
# =====================
lint:
	npm run lint
	npm run type-check

lint-fix:
	npm run lint -- --fix
	npm run format

# =====================
# テスト
# =====================
test: check-env
	NODE_ENV=test npm run test

test-watch:
	NODE_ENV=test npm run test -- --watch

test-ci: lint test
	npm run test -- --coverage --ci

# =====================
# Docker / DB
# =====================
db-up:
	docker compose up -d postgres redis

db-down:
	docker compose down

db-reset: db-down
	docker compose up -d postgres
	sleep 3
	npm run db:migrate:reset
	npm run db:seed

migrate:
	npm run db:migrate

seed:
	npm run db:seed

# =====================
# セットアップ
# =====================
setup:
	cp .env.example .env
	npm install
	docker compose up -d postgres redis
	sleep 3
	npm run db:migrate
	npm run db:seed
	@echo "Setup complete!"

# =====================
# デプロイ
# =====================
deploy: test-ci build
	AWS_PROFILE=$(AWS_PROFILE) aws s3 sync dist/ s3://$(S3_BUCKET_STAGING)/
	@echo "Deployed to staging!"

deploy-prod: test-ci build
	@read -p "Are you sure you want to deploy to PRODUCTION? [y/N] " confirm && \
		[ "$$confirm" = "y" ] || (echo "Cancelled." && exit 1)
	AWS_PROFILE=$(AWS_PROFILE) aws s3 sync dist/ s3://$(S3_BUCKET_PROD)/
	@echo "Deployed to production!"

# =====================
# クリーンアップ
# =====================
clean:
	rm -rf dist node_modules .next .cache coverage

# =====================
# ヘルプ
# =====================
help:
	@echo "Available targets:"
	@echo "  dev          - Start development server"
	@echo "  build        - Production build"
	@echo "  lint         - Run linter and type check"
	@echo "  lint-fix     - Fix lint errors and format"
	@echo "  test         - Run tests"
	@echo "  test-watch   - Run tests in watch mode"
	@echo "  test-ci      - Run lint + test with coverage"
	@echo "  db-up        - Start database containers"
	@echo "  db-down      - Stop database containers"
	@echo "  db-reset     - Reset database with seed data"
	@echo "  migrate      - Run database migrations"
	@echo "  seed         - Seed database"
	@echo "  setup        - Initial project setup"
	@echo "  deploy       - Deploy to staging"
	@echo "  deploy-prod  - Deploy to production"
	@echo "  clean        - Remove build artifacts"

よく見るMakefileですね。変数定義、環境チェック、Docker操作、デプロイ前の確認プロンプト、helpターゲットの手動管理など、プロジェクトが成長するにつれて複雑になっていく典型的なパターンです。

これらをmise Tasksに移行していきます。

ポイント:

  • .PHONYで定義しているターゲットが移行対象
  • タスク間の依存関係(deploy: test-ci buildのような記述)をメモしておく
  • 変数(NODE_ENVAWS_PROFILE等)の扱いを確認しておく
  • 確認プロンプトやヘルプなど、自前で実装しているロジックがmiseの標準機能で置き換え可能か確認する

2. mise.tomlにタスクを定義する

既存のmise.tomlの末尾に[tasks]セクションを追加し、Makefileのタスクを書き換えます。[tools]セクションなど既存の設定はそのまま残してください。

まずはシンプルなタスクから移行しましょう。1行で定義できるものは1行で書けます。

mise.toml
[tasks]
build = "NODE_ENV=production npm run build"
lint = "npm run lint && npm run type-check"
"lint:fix" = "npm run lint -- --fix && npm run format"
migrate = "npm run db:migrate"
seed = "npm run db:seed"

これだけでmise run buildmise run lintでタスクを実行できるようになります。

ポイント:

  • make buildmise run build に変わる(mise buildでも実行可能だが、miseのサブコマンドと名前が衝突する可能性があるためmise runを使うのが安全)
  • シンプルなコマンドは タスク名 = "コマンド" の1行で定義可能
  • タスク名にコロンを含める場合は "lint:fix" のようにクォートで囲む

3. 詳細なタスク設定を追加する

手順2で定義した簡略形式を、テーブル形式に書き換えます。Makefileの全タスクをカバーし、説明文や依存関係も追加しましょう。先ほどの[tasks]セクションを以下の内容で置き換えてください。

mise.toml
# =====================
# 開発
# =====================
[tasks.dev]
description = "開発サーバーを起動する(DB自動起動付き)"
depends = ["db:up"]
env = { NODE_ENV = "development" }
run = "npm run dev"

[tasks.build]
description = "プロダクションビルドを実行する"
env = { NODE_ENV = "production" }
run = "npm run build"

# =====================
# コード品質
# =====================
[tasks.lint]
description = "リンターと型チェックを実行する"
run = [
  "npm run lint",
  "npm run type-check",
]

[tasks."lint:fix"]
description = "Lint修正とフォーマットを実行する"
run = [
  "npm run lint -- --fix",
  "npm run format",
]

# =====================
# テスト
# =====================
[tasks.test]
description = "テストを実行する"
env = { NODE_ENV = "test" }
run = "npm run test"

[tasks."test:ci"]
description = "CI用のチェックを一括実行する(lint + test + coverage)"
depends = ["lint"]  # Makefileではtestにも依存していたが、runで--coverage付きテストを実行するため不要
env = { NODE_ENV = "test" }
run = "npm run test -- --coverage --ci"

# =====================
# Docker / DB
# =====================
[tasks."db:up"]
description = "データベースコンテナを起動する"
run = "docker compose up -d postgres redis"

[tasks."db:down"]
description = "データベースコンテナを停止する"
run = "docker compose down"

[tasks."db:reset"]
description = "データベースをリセットする(データ全削除)"
depends = ["db:down"]
confirm = "データベースを完全にリセットします。よろしいですか?"
run = [
  "docker compose up -d postgres",
  "sleep 3",
  "npm run db:migrate:reset",
  "npm run db:seed",
]

[tasks.migrate]
description = "DBマイグレーションを実行する"
run = "npm run db:migrate"

[tasks.seed]
description = "DBにシードデータを投入する"
run = "npm run db:seed"

# =====================
# セットアップ
# =====================
[tasks.setup]
description = "開発環境を初期セットアップする"
run = [
  "cp .env.example .env",
  "npm install",
  "docker compose up -d postgres redis",
  "sleep 3",
  "npm run db:migrate",
  "npm run db:seed",
]

# =====================
# デプロイ
# =====================
[tasks.deploy]
description = "stagingにデプロイする"
depends = ["test:ci", "build"]
run = "aws s3 sync dist/ s3://myapp-staging/"

[tasks."deploy:prod"]
description = "productionにデプロイする"
depends = ["test:ci", "build"]
confirm = "本番環境にデプロイします。本当によろしいですか?"
run = "aws s3 sync dist/ s3://myapp-prod/"

# =====================
# クリーンアップ
# =====================
[tasks.clean]
description = "ビルド成果物を削除する"
confirm = "dist/, node_modules/, .cache/, coverage/ を削除します。よろしいですか?"
run = "rm -rf dist node_modules .next .cache coverage"

Makefileと比較して、以下の点がすっきりしました。

Makefileの自前実装 → miseの標準機能に置き換わったもの:

  • helpターゲットの手動管理 → descriptionで自動生成(mise tasksmise runで表示)
  • deploy-prodread -pによる確認 → confirmフィールドで1行
  • check-envターゲットによる.envチェック → dependsで前提タスクを明示化
  • $(VAR)$$varの混在 → envフィールドで宣言的に管理
  • .PHONY宣言 → 不要

ポイント:

  • dependsで依存タスクを指定できる(Makefileのdeploy: test-ci buildに相当)
  • descriptionを設定するとmise tasksの一覧表示で説明が表示される
  • dependsに指定したタスクはデフォルトで並列実行される(順序を制御したい場合はrun配列や--jobs 1を使う)
  • confirmでデプロイ前の確認プロンプトが標準機能として使える

4. タスク一覧を確認する

定義したタスクが正しく認識されているか確認します。

mise tasks

以下のように一覧が表示されます。

Name         Description                                    Source
build        プロダクションビルドを実行する                     ~/project/mise.toml
clean        ビルド成果物を削除する                            ~/project/mise.toml
db:down      データベースコンテナを停止する                     ~/project/mise.toml
db:reset     データベースをリセットする(データ全削除)           ~/project/mise.toml
db:up        データベースコンテナを起動する                     ~/project/mise.toml
deploy       stagingにデプロイする                            ~/project/mise.toml
deploy:prod  productionにデプロイする                         ~/project/mise.toml
dev          開発サーバーを起動する(DB自動起動付き)            ~/project/mise.toml
lint         リンターと型チェックを実行する                     ~/project/mise.toml
lint:fix     Lint修正とフォーマットを実行する                   ~/project/mise.toml
migrate      DBマイグレーションを実行する                      ~/project/mise.toml
seed         DBにシードデータを投入する                        ~/project/mise.toml
setup        開発環境を初期セットアップする                     ~/project/mise.toml
test         テストを実行する                                 ~/project/mise.toml
test:ci      CI用のチェックを一括実行する(lint + test + coverage) ~/project/mise.toml

Makefileではhelpターゲットに手動で説明を書いていましたが、miseはdescriptionから自動生成してくれます。タスクを追加するたびにhelpを更新し忘れる、ということがなくなります。

ポイント:

  • descriptionが一覧に表示されるため、チームメンバーがタスクの内容を把握しやすい
  • Makefileのhelpターゲットを自前で管理する必要がなくなった

さらに、引数なしでmise runを実行すると、インタラクティブにタスクを選択できるUIが表示されます。

mise run
Tasks
Select a task to run
❯ build        プロダクションビルドを実行する
  clean        ビルド成果物を削除する
  db:down      データベースコンテナを停止する
  db:reset     データベースをリセットする(データ全削除)
  db:up        データベースコンテナを起動する
  deploy       stagingにデプロイする
  deploy:prod  productionにデプロイする
  dev          開発サーバーを起動する(DB自動起動付き)
  lint         リンターと型チェックを実行する
  ...

Makefileだと「どんなタスクがあったっけ?」と思ったときにMakefileを直接開いて確認する必要がありましたが、miseならmise runだけで選択肢が表示されます。descriptionをきちんと書いておけば、環境構築のドキュメントとしても機能するため、チーム開発でのオンボーディングにも使えます。

5. mise Tasksならではの便利な機能を活用する

Makefileでは実現しづらい(あるいは面倒な)機能が、mise Tasksでは標準で用意されています。移行のついでに活用してみましょう。

5-1. エイリアスの設定

よく使うタスクに短い別名を付けられます。Makefileでは同じターゲットを2つ書くか、変数で管理する必要がありましたが、mise Tasksならaliasを1行追加するだけです。

mise.toml
[tasks.test]
description = "テストを実行する"
alias = "t"
run = "npm run test"

[tasks.lint]
description = "リンターを実行する"
alias = "l"
run = "npm run lint"
# どちらでも実行可能
mise run test
mise run t

mise run lint
mise run l

5-2. 複数コマンドの順次実行

runに配列を渡すと、上から順に順次実行されます。途中で失敗した場合はそこで停止します。dependsが並列実行なのに対し、run配列は必ず直列です。Makefileの&&チェーンと同じ挙動ですが、TOML配列のほうが見やすいです。

mise.toml
[tasks.ci]
description = "CI用のチェックを一括実行する"
run = [
  "npm run lint",
  "npm run test",
  "npm run build",
]
mise run ci
# → lint → test → build の順に実行される
# lintが失敗したらtest以降は実行されない

5-3. 環境変数の設定

タスクごとに環境変数を指定できます。Makefileではexportやコマンド前の変数指定が必要でしたが、envフィールドで宣言的に書けます。

mise.toml
[tasks.test]
description = "テストを実行する"
env = { NODE_ENV = "test", CI = "true" }
run = "npm run test"

[tasks."test:coverage"]
description = "カバレッジ付きでテストを実行する"
env = { NODE_ENV = "test", COVERAGE = "true" }
run = "npm run test -- --coverage"

5-4. 変更検知によるスキップ(sources / outputs)

sourcesoutputsを設定すると、ソースファイルに変更がない場合にタスクの実行を自動スキップできます。これはMakefileのタイムスタンプベースのビルドと似た仕組みですが、グロブパターンが使えるのでより柔軟です。

mise.toml
[tasks.build]
description = "プロダクションビルドを実行する"
sources = ["src/**/*", "package.json", "tsconfig.json"]
outputs = ["dist/**/*"]
run = "npm run build"
# 初回: ビルドが実行される
mise run build

# 2回目: ソースに変更がなければスキップされる
mise run build
# → スキップ(高速に完了)

# src/内のファイルを編集した後
mise run build
# → 再度ビルドが実行される

5-5. 引数サポート(usage)

Makefileとの大きな違いの一つが、この引数サポートです。

Makefileでタスクに引数を渡そうとすると、こんなハックが必要でした。

Makefile
# Makefileで引数を渡す場合のハック
test:
	npm run test -- $(filter-out $@,$(MAKECMDGOALS))

# もしくは環境変数で渡す
deploy:
	./deploy.sh $(ENV)
# → make deploy ENV=production

これに対してmise Tasksでは、usageフィールド位置引数・フラグ・オプションを宣言的に定義できます。定義した引数は usage_ プレフィックス付きの環境変数として自動展開され、シェル補完やヘルプテキストの自動生成にも対応します。

基本: 位置引数(arg)

argで位置引数を定義します。<name>は必須、[name]は任意です。

mise.toml
[tasks.test]
description = "テストを実行する"
usage = '''
arg "[file]" help="テストファイルを指定(省略時は全件)" default=""
'''
run = '''
#!/usr/bin/env bash
npm run test -- ${usage_file}
'''
# 全テスト実行(default="" が使われる)
mise run test

# 特定ファイルのみ
mise run test src/utils.test.ts

引数の環境変数への展開ルール:

  • arg "<environment>"$usage_environment
  • flag "--dry-run"$usage_dry_run(ハイフンはアンダースコアに変換)
フラグ(flag)

フラグにはブール型(値を取らない)と値付きの2種類があります。

mise.toml
[tasks.test]
description = "テストを実行する"
usage = '''
arg "[file]" help="テストファイルを指定" default=""
flag "-w --watch" help="ウォッチモードで実行"
flag "--coverage" help="カバレッジレポートを出力"
'''
run = '''
#!/usr/bin/env bash
npm run test -- ${usage_file} ${usage_watch:+--watch} ${usage_coverage:+--coverage}
'''
# 全テスト実行
mise run test

# 特定ファイルをウォッチモードで
mise run test src/utils.test.ts --watch

# カバレッジ付き
mise run test --coverage

# 組み合わせも可能
mise run test src/utils.test.ts --watch --coverage

値付きフラグ<value> を付けて定義します。

mise.toml
[tasks.build]
description = "ビルドを実行する"
usage = '''
flag "--target <target>" help="ビルドターゲット" default="es2020"
flag "--outdir <dir>" help="出力ディレクトリ" default="dist"
'''
run = '''
#!/usr/bin/env bash
npx tsc --target ${usage_target?} --outDir ${usage_outdir?}
'''
# デフォルト設定でビルド
mise run build

# ターゲットと出力先を変更
mise run build --target es2022 --outdir build
選択肢の制限(choices)

引数やフラグの値を特定の選択肢に制限できます。不正な値が渡された場合はエラーになります。

mise.toml
[tasks.deploy]
description = "指定環境にデプロイする"
depends = ["build"]
usage = '''
arg "<environment>" help="デプロイ先の環境" {
  choices "dev" "staging" "prod"
}
flag "--region <region>" help="AWSリージョン" default="ap-northeast-1" {
  choices "ap-northeast-1" "us-east-1" "eu-west-1"
}
'''
run = '''
#!/usr/bin/env bash
echo "Deploying to ${usage_environment?} in ${usage_region?}..."
aws s3 sync dist/ s3://my-bucket-${usage_environment?}/ --region ${usage_region?}
'''
# staging にデプロイ(リージョンはデフォルト: ap-northeast-1)
mise run deploy staging

# production を us-east-1 にデプロイ
mise run deploy prod --region us-east-1

# 不正な値はエラー
mise run deploy local
# → Invalid choice for arg environment: local, expected one of dev, staging, prod
複数引数(var)

var=#trueを付けると、複数の値を受け取れます。

mise.toml
[tasks.format]
description = "指定ファイルをフォーマットする"
usage = '''
arg "<files>" var=#true help="フォーマットするファイル(複数可)"
'''
run = '''
#!/usr/bin/env bash
eval "files=($usage_files)"
for f in "${files[@]}"; do
  echo "Formatting: $f"
  npx prettier --write "$f"
done
'''
# 複数ファイルを一度に指定
mise run format src/index.ts src/utils.ts src/types.ts
環境変数との連携(env)

引数に env を指定すると、環境変数からも値を受け取れます。優先順位は CLI引数 > 環境変数 > デフォルト値 です。

mise.toml
[tasks.deploy]
description = "デプロイする"
usage = '''
arg "<environment>" help="デプロイ先" default="staging"
flag "--region <region>" help="AWSリージョン" env="AWS_REGION" default="ap-northeast-1"
'''
run = '''
#!/usr/bin/env bash
echo "Deploying to ${usage_environment?} in ${usage_region?}"
'''
# CLI引数が最優先
mise run deploy prod --region us-east-1

# 環境変数でも指定可能
AWS_REGION=eu-west-1 mise run deploy staging

# どちらも未指定ならデフォルト値
mise run deploy
# → Deploying to staging in ap-northeast-1
bashでの変数展開パターン

usageで定義した引数をシェルスクリプト内で使うときの展開パターンをまとめます。

構文 用途
${usage_name?} 値を参照(未定義ならエラー) ${usage_environment?}
${usage_name:-default} 未設定時にデフォルト値を使用 ${usage_port:-8080}
${usage_name:+value} 値がある場合のみ展開(フラグ→オプション変換に便利) ${usage_verbose:+--verbose}
ヘルプの自動生成

usageを定義しておくと、mise run <task> --helpでヘルプが自動生成されます。help属性に書いた説明文がそのまま表示されるので、descriptionと合わせて書いておけばチームメンバーがすぐに使い方を把握できます。

mise run deploy --help
# → Usage: deploy <environment> [--region <region>]
# →
# → Arguments:
# →   <environment>   デプロイ先の環境 [choices: dev, staging, prod]
# →
# → Flags:
# →   --region <region>  AWSリージョン [default: ap-northeast-1]

5-6. 実行前の確認プロンプト(confirm)

本番環境への操作など、誤って実行すると危険なタスクにはconfirmを設定できます。手順3のdeploy:proddb:resetで既に使いましたが、もう少し詳しく見てみましょう。

元のMakefileでは、確認プロンプトをこう書いていました。

Makefile
deploy-prod: test-ci build
	@read -p "Are you sure you want to deploy to PRODUCTION? [y/N] " confirm && \
		[ "$$confirm" = "y" ] || (echo "Cancelled." && exit 1)
	AWS_PROFILE=$(AWS_PROFILE) aws s3 sync dist/ s3://$(S3_BUCKET_PROD)/

mise Tasksならconfirmを1行追加するだけです。

mise.toml
[tasks."deploy:prod"]
description = "productionにデプロイする"
depends = ["test:ci", "build"]
confirm = "本番環境にデプロイします。本当によろしいですか?"
run = "aws s3 sync dist/ s3://myapp-prod/"
mise run deploy:prod
# → "本番環境にデプロイします。本当によろしいですか?" と表示される
# → y/n で選択

confirmには引数の値をTeraテンプレートで埋め込むこともできます。

mise.toml
[tasks.deploy]
description = "指定環境にデプロイする"
usage = 'arg "<environment>" help="デプロイ先" { choices "staging" "prod" }'
confirm = "{{ usage.environment }} にデプロイします。よろしいですか?"
depends = ["test:ci", "build"]
run = 'aws s3 sync dist/ s3://myapp-${usage_environment?}/'

5-7. ファイル監視による自動再実行(mise watch)

mise watchを使うと、ファイルの変更を監視してタスクを自動で再実行できます。内部的にはwatchexecを使用しています。

# buildタスクをファイル変更時に自動再実行
mise watch build

# 監視対象を特定のディレクトリに限定
mise watch build --watch src --watch public

# 特定の拡張子のみ監視
mise watch build --exts ts,tsx,js,jsx

# テストを自動再実行(変更検知 + 画面クリア)
mise watch test --clear

開発サーバーのように常駐プロセスを再起動したい場合は--restartオプションを使います。

# サーバーを起動し、ソース変更で自動再スタート
mise watch serve --watch src --exts ts --restart

5-8. タスク専用のツールバージョン指定(tools)

mise Tasksならではの強力な機能です。特定のタスク実行時にだけ使いたいツールとそのバージョンを指定できます。

mise.toml
[tasks.build]
description = "プロダクションビルドを実行する"
tools = { node = "20" }
run = "npm run build"

[tasks."legacy:build"]
description = "レガシー環境向けにビルドする"
tools = { node = "18" }
run = "npm run build"
# Node.js 20でビルド
mise run build

# Node.js 18でビルド(レガシー対応)
mise run legacy:build

Makefileでこれを実現するにはnvm usevolta runを各ターゲット内で呼ぶ必要がありましたが、miseならランタイム管理とシームレスに統合されます。

5-9. 高度な依存関係制御(depends / wait_for / depends_post)

mise Tasksでは、タスクの実行順序を3種類の方法で制御できます。

手順3のdeployタスクを拡張して、複数の前提タスクを設定する例です。

mise.toml
# depends: タスク実行"前"に実行。失敗したら本タスクは実行されない
[tasks.deploy]
depends = ["lint", "test", "build"]
run = "aws s3 sync dist/ s3://my-bucket"

# wait_for: 指定タスクが他の経路で実行中なら完了を待つが、実行キューには追加しない
# (dependsと異なり、wait_forだけではタスクが起動されない)
[tasks.codegen]
run = "npx graphql-codegen"

[tasks.lint]
wait_for = ["codegen"]
run = "eslint ."

# depends_post: タスク実行"後"に実行される後処理タスク
[tasks.build]
run = "npm run build"
depends_post = ["notify"]

[tasks.notify]
hide = true
run = 'echo "ビルド完了!"'
プロパティ 実行タイミング 失敗時の挙動 用途
depends タスク実行 失敗で本タスク中止 前提タスク(build前のlint等)
wait_for タスク実行 実行キューに追加しない 他で実行中のタスクの完了待ち
depends_post タスク実行 - 後処理(通知、クリーンアップ等)

5-10. 内部タスクの非表示(hide)

他のタスクからのみ呼ばれるヘルパータスクはhide = trueで一覧から非表示にできます。mise tasksの出力がすっきりします。

mise.toml
[tasks.setup]
description = "開発環境をセットアップする"
depends = ["setup:deps", "setup:env"]
run = 'echo "セットアップ完了!"'

[tasks."setup:deps"]
hide = true
run = "npm install"

[tasks."setup:env"]
hide = true
run = "cp .env.example .env"
mise tasks
# → setup のみ表示される(setup:deps, setup:env は非表示)

# 非表示タスクを含めて確認したい場合
mise tasks --hidden

5-11. Python / Node.js でタスクを書く

runフィールド内でシェバン(#!)を使うと、シェルスクリプト以外の言語でタスクを書けます。JSONのパースやAPI呼び出しなど、bashだと面倒な処理をPythonやNode.jsで書けます。

mise.toml
[tasks.check-api]
description = "APIのヘルスチェックを実行する"
run = '''
#!/usr/bin/env python3
import urllib.request
import json

url = "https://api.example.com/health"
try:
    with urllib.request.urlopen(url) as res:
        data = json.loads(res.read())
        print(f"Status: {data['status']}")
except Exception as e:
    print(f"Error: {e}")
    exit(1)
'''

[tasks.gen-config]
description = "設定ファイルを生成する"
run = '''
#!/usr/bin/env node
const fs = require('fs');
const config = {
  env: process.env.NODE_ENV || 'development',
  timestamp: new Date().toISOString(),
};
fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
console.log('config.json を生成しました');
'''

5-12. 複数タスクの同時実行(::: デリミタ)

:::で区切ることで、1コマンドで複数のタスクをそれぞれ異なる引数で同時実行できます。

# buildとtestを同時に実行
mise run build ::: test

# それぞれに引数を渡す
mise run build --release ::: test --verbose

5-13. 実行ディレクトリの制御(dir)

モノレポなどでパッケージごとにタスクを実行したい場合、dirで実行ディレクトリを指定できます。

mise.toml
[tasks."frontend:dev"]
description = "フロントエンドの開発サーバーを起動する"
dir = "packages/frontend"
run = "npm run dev"

[tasks."backend:dev"]
description = "バックエンドの開発サーバーを起動する"
dir = "packages/backend"
run = "npm run dev"

[tasks."test:here"]
description = "カレントディレクトリでテストを実行する"
dir = "{{cwd}}"
run = "npm test"

{{cwd}}を使うと、mise runを実行したディレクトリでタスクが実行されます。デフォルトではmise.tomlがあるディレクトリで実行されるため、この違いを覚えておくと便利です。

5-14. 個人用タスクの管理(mise.local.toml)

チーム開発で特に便利な機能です。mise.local.tomlを作成すると、チームに共有しない個人用のタスクを定義できます。

miseの設定ファイルには優先順位があり、mise.local.tomlが最も優先されます。

ファイル 用途 Git管理
mise.local.toml 個人カスタム設定 .gitignoreに追加(コミットしない)
mise.toml プロジェクト共通設定 コミットする
~/.config/mise/config.toml グローバル設定 -

たとえば、自分だけが使うデバッグ用タスクや、ローカル環境固有の設定をmise.local.tomlに書けます。

mise.local.toml
# 自分だけが使うデバッグ用タスク
[tasks.debug]
description = "デバッグモードで起動する"
env = { DEBUG = "true", LOG_LEVEL = "verbose" }
run = "npm run dev"

# ローカルのDocker環境を使ったDB起動
[tasks."db:up"]
description = "ローカルDBを起動する"
run = "docker compose -f docker-compose.dev.yml up -d"

[tasks."db:reset"]
description = "ローカルDBをリセットする"
confirm = "データベースを完全にリセットします。よろしいですか?"
run = [
  "docker compose -f docker-compose.dev.yml down -v",
  "docker compose -f docker-compose.dev.yml up -d",
  "npm run db:migrate",
]

mise.local.tomlはmise固有のファイルなので、プロジェクトの.gitignoreではなくグローバルのgitignoreに追加するのが適切です。.DS_Store.idea/と同じ考え方です。

# グローバルのgitignoreに追加
echo "mise.local.toml" >> ~/.config/git/ignore

チームでまだmiseを導入していなくても、個人で先に使い始められるのもポイントです。mise.local.tomlはGit管理外なので、他のメンバーに影響を与えません。自分だけMakefileの代わりにmiseを使い、便利さを実感してからチームに提案する、という段階的な導入ができます。

5-15. 外部スクリプトの実行(file)

runに直接コマンドを書く代わりに、fileでスクリプトファイルを指定できます。長くなったタスクを外部ファイルに切り出すのに便利です。

mise.toml
[tasks.release]
description = "リリーススクリプトを実行する"
confirm = "リリースを実行しますか?"
file = "scripts/release.sh"

ローカルファイルだけでなく、HTTPやGitリポジトリからリモートスクリプトを取得して実行することもできます。チーム共通のスクリプトを一元管理したい場合に便利です。

mise.toml
# HTTP経由でスクリプトを取得・実行
[tasks.setup-ci]
description = "CI環境をセットアップする"
file = "https://raw.githubusercontent.com/myorg/scripts/main/setup-ci.sh"

# Gitリポジトリから特定バージョンのスクリプトを取得
[tasks.deploy-infra]
description = "インフラをデプロイする"
file = "git::https://github.com/myorg/deploy-scripts.git//deploy.sh?ref=v2.0.0"

5-16. ファイルベースタスク(mise-tasks/ディレクトリ)

TOML内に書くには長すぎるスクリプトは、mise-tasks/ディレクトリにファイルとして配置できます。ディレクトリ構造がそのままタスクの名前空間になります。

mise-tasks/
├── build           # → mise run build
├── lint            # → mise run lint
└── db/
    ├── migrate     # → mise run db:migrate
    ├── seed        # → mise run db:seed
    └── reset       # → mise run db:reset

各ファイルには#MISEコメントで設定を記述します。

mise-tasks/db/reset
#!/usr/bin/env bash
#MISE description="データベースをリセットする"
#MISE depends=["db:down"]
#MISE confirm="データベースを完全にリセットします。よろしいですか?"

docker compose up -d postgres
sleep 3
npm run db:migrate:reset
npm run db:seed
echo "Database reset complete!"
# 実行可能権限を付与する(必須)
chmod +x mise-tasks/db/reset

TOML定義とファイルベースは混在可能です。シンプルなタスクはmise.tomlに、複雑なスクリプトはmise-tasks/に、と使い分けるのがおすすめです。

5-17. タスクのタイムアウト設定

長時間実行を防止したい場合、グローバルにタイムアウトを設定できます。

mise.toml
[settings]
task.timeout = "10m"

CIで「テストがハングして永遠に終わらない」を防ぐのに使えます。

つまずきポイントと対処法

つまずき1: usageの引数構文が分からない

原因:

  • usageフィールドはTOMLでもシェルでもない独自のDSL(usage-libの構文)で、初見では戸惑いやすい
  • 定義した引数がusage_プレフィックスの環境変数に暗黙的に展開される仕組みも独特

対処法:

  • usageを使わなくてもmise Tasksの基本的な移行は問題なくできる。まずはenvフィールドで環境変数を渡す方法で十分
  • 引数が必要になったタイミングで、シンプルなargから試すのがおすすめ
mise.toml
# Step 1: まずはenvで十分
[tasks.deploy]
env = { ENVIRONMENT = "staging" }
run = 'aws s3 sync dist/ s3://myapp-${ENVIRONMENT}/'

# Step 2: 引数が欲しくなったらusageを追加
[tasks.deploy]
usage = 'arg "<environment>" help="デプロイ先" default="staging"'
run = 'aws s3 sync dist/ s3://myapp-${usage_environment?}/'
  • mise run <task> --helpで引数の使い方が自動表示されるので、定義した後の確認も簡単

つまずき2: 依存タスクが並列で実行されてしまう

原因:

  • dependsに指定したタスクはデフォルトで並列実行される
  • 例えばdepends = ["lint", "build"]とすると、lintとbuildが同時に実行される

解決方法:

  • 順序が重要な場合はdependsではなく、runに配列で複数コマンドを指定する
  • または--jobs 1オプションで直列実行を指定する
mise run deploy --jobs 1
mise.toml
# run配列なら必ず上から順に実行される
[tasks.ci]
run = [
  "npm run lint",
  "npm run test",
  "npm run build",
]

つまずき3: Makefileの変数展開が使えない

原因:

  • Makefileの$(VAR)$(shell ...)$@$$varなどの構文はmiseでは使えない

解決方法:

Makefileの変数パターンごとに、miseでの書き換え方法が異なります。

Makefile
# Makefileの変数パターン
APP_NAME := myapp
NODE_ENV ?= development
S3_BUCKET := $(APP_NAME)-$(NODE_ENV)

deploy:
	aws s3 sync dist/ s3://$(S3_BUCKET)/
mise.toml
# miseでの書き換え

# パターン1: env フィールドで定義
[tasks.deploy]
env = { APP_NAME = "myapp" }
run = 'aws s3 sync dist/ s3://${APP_NAME}-staging/'

# パターン2: usage(引数)で外部から受け取る
[tasks.deploy]
usage = 'arg "<environment>" help="デプロイ先" default="staging"'
run = 'aws s3 sync dist/ s3://myapp-${usage_environment?}/'

# パターン3: シェルスクリプト内で組み立てる
[tasks.deploy]
env = { APP_NAME = "myapp" }
usage = 'arg "<environment>" help="デプロイ先" default="staging"'
run = '''
#!/usr/bin/env bash
BUCKET="${APP_NAME}-${usage_environment?}"
aws s3 sync dist/ "s3://${BUCKET}/"
'''

動作確認

移行が完了したら、Makefileのタスクと対応するmiseコマンドが正しく動作するか確認します。

# タスク一覧の確認(Makefileのhelpターゲット相当)
mise tasks

# 開発系
mise run dev          # make dev 相当
mise run lint         # make lint 相当
mise run lint:fix     # make lint-fix 相当

# テスト系
mise run test         # make test 相当
mise run test:ci      # make test-ci 相当

# DB系
mise run db:up        # make db-up 相当
mise run db:reset     # make db-reset 相当(確認プロンプト付き)

# セットアップ
mise run setup        # make setup 相当

# ビルド&デプロイ
mise run build        # make build 相当
mise run deploy       # make deploy 相当(test:ciとbuildが事前に並列実行される)
mise run deploy:prod  # make deploy-prod 相当(確認プロンプト付き)

# クリーンアップ
mise run clean        # make clean 相当(確認プロンプト付き)

最終的なmise.tomlの例

手順3の基本形に、手順5で紹介した便利機能(エイリアス、変更検知、引数サポート等)を組み込んだ例です。プロジェクトに合わせて必要な機能を取捨選択してください。

mise.toml
# =====================
# 開発
# =====================
[tasks.dev]
description = "開発サーバーを起動する(DB自動起動付き)"
depends = ["db:up"]
env = { NODE_ENV = "development" }
run = "npm run dev"

[tasks.build]
description = "プロダクションビルドを実行する"
alias = "b"
env = { NODE_ENV = "production" }
sources = ["src/**/*", "package.json", "tsconfig.json"]
outputs = ["dist/**/*"]
run = "npm run build"

# =====================
# コード品質
# =====================
[tasks.lint]
description = "リンターと型チェックを実行する"
alias = "l"
run = [
  "npm run lint",
  "npm run type-check",
]

[tasks."lint:fix"]
description = "Lint修正とフォーマットを実行する"
run = [
  "npm run lint -- --fix",
  "npm run format",
]

# =====================
# テスト
# =====================
[tasks.test]
description = "テストを実行する"
alias = "t"
env = { NODE_ENV = "test" }
run = "npm run test"

[tasks."test:ci"]
description = "CI用のチェックを一括実行する(lint + test + coverage)"
depends = ["lint"]
env = { NODE_ENV = "test" }
run = "npm run test -- --coverage --ci"

# =====================
# Docker / DB
# =====================
[tasks."db:up"]
description = "データベースコンテナを起動する"
run = "docker compose up -d postgres redis"

[tasks."db:down"]
description = "データベースコンテナを停止する"
run = "docker compose down"

[tasks."db:reset"]
description = "データベースをリセットする(データ全削除)"
depends = ["db:down"]
confirm = "データベースを完全にリセットします。よろしいですか?"
run = [
  "docker compose up -d postgres",
  "sleep 3",
  "npm run db:migrate:reset",
  "npm run db:seed",
]

[tasks.migrate]
description = "DBマイグレーションを実行する"
run = "npm run db:migrate"

[tasks.seed]
description = "DBにシードデータを投入する"
run = "npm run db:seed"

# =====================
# セットアップ
# =====================
[tasks.setup]
description = "開発環境を初期セットアップする"
run = [
  "cp .env.example .env",
  "npm install",
  "docker compose up -d postgres redis",
  "sleep 3",
  "npm run db:migrate",
  "npm run db:seed",
]

# =====================
# デプロイ
# =====================
[tasks.deploy]
description = "指定環境にデプロイする"
depends = ["test:ci", "build"]
usage = '''
arg "<environment>" help="デプロイ先" default="staging" {
  choices "staging" "prod"
}
'''
confirm = "{{ usage.environment }} にデプロイします。よろしいですか?"
run = 'aws s3 sync dist/ s3://myapp-${usage_environment?}/'

# =====================
# クリーンアップ
# =====================
[tasks.clean]
description = "ビルド成果物を削除する"
confirm = "dist/, node_modules/, .cache/, coverage/ を削除します。よろしいですか?"
run = "rm -rf dist node_modules .next .cache coverage"

まとめ

MakefileからmiseのTasks機能へのサンプル移行手順、Tasks機能についてまとめました。

観点 Makefile mise Tasks
記法 Makefile独自の構文 TOML形式
タスク一覧 自前で実装が必要 mise tasksで標準対応
依存関係 target: dep1 dep2 depends / wait_for / depends_post
環境変数 export VAR=value env = { VAR = "value" }
引数サポート $(filter-out ...)等のハック usageで宣言的に定義
実行前の確認 read -pを自前実装 confirmで1行
ファイル監視 別ツール(entr等)が必要 mise watchで標準対応
変更検知スキップ タイムスタンプベース sources/outputsでグロブ対応
ツールバージョン指定 nvm use等を自前で記述 toolsでタスク単位指定
インタラクティブ選択 なし mise runで対話的にタスク選択
ヘルプ自動生成 helpターゲットを手動管理 description+usageで自動生成
個人用タスク なし(全員に影響) mise.local.tomlで個人管理
リモートスクリプト curl | bash等で自前実装 fileでHTTP/Git URLを直接指定
タイムアウト なし task.timeoutで設定可能
.PHONY宣言 必要 不要
ランタイム管理 別ツールが必要 mise自体で統合管理

miseでランタイム管理をしているプロジェクトであれば、タスクランナーも統一することで設定ファイルがシンプルになります。移行自体も、既存のMakefileのコマンドをTOML形式に書き換えるだけなので比較的簡単かなと思います。

引数サポート、ファイル監視、実行前の確認プロンプト、タスク専用ツール指定といった機能は、Makefileでは実装が面倒なところもあるのでmiseを利用しているのであれば、Tasksの機能も試してみてください。

誰かの参考になれば幸いです。


参考リンク:

この記事をシェアする

FacebookHatena blogX

関連記事