Sổ tay kỹ thuật

Makefile và just làm task runner

cheatsheet#make#just#cli· cập nhật 2026-11-06

Tra nhanh cú pháp Makefile (.PHONY, biến, tab, target phụ thuộc) và just để gom lệnh hay chạy của project thành make test, just deploy.

Sửa trang này
Mục lục

Mục tiêu: gõ make test / just test thay vì nhớ chuỗi lệnh dài; CI và người mới chạy giống nhau. Đã chạy thử với GNU Make 4.3 và just 1.59 (uvx --from rust-just just nếu chưa cài). make có sẵn trên hầu hết Linux; just cú pháp gọn hơn, truyền tham số dễ hơn nhưng phải cài thêm.

Makefile mẫu

Makefile
.PHONY: help install lint test run image deploy clean
.DEFAULT_GOAL := help
SHELL := bash
.SHELLFLAGS := -eu -o pipefail -c
 
PY ?= uv run
IMAGE := netinv
TAG ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo dev)
SRC := $(wildcard app/*.py)
export APP_ENV ?= dev
 
help: ## Liệt kê target
	@grep -E '^[a-zA-Z_-]+:.*?## ' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "%-12s %s\n", $$1, $$2}'
 
install: ## Cài dependency
	uv sync
 
lint: ## ruff + mypy
	$(PY) ruff check .
	$(PY) mypy
 
test: lint ## Chạy test (sau lint)
	$(PY) pytest -q $(ARGS)
 
run:
	$(PY) fastapi dev app/main.py
 
image:
	docker build -t $(IMAGE):$(TAG) .
 
deploy: test image
	@echo "deploy $(IMAGE):$(TAG) env=$$APP_ENV"
 
clean:
	-rm -rf .pytest_cache .ruff_cache
 
out/report.txt: $(SRC)
	mkdir -p $(@D)
	echo $^ > $@

Dòng lệnh trong recipe phải bắt đầu bằng ký tự TAB, không phải dấu cách. Sai sẽ báo Makefile:N: *** missing separator. Stop. Kiểm tra bằng cat -A Makefile: TAB hiện thành ^I.

Cú pháp Make

Mục đích Cú pháp
Target không phải file (luôn chạy) .PHONY: test lint
Target mặc định khi gõ make .DEFAULT_GOAL := help (hoặc target đầu tiên trong file)
Phụ thuộc: chạy lint trước test test: lint
Gán ngay / gán lười (đánh giá khi dùng) IMAGE := netinv / IMAGE = netinv
Gán nếu chưa có (cho phép override từ env) TAG ?= dev
Nối thêm FLAGS += -v
Dùng biến Make / biến shell trong recipe $(IMAGE) / $$HOME
Kết quả lệnh shell $(shell git rev-parse --short HEAD)
Danh sách file $(wildcard app/*.py)
Xuất biến ra env của recipe export APP_ENV ?= dev
Không in lệnh trước khi chạy @echo xong
Bỏ qua lỗi của một dòng -rm -rf build
Biến tự động: target / prerequisite đầu / tất cả / thư mục target $@ / $< / $^ / $(@D)
Dùng bash + dừng ngay khi lỗi SHELL := bash và .SHELLFLAGS := -eu -o pipefail -c
Mỗi dòng recipe là một shell riêng cd x && make phải viết trên cùng một dòng

Lệnh make

Mục đích Lệnh
Chạy target make test
Truyền / override biến make test ARGS="-k vlan", make image TAG=v1.2.0
In lệnh sẽ chạy, không chạy (dry-run) make -n deploy
Chạy ở thư mục khác make -C services/api test
Chạy song song make -j4 lint image
Bắt buộc build lại mọi target make -B
Xem giá trị biến sau khi parse make -p | grep '^TAG '
Tiếp tục các target khác khi một target lỗi make -k

justfile mẫu

justfile
set dotenv-load
set shell := ["bash", "-euo", "pipefail", "-c"]
 
image := "netinv"
tag := `git rev-parse --short HEAD 2>/dev/null || echo dev`
env := env("APP_ENV", "dev")
 
# Liệt kê recipe (recipe đầu tiên là mặc định)
default:
    @just --list
 
# Cài dependency
install:
    uv sync
 
# ruff + mypy
lint:
    uv run ruff check .
    uv run mypy
 
# Chạy test, truyền thêm tham số: just test -k vlan
test *args: lint
    uv run pytest -q {{args}}
 
# Build image, tag tuỳ chọn: just image v1.2.0
image t=tag:
    docker build -t {{image}}:{{t}} .
 
# Deploy, hỏi xác nhận trước
[confirm("Deploy lên production?")]
deploy: test (image tag)
    echo "deploy {{image}}:{{tag}} env={{env}}"
 
# Script nhiều dòng chạy trong một shell
backup host:
    #!/usr/bin/env bash
    set -euo pipefail
    mkdir -p out
    echo "backup {{host}}" > out/{{host}}.txt

Cú pháp và lệnh just

Mục đích Cú pháp / lệnh
Liệt kê recipe kèm comment phía trên just --list (-l)
Chạy recipe / truyền tham số just test -k vlan, just image v1.2.0, just backup pe1
Tham số có mặc định / nhận nhiều giá trị image t=tag: / test *args: (+args = ít nhất 1)
Dùng biến, tham số {{tên}}
Biến từ lệnh shell tag := `git rev-parse --short HEAD`
Biến từ env, có mặc định env := env("APP_ENV", "dev")
Tự nạp file .env set dotenv-load
Phụ thuộc, kèm tham số deploy: test (image tag)
Hỏi xác nhận trước khi chạy [confirm("Deploy lên production?")], bỏ hỏi: just --yes deploy
Script nhiều dòng trong một shell Dòng đầu recipe là shebang #!/usr/bin/env bash
Dry-run just -n test
Xem một recipe / giá trị biến just --show image / just --evaluate tag
Kiểm tra / sửa format justfile just --fmt --check / just --fmt

Chọn cái nào

Tiêu chí make just
Có sẵn trên server/CI Gần như luôn có Phải cài (uv tool install rust-just, apt/brew, binary)
Tab bắt buộc Có Không (thụt đầu dòng bất kỳ, nhất quán)
Tham số dòng lệnh Qua biến VAR=... Tham số vị trí như hàm
Build theo timestamp file Có (thế mạnh của make) Không, mọi recipe luôn chạy
Mỗi dòng một shell riêng Có Có (trừ recipe shebang)

Chỉ dùng như task runner thì liệt kê mọi target trong .PHONY; nếu không, khi lỡ có file/thư mục tên test thì make test báo 'test' is up to date và không chạy gì.