🥘

Forget `make`, `just` run.

に公開

## はじめに

Web開発の現場で Makefile をタスクランナーとして使用している所は今でも少なくないと思います。

が、しかし Makefile は本来C、C++のコンパイルなどのビルド作業での使用を想定されたツールであり、タスクランナーとして使用するとどうしても苦しい場面に遭遇することがあります。

  • コマンド名と同名のファイル名が存在すると実行されない(.PHONY が必要)
  • tab によるインデントが必須
  • 引数を渡すのが一癖ある ( make task ARG=hoge など)
  • Makefile が配置してあるディレクトリからしかタスクを実行できない
  • etc...

make(メイク)は、プログラムのビルド作業を自動化するツール。コンパイル、リンク、インストール等のルールを記述したテキストファイル (makefile) に従って、これらの作業を自動的に行う。
引用:make (UNIX) - Wikipedia

今回はそんな悩みを解決する 「just」 を紹介します。

## just とは

just は Rust製のタスクランナーです。
構文は Makefile に似ていますがより直感的に記述できるようになっています。
また、Makefile に比べて機能も豊富で複雑な要件にも対応できます。

ちなみに、just ではタスクのことを「レシピ」と呼びます。

https://github.com/casey/just

### インストール

macOSでは Homebrew を使ってインストールできます。
他にも様々なインストール方法が用意されています。

brew install just

### 設定ファイル

設定ファイルは justfile という名前で配置します。
Justfile, JUSTFILE, .justfile など色々な名前に対応してます。

## justの魅力

筆者がMakefileから移行するきっかけになったjustの魅力を紹介します。

### 引数が扱いやすい

Makefileでは引数を渡す際に make task ARG=value のような形式で変数を指定する必要があります。

Makefile
greet:
	echo "Hello, $(NAME)!"
shell
make greet NAME=Alice
# 出力: Hello, Alice!

一方、just ではレシピに引数を直接定義でき、慣れ親しんだCLIツールのように引数を渡すことができます。

justfile
greet name:
  echo "Hello, {{name}}!"
shell
just greet Alice
# 出力: Hello, Alice!

### どこからでも実行できる

Makefileでは、Makefileが配置されているディレクトリからしかタスクを実行できません。

shell
cd sub_directory
make build
# make: *** No rule to make target `build'.  Stop.

just では justfile が配置されているディレクトリ配下であれば、どこからでも実行できます。

shell
cd sub_directory
just build
# 正常に実行される

### 豊富な組み込み関数

just はビルドツールではなくタスクランナーなので便利な組み込み関数がたくさん用意されています。
shell芸を披露せずに済みます。

justfile
# システム情報
system-info:
  @echo "OS: {{os()}}"           # linux, macos, windows
  @echo "Arch: {{arch()}}"       # x86_64, aarch64
  @echo "CPUs: {{num_cpus()}}"   # CPU数

# 日時処理
backup:
  tar -czf "backup_{{datetime('%Y%m%d_%H%M%S')}}.tar.gz" ./data

# 文字列操作
name := "hello_world"

convert:
  @echo "{{uppercase(name)}}"      # HELLO_WORLD
  @echo "{{kebabcase(name)}}"      # hello-world
  @echo "{{replace(name, '_', '-')}}"  # hello-world

# シェル実行
git_branch := `git branch --show-current` # ``で囲う事で実行結果を変数に格納
branch_name:
  @echo "{{git_branch}}"

# shell関数でコマンドを実行
line_count := shell('wc -l < "$1"', 'README.md')

info:
  @echo "Lines: {{trim(line_count)}}"

## 実用例

実際の開発現場でよく使われるであろうパターンや筆者がよく使っているパターンを紹介します。

### Dockerコマンドのラップ

docker compose はコマンドが長くなりがちなので、just でラップしておくと快適です。

ちなみに alias を使うことでエイリアスを作成できます。

justfile
# コンテナ起動
up:
  docker compose up -d
alias u := up

