モダンなコマンドランナー「just」で整えるクロスプラットフォーム自動化レシピ

モダンなコマンドランナー「just」で整えるクロスプラットフォーム自動化レシピ

2026/10/18
目次
開閉
ターミナルで快適にコマンドを実行する様子

Dockerコンテナの起動、データベースのマイグレーション、テストやリントの一括実行。Webアプリケーションの開発現場では、日常的に叩くコマンドが年々増え続けています。

こうした複雑なコマンドを1つの短いエイリアスに束ねる道具として、長年親しまれてきたのが Makefile です。

私自身、これまで多くのプロジェクトでMakefileを活用してきました。しかし、チームの規模が大きくなったり、会社支給のMacと自宅のWindows PCを行き来する開発スタイルが定着したりするにつれて、Makefile特有の「地味な引っかかり」に直面する場面が増えてきました。

  • エディタの自動フォーマッタがスペースに変換してしまい、missing separator で構文エラーになる
  • .env の環境変数をコマンド内で使いたいだけなのに、シェル芸じみた記述が必要になる
  • Windows環境のメンバーが参入した際、「make コマンドが認識されない」「パスの区切り文字でコケる」といったトラブルで環境構築が止まる

40年以上前にC言語のコンパイル向けに設計されたツールを、令和のWeb開発のタスクランナーとして使い続けることには、少なからず無理が生じていたのも事実です。

このストレスを根本から解消し、MacでもWindowsでも快適にコマンド自動化を回せるモダンな選択肢が、Rust製のコマンドランナー「just」(公式マニュアル)です。今回は、Makefileからjustへ移行して実感した手触りの良さと、実務で役立つ実践レシピをまとめました。


なぜ Makefile ではなく just なのか?

Makeは「ファイルのタイムスタンプを比較して、更新されたファイルだけをビルドする」ためのビルドツールです。一方、justは最初から「コマンドを実行して定型作業を自動化する(コマンドランナー)」ことだけに特化して作られています。

この設計思想の違いが、日々の開発体験において決定的な差を生み出します。

1. タブ文字縛りからの完全な解放

Makefile最大の罠といえば、レシピのインデントに「タブ文字」が必須である点です。うっかりスペース4つでインデントすると、即座に実行時エラーを吐いて止まります。

Makefile:4: *** missing separator.  Stop.

just では、タブ文字はもちろん、半角スペース2つでも4つでも自由にインデント可能です。VS Codeやエディタの保存時フォーマット設定に気を使ったり、.editorconfig でMakefile専用の例外設定を書いたりするストレスから完全に解放されます。

2. .env ファイルの自動読み込み

Web開発では、APIキーやDB接続URLを .env で管理するのが一般的です。Makefileでこれを扱おうとすると、ファイルの存在チェックや export $(cat .env) のような工夫が必要でした。

justなら、ファイルの先頭に以下の設定を1行書くだけで完了します。

set dotenv-load

これだけで、カレントディレクトリの .env が自動的に読み込まれ、すべてのレシピ内で環境変数がそのまま透過的に参照できるようになります。

3. レシピごとに「実行言語」を切り替えられる

Makefileのレシピは基本的にシステムのシェル(sh/bash)で動きます。そのため、少し凝った条件分岐やJSONの加工を行いたい場合、可読性の低いシェルスクリプトを無理やりインラインで書くことになりがちでした。

