为什么我构建了另一个 Task Runner
Source: Dev.to
现有任务运行器的问题
是的,我知道——又一个 2026 年的任务运行器。让我解释一下原因。
我不是 Make 专家,但当有人需要向我们的 Makefile 添加任务时,我已经成了首选人选。典型问题包括:
- “我该如何向这个目标传递环境变量?”
- “为什么只有在我添加制表符后才会工作?”
- “我忘记把任务加入
.PHONY了。”
每次我都在谷歌搜索他们本可以自己搜索的相同问题。我们的构建过程包含如下片段:
deploy-%: guard-% check-git-clean
@$(eval ENV := $(word 2,$(subst -, ,$@)))
./scripts/deploy.sh $(ENV)
如果你在不查阅文档的情况下就能理解 $(word 2,$(subst -, ,$@)),恭喜你——你已经是那 1 % 记住 Make 那晦涩变量替换语法的开发者之一。
公司里的其他仓库使用 npm 脚本或通过 uv 运行的自定义 Python CLI 包。每个项目都有自己的做法,而 npm‑script 仓库同样会遇到跨平台问题:
{
"clean": "rm -rf dist || rmdir /s /q dist",
"build": "NODE_ENV=production webpack || set NODE_ENV=production && webpack"
}
丑陋。脆弱。而且仍然会在随机的边缘情况中出错。
我想要的
-
用我思考的方式编写任务:
# 只需要部署 deploy() { ./scripts/deploy.sh $1 } -
当需要真正的逻辑时使用 Python(而不是 Bash 那噩梦般的字符串操作)。
-
在处理 JSON 或异步操作时使用 Node。
-
实现跨平台支持,而不需要条件地狱。
-
提供一些 AI 代理真正能使用的东西(稍后会详细说明)。
介绍 run
run 让你可以在 Runfile 中将任务定义为简单函数。无需 YAML、TOML,也不需要 $(word …) 之类的技巧。
示例 Runfile
# 简单任务使用 Shell
build() cargo build --release
# 需要时使用 Python
analyze() {
#!/usr/bin/env python
import json, sys
with open(sys.argv[1]) as f:
data = json.load(f)
print(f"Processed {len(data)} records")
}
# Node 也可以
process() {
#!/usr/bin/env node
const fs = require('fs');
console.log('Processing...');
}
# 平台特定版本
# @os windows
deploy() {
.\scripts\deploy.ps1 $1
}
# @os linux darwin
deploy() {
./scripts/deploy.sh $1
}
无需额外的配置文件。只要函数即可。
Model Context Protocol (MCP) 支持
run 内置 MCP 服务器,使 AI 代理(例如 Claude Code)能够自动发现并执行你项目中的工具。
为任务添加元数据:
# @desc Deploy to specified environment
# @arg 1:environment string Target environment (staging|prod)
deploy() {
./scripts/deploy.sh $1
}
现在,兼容 MCP 的代理能够准确知道该工具的功能以及如何调用——无需猜测,也不需要冗长的 Markdown 说明。
其他优势
- 单一 Rust 可执行文件——无运行时,无包含数百个依赖的
package.json。 - 开箱即用的 Bash、Zsh、Fish 与 PowerShell 补全。
- 所有任务的 Tab 自动补全,无需额外配置。
入门指南
如果你对 Make、Just 或 npm 脚本已经满意,那没问题。但如果你曾想过“必须有更简单的办法”,不妨试试 run:
brew install nihilok/tap/runfile
或
cargo install run
源码托管在 GitHub。它解决了我的问题;也许也能解决你的问题。