# コンテナ停止
down:
  docker compose down
alias d := down

### 開発環境のセットアップ

開発環境のセットアップをコマンド一発で実行できるようにしておくと快適ですよね〜

ちなみに [group()] を使うことでレシピをグループ化できます。

justfile
[group('all')]
setup: build db-create db-migrate up start-frontend

[group('backend')]
up:
  docker compose up -d

[group('backend')]
build:
  docker compose build --no-cache

[group('backend')]
db-create:
  docker compose run --rm api bin/rails db:create

[group('backend')]
db-migrate:
  docker compose run --rm api bin/rails db:migrate

[group('frontend')]
start-frontend:
  pnpm install && pnpm dev

### 柔軟なコマンド実行を実現する

justの可変長引数を活用すると、コンテナ内やモノレポ環境で任意のコマンドを実行できるようになります。
これが便利で筆者はよく使っています。

  • +arg: 1つ以上の引数を受け取る(必須)
  • *arg: 0個以上の引数を受け取る(省略可能)
justfile
# +: 引数必須(コンテナ内でコマンド実行)
backend-run +script:
  docker compose run --rm api {{script}}

# *: 引数省略可(デフォルト動作を定義できる)
frontend-run *args:
  #!/usr/bin/env bash
  if [ -z "{{args}}" ]; then
    cd frontend && pnpm dev
  else
    cd frontend && pnpm {{args}}
  fi
shell
just backend-run bin/rails console
just frontend-run          # → pnpm dev
just frontend-run lint     # → pnpm lint

### Makefileとの共存

無理矢理感はありますがmakeコマンドをjustでラップしてしまえば、タスクランナーをjustに統一できます。

justfile
proxy-make +cmd:
  make {{cmd}}
alias make := proxy-make
shell
just make build    # → make build
just make test     # → make test

Makefileから段階的に移行したり、個人のローカル環境でのみjustを使う場合などに役に立つかもしれません。

### 危険な操作に確認を挟む

[confirm()] を使うと、レシピ実行前に確認プロンプトを表示できます。
データ削除など取り消しできない操作に付けておくと安心です。

justfile
[confirm('本当に削除しますか?(y/n)')]
cleanup:
  #!/usr/bin/env bash
  find . -type d -empty -delete
  echo "Cleanup completed."
shell
just cleanup
# 本当に削除しますか?(y/n)

-y フラグで確認をスキップできるので、CIでも使えます。

shell
just -y cleanup

### justのみ実行でレシピ一覧を確認する

just --list でレシピ一覧を確認できるのですがこれをjustのみで実行できるようにしておくと便利です。
「あーあのレシピなんだっけ?」となったときにjustと打つだけでレシピ一覧を確認できます。

justfile
_:
  @just --list
shell
just
# Available recipes:
#     [backend]
#     backend-exec +script         # [alias: be]
#     backend-run +script          # [alias: b]
#     build
#     db-create                    # [alias: dc]
#     db-drop                      # [alias: dd]
#     db-migrate                   # [alias: dm]
#     down                         # [alias: d]
#     up                           # [alias: u]

#     [frontend]
#     start-frontend-app *app      # [alias: sf]

#     [utility]
#     proxy-make +cmd              # [alias: make]
#     remove-unused-dirs           # [alias: rud]

[group()] でグループ化しておくと、グループごとに表示されるのでさらに見やすくなります。

## まとめ

今回は Makefile を代替するタスクランナーとして just を紹介しました。

個人的最推しポイントは fish や zsh などのシェルで just → j とエイリアスを組んでおくことでホームポジションから指を動かさずに入力できることです。

筆者は開発環境の管理には mise を使っているのですが mise のタスクランナーよりも just の方が直感的に書けますし、依存関係も簡単に管理できるのでタスクランナーだけは just を使用しています。

みなさんもこの冬、一度 Makefile のことは忘れて just を試してみてはいかがでしょうか。

GitHubで編集を提案
SMARTCAMP Engineer Blog

Discussion