每个项目都有一些高频命令:构建、测试、部署、格式化、初始化环境。这些命令要么太长记不住,要么有执行顺序的依赖,要么散落在 README 的某个角落里,每次看文档才知道怎么跑。
just 解决这个问题:把这些命令固化成一个可发现、可复用、可交给 CI / Agent 执行的命令菜单。
1. 为什么不只在 README 里写
README 解释背景和动机,justfile 执行操作——两者分工不同,不应该混淆。
README 里的命令有三个缺陷:容易过时(代码改了,文档没改)、参数容易出错(复制粘贴时少了个 flag)、无法被工具直接执行(CI 脚本、Agent 必须解析自然语言文档)。
justfile 里的命令有名字、有依赖关系、有默认值、可以接参数——它是可执行的文档。
2. just vs Makefile
Makefile 能做同样的事,但有三个让人头疼的问题:
- 语法晦涩:
$@、$<、$^等自动变量,不查文档几乎看不懂 - Tab 陷阱:缩进必须是 Tab 而不是 Space,混用会报莫名其妙的错误
- 跨平台差异:GNU Make 和 BSD Make 行为不一致,macOS 默认是 BSD Make
just 的定位很清晰:专注做命令菜单这一件事,不是构建系统,没有文件时间戳比较,没有隐式规则——只是有名字的命令块,执行顺序由依赖声明决定。
3. 基本语法
justfile 放在项目根目录,文件名就叫 justfile(无扩展名)。
最小示例:
# 默认 recipe:直接运行 just 时执行
default:
just --list
# 构建
build:
cargo build --release
# 测试
test:
cargo test
# 带依赖:先 lint 和 test,再 build
ci: lint test build
# 带参数(有默认值)
serve port="8000":
python -m http.server {{ port }}
# 运行时:just serve 用默认端口,just serve 3000 指定端口关键语法点:
| 语法 | 含义 |
|---|---|
recipe-name: | 定义一个 recipe |
recipe: dep1 dep2 | recipe 的前置依赖(先执行 dep1 和 dep2) |
arg="default" | 带默认值的参数 |
{{ arg }} | 在命令里引用参数 |
@echo "..." | @ 前缀不打印命令本身,只打印输出 |
# 注释 | 在 just --list 里显示为描述 |
查看所有可用 recipe:
just --list
# 输出:
# build 构建项目
# ci lint + test + build
# serve 启动本地服务器
# test 运行测试just --list 是项目命令的自文档。新人第一次接触项目时,这一个命令告诉他能做什么。
4. 完整示例
一个典型前端项目的 justfile:
# 命令菜单(直接运行 just 显示)
default:
just --list
# 安装依赖和工具
bootstrap:
npm install
npx playwright install
# 开发服务器
dev:
npm run dev
# 运行测试
test:
npm test
# 代码检查
lint:
npx eslint src/ --max-warnings 0
# 格式化
fmt:
npx prettier --write "src/**/*.{js,ts,tsx}"
# 构建(先 lint 和 test)
build: lint test
npm run build
# 清理产物
clean:
rm -rf dist/ .next/ node_modules/.cache/
# 部署(先构建)
deploy: build
rsync -avz ./dist/ deploy@prod:/var/www/app/
@echo "Deployed to production"
# 检查所有(CI 入口)
ci: lint test build执行 just deploy 时,just 会自动先跑 build,build 又会先跑 lint 和 test——依赖链自动解析。
5. 全局 justfile
除了项目级 justfile,还可以有全局 justfile:
~/.config/just/justfile全局 recipe 在任何目录都可以调用,适合放系统维护类操作:
# 更新 Homebrew 和所有包
update:
brew update && brew upgrade
# 同步 dotfiles
sync:
chezmoi apply --verbose
# 查看 dotfiles 变更
diff:
chezmoi diff
# 清理系统缓存
clean:
brew cleanup
npm cache clean --force项目里的 justfile 优先级高于全局,两者可以共存。
6. 与工具链集成
CI/CD 共用入口:CI 跑 just test,本地也跑 just test,保证两者执行的是完全相同的命令——环境差异来源减少。
# GitHub Actions
jobs:
test:
steps:
- uses: extractions/setup-just@v2
- run: just cipre-commit hook:在 .git/hooks/pre-commit 里调用 just lint,统一本地和 CI 的代码检查标准:
#!/bin/sh
just lint编辑器集成:VS Code 的 Task Runner 可以直接列出 justfile 里的 recipe;Neovim 可以用 :terminal just test 直接运行。
AI Agent 友好:任务名称清晰、参数明确、输出稳定的 justfile,让 Agent 不需要猜命令——它可以直接 just test 验证修复是否生效,just lint 检查代码质量,just build 确认构建无误。这是在 AI 辅助开发工作流里 justfile 越来越重要的原因。
7. 设计原则
项目常用操作要有名字:lint、test、build、fmt、bootstrap、deploy 这些动词应该能被快速发现,不需要翻 README。
危险动作显式命名:删除数据、部署到生产、覆盖数据库这类操作,不要用无害的名字掩盖,必要时加确认步骤:
# 危险:部署到生产前要求确认
deploy-prod:
@echo "⚠️ 即将部署到生产环境,确认?(y/n)"
@read ans && [ "$$ans" = "y" ] || exit 1
just _do-deploy-prod
_do-deploy-prod:
rsync -avz ./dist/ prod-server:/var/www/README 和 justfile 分工:README 写背景、依赖和注意事项;justfile 固化稳定命令。不要在 justfile 里写大量注释解释”为什么”——那应该在 README 或 ADR 里。
CI 和本地共用:如果两者执行不同的命令,最终会出现”本地通过 CI 失败”的情况。共用 just ci 作为单一真相源。