用 just 管理项目命令:把常用脚本整理成可执行菜单
项目维护久了,经常会出现一个小问题:命令都在脑子里,或者散落在 README、shell 历史记录、CI 配置和同事的聊天记录里。
just 适合解决这个问题。它不是构建系统,而是一个项目命令运行器:你把常用命令写进 justfile,之后用 just test、just run、just deploy 这类固定入口执行。本文用一个最小 Python 项目演示一套可以直接复制的工作流。
完成后,你会得到:
- 一个基础可用的
justfile - 带参数的运行命令
- 一组检查、测试和清理命令
- 常见报错的直接修复方法
适合什么场景
just 特别适合这些项目:
- README 里有很多重复命令
- 本地开发、测试、格式检查、构建命令比较固定
- 团队成员经常问“这个项目怎么跑”
- 你不想为了简单命令引入复杂构建系统
- 你希望 CI 和本地尽量复用同一套命令
如果项目只有一个脚本,直接运行脚本就够了。如果项目已经有 npm scripts、Makefile 或 CI 配置,just 也可以作为更清晰的本地入口。
方法 1:安装并验证 just
macOS 可以用 Homebrew:
brew install just
已经有 Rust 工具链的机器,可以用 Cargo:
cargo install just
如果服务器上不想依赖包管理器,也可以把预编译版本安装到 ~/bin:
mkdir -p ~/bin
curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/bin
export PATH="$PATH:$HOME/bin"
验证安装:
just --version
just --help
能看到版本号和帮助信息,就可以开始写 justfile。
方法 2:创建一个可复用 justfile
先准备一个最小 Python 项目:
mkdir just-demo
cd just-demo
写入主程序:
cat > greet.py <<'EOF'
from __future__ import annotations
import sys
def greet(name: str) -> str:
return f"hello, {name}"
if __name__ == "__main__":
name = sys.argv[1] if len(sys.argv) > 1 else "world"
print(greet(name))
EOF
写入一个标准库测试:
cat > test_greet.py <<'EOF'
import unittest
from greet import greet
class GreetTest(unittest.TestCase):
def test_greet(self) -> None:
self.assertEqual(greet("bobobk"), "hello, bobobk")
if __name__ == "__main__":
unittest.main()
EOF
现在创建 justfile:
cat > justfile <<'EOF'
python := "python3"
default:
@just --list
run name="world":
{{python}} greet.py "{{name}}"
test:
{{python}} -m unittest -v
syntax:
{{python}} -m compileall -q .
check: syntax test
clean:
rm -rf __pycache__ .pytest_cache .ruff_cache
EOF
这个文件做了几件事:
default:直接运行just时列出所有命令run name="world":定义一个带默认值的参数test:运行标准库测试syntax:做一次 Python 语法检查check: syntax test:把多个步骤串起来clean:清理本地缓存目录
方法 3:运行和覆盖参数
先列出可用命令:
just
预期会看到类似输出:
Available recipes:
check
clean
default
run name="world"
syntax
test
运行默认参数:
just run
输出:
hello, world
传入参数:
just run bobobk
输出:
hello, bobobk
运行完整检查:
just check
输出会先执行语法检查,再执行测试。如果其中一步失败,后续命令不会继续把结果伪装成成功。
如果你想临时换 Python 命令,可以覆盖变量:
just python=python3.12 test
这比让每个人手动改 justfile 更适合临时调试。
方法 4:把 just 用在真实项目里
一个真实项目的 justfile 可以从下面这组命令开始:
python := "python3"
src := "app"
default:
@just --list
install:
uv sync
dev:
uv run fastapi dev {{src}}/main.py
fmt:
uvx ruff format .
lint:
uvx ruff check .
test:
uv run pytest -q
check: fmt lint test
build:
docker build -t my-app:local .
这类文件的价值不是命令更短,而是入口更稳定。新同事拉代码后,不需要猜 README 哪一段是最新的,先执行:
just
再根据列表执行:
just install
just dev
just check
CI 里也可以复用同一个入口:
- name: Check project
run: just check
这样本地和 CI 的命令就不容易分叉。
验证
用下面几条命令确认示例项目正常:
just --list
just run Alice
just test
just check
预期结果:
hello, Alice
测试部分应该出现类似:
test_greet (test_greet.GreetTest.test_greet) ... ok
如果这些命令都能运行,说明 just、justfile、参数传递和命令依赖都已经工作。
常见问题
1. 报错:No justfile found
原因是当前目录和上级目录里都没有 justfile。
先确认位置:
pwd
ls -la
如果文件不在当前项目里,切到项目根目录:
cd /path/to/project
just --list
2. 报错:Unknown recipe
说明命令名写错了,或者对应 recipe 没有定义。
先列出所有命令:
just --list
再复制列表里的 recipe 名称执行,避免凭记忆输入。
3. 变量在下一行丢失
just 的普通 recipe 每一行都会交给 shell 执行。不要写成这样:
bad:
TOKEN=demo
echo "$TOKEN"
更稳的做法是放在同一行:
good:
TOKEN=demo && echo "$TOKEN"
如果逻辑比较长,用 shebang recipe:
script:
#!/usr/bin/env bash
set -euo pipefail
TOKEN=demo
echo "$TOKEN"
4. 参数里有空格
参数替换时要加引号。比如:
run name:
python3 greet.py "{{name}}"
然后这样执行:
just run "Bob Lee"
否则 shell 可能会把一个参数拆成多个参数。
总结
just 最实用的价值,是把项目里的常用命令变成一个可发现的菜单。它不会替代所有构建工具,但很适合统一本地开发、测试、格式检查和清理入口。
建议从 5 个 recipe 开始:default、install、dev、test、check。等这些命令稳定后,再把部署、构建、数据导入、日志排查等操作逐步加进去。
- 原文作者:春江暮客
- 原文链接:https://www.bobobk.com/just-command-runner-workflow.html
- 版权声明:本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。