justはレシピの冒頭にシバン(#!)を書くことで、レシピごとにPython、Node.js、PowerShell、shを自由に切り替えて記述できます。

# Pythonを使ってAPIのヘルスチェックを行うレシピ例
health-check:
    #!/usr/bin/env python3
    import urllib.request
    import json

    url = "http://localhost:8000/health"
    try:
        with urllib.request.urlopen(url) as res:
            data = json.loads(res.read().decode())
            print(f"API Status: {data.get('status', 'unknown')}")
    except Exception as e:
        print(f"Health check failed: {e}")

チーム内で共通して使われているスクリプト言語をそのまま活かせるため、メンテナンス性が大幅に向上します。

4. 引数の扱いとデフォルト値が直感的

Makefileで引数を渡す場合、make deploy target=staging のように環境変数渡しにするのが一般的で、引数のバリデーションやデフォルト値の制御には工夫が必要でした。

justは関数定義のように位置引数や可変長引数を自然に扱えます。


# 環境名を引数に取り、指定がない場合は 'local' をデフォルトにする
deploy env='local':
    @echo "Deploying to {{ env }} environment..."

呼び出し側も just deploy や just deploy production と直感的に指定できます。


環境構築と基本操作

インストールは各OSの標準パッケージマネージャから1コマンドで完結します。

# macOS (Homebrew)
brew install just

# Windows (winget または scoop / cargo)
winget install Casey.Just
# または
scoop install just

導入後、バージョンを確認してみます。

just --version
# 出力例: just 1.35.0

最初の一歩:justfile を作成する

リポジトリのルートディレクトリに justfile(または Justfile)を作成します。

# .envファイルを自動で読み込む
set dotenv-load

# デフォルトで実行されるレシピ(一覧表示)
default:
    @just --list

# ローカル開発コンテナをバックグラウンドで起動する
up:
    docker compose up -d

# すべてのコンテナを安全に停止する
down:
    docker compose down

just --list による美しい自己文書化

just(または just --list)を実行すると、ファイル内に書いたコメントがそのまま整形され、以下のように色付きのヘルプ一覧として出力されます。

Available recipes:
    default # デフォルトで実行されるレシピ(一覧表示)
    down    # すべてのコンテナを安全に停止する
    up      # ローカル開発コンテナをバックグラウンドで起動する

新しくチームに参加したメンバーも、just と叩くだけで「このプロジェクトで日常的に叩くべきコマンド」が一目で把握できます。READMEのコマンド一覧が陳腐化して嘘をつく問題も防げます。


実務でそのまま使える実践レシピ集

ここからは、私が実際の自社サービスや業務プロジェクトで愛用している実用レシピを紹介します。


set dotenv-load

# 利用可能なタスク一覧を表示
default:
    @just --list

# 開発用コンテナ起動とログ監視
dev:
    docker compose up -d
    docker compose logs -f app

# フロントエンドとバックエンドのLintを一括実行
lint:
    npm run lint
    cargo clippy -- -D warnings

# 単体テストを実行(引数で特定ファイルやフィルタを指定可能)
test path="":
    #!/usr/bin/env bash
    if [ -z "{{ path }}" ]; then
        echo "Running all tests..."
        cargo test
    else
        echo "Running specific test: {{ path }}..."
        cargo test {{ path }}
    fi

# 【注意】ローカルDBを初期化してマイグレーションとシードを再投入
db-reset:
    #!/usr/bin/env bash
    echo "⚠️  ローカルデータベースを完全に再構築します。"
    read -p "続行しますか? (y/N): " confirm
    if [[ "$confirm" =~ ^[Yy]$ ]]; then
        docker compose stop db
        docker compose rm -f -v db
        docker compose up -d db
        echo "Waiting for PostgreSQL to be ready..."
        until docker compose exec db pg_isready -U postgres > /dev/null 2>&1; do
            sleep 1
        done
        npm run db:migrate
        npm run db:seed
        echo "✅ データベースの初期化が完了しました。"
    else
        echo "中止しました。"
    fi

レシピのポイント解説

  • 引数のオプショナル受け取り(test path=""): 引数なしなら just test で全件実行、特定パスを狙い撃ちしたい時は just test users::tests のように柔軟に指定できます。
  • 破壊的コマンドの安全ガード(db-reset): DBの全初期化など取り返しのつかないコマンドには、シェルスクリプトの対話式プロンプト(read -p)を組み込んでおくと、タイポや誤実行による手戻りを確実に防げます。
Lowom 編集長 編集長
CAUTION 対話プロンプトとCI環境の相性
ローカルでは便利な対話プロンプト(read -p)ですが、CI環境(GitHub Actions等)で実行すると入力待ちでタイムアウト停止してしまいます。CIでも同じレシピを流したい場合は、`just db-reset --force` のようなフラグ引数を用意し、CI時は確認をスキップする条件分岐を入れておくと安全です。

MacとWindowsの両立:クロスプラットフォーム運用のツボ

会社ではMacBook Proを使い、自宅ではWindowsノートPCを開くエンジニアにとって、OS固有のコマンド差異は地味なストレス源になります。

MakefileではWindows対応のために別途 make.exe を入れさせたり、パス区切り(/ と \)の違いで苦労したりすることが日常茶飯事でした。justはこの点でも非常に洗練されています。

1. set shell によるデフォルトシェルの統一

Windowsマシンで just を動かす際、デフォルトでは cmd.exe や PowerShell が呼び出されます。もしMacやLinuxと同じようにbash文法で共通化したい場合は、以下のように指定できます。

# Windows環境でもGit for Windowsのbashを使う設定例
set windows-shell := ["sh", "-c"]

これだけで、Windows上でもMacと全く同一のシェル記法が動作し、OSごとのレシピ分岐を書く必要性が激減します。

2. OS固有のレシピは属性で切り替える

どうしてもOSごとに処理を分けたい場合(たとえばブラウザの起動コマンドやクリップボード操作など)は、属性タグを使って綺麗に記述できます。


# macOS用のブラウザ起動
[macos]
open-browser url="http://localhost:3000":
    open {{ url }}

# Windows用のブラウザ起動
[windows]
open-browser url="http://localhost:3000":
    cmd.exe /c start {{ url }}

# Linux用のブラウザ起動
[linux]
open-browser url="http://localhost:3000":
    xdg-open {{ url }}

呼び出し側はどのOSであっても just open-browser と叩くだけです。実行環境のOSをjustが自動判別して、適切な実装を呼び出してくれます。

Lowom 編集長 編集長
TIPS 現場のワンポイント
自宅のWindows環境では、WSL2(Ubuntu)の中でjustを動かす運用も非常に快適です。WindowsネイティブのPowerShellから叩きたいリポジトリには`windows-shell`を設定し、Docker中心のリポジトリはWSL2側でそのまま実行するなど、柔軟に使い分けるのがおすすめです。

あえて知っておくべきトレードオフ(割り切り)

ここまでjustの長所を中心に紹介してきましたが、実務で採用する以上、トレードオフも正しく理解しておく必要があります。

1. 「事前インストール」が必要になる

Makeの最大の強みは、「ほぼすべてのUnix系OSやCI環境にデフォルトで最初から入っていること」です。一方、justはサードパーティ製ツールであるため、チームメンバー各自のローカル環境にインストールしてもらう必要があります。

幸い、Homebrew、winget、cargo、npm(npx just-install)などあらゆる手段で数秒で導入できるため、導入のハードルは昔に比べて非常に低くなっています。

2. GitHub ActionsなどのCIでの利用

CI環境でjustを使いたい場合は、ステップの冒頭でセットアップアクションを呼び出します。公式がメンテナンスしている extra/setup-just を使えば、1ステップ追加するだけで導入できます。

- name: Set up just
  uses: extra/setup-just@v2

- name: Run lint and test
  run: |
    just lint
    just test

3. ファイル間の依存関係追跡はMakeに軍配が上がる

「C/C++ソースコードのタイムスタンプを見て、変更があったオブジェクトファイルだけを差分リビルドする」といった厳密なファイル依存グラフの解決は、Makeの独壇場です。justは依存ファイルを監視してビルドをスキップする機能は持っていません。

しかし、現代のWeb開発において、TypeScriptのトランスパイルやGo/Rustのビルドはそれぞれの言語ツールチェーン(Vite, Next.js, cargo, go build)が自前で高速なキャッシュ機構を持っています。私たちがコマンドランナーに求めているのは「複合コマンドの呼び出しやすさと共通化」であるため、この割り切りは実務上まったく問題になりません。


道具を少し新しくするだけで、開発のリズムは整う

チーム開発において、「開発環境のコマンドを迷わず叩けること」は、エンジニアの認知負荷を下げる非常に重要な土台です。

「Makefileのタブ文字エラーで新人の手が止まる」「WindowsだとMakefileが動かないから個別にコマンドを打ち込む」といった日々の小さな摩擦は、1回あたりは数分のロスでも、チーム全体で見れば確実に開発のリズムを鈍らせていきます。

以前ご紹介したMakefileのTipsからさらに一歩進めて、より柔軟でモダンなタスク自動化を取り入れてみたい方は、ぜひ次のプロジェクトで justfile を1つ置いてみてください。その軽快な手触りに、きっと驚くはずです。

以前解説した、Dockerコマンドの集約やローカル環境の共通化の考え方については、以下の記事でも詳しく取り上げています。あわせて参考にしてみてください。

Lowom 編集長
この記事を書いた人:Lowom 編集長 現役Webエンジニア

都内IT企業に勤める業界20年のWebエンジニア。業務効率化・自動化スクリプトや快適な開発環境の構築、厳選したツール・ガジェットの活用法など、手元で実際に検証したリアルな一次情報をお届けします。

運営者プロフィール詳細を見る