Go Task 工具
Task 是用 Go 编写的任务运行器和构建工具,简化构建流程,提供比 GNU Make 更直观的替代方案。 使用 YAML 文件定义任务,支持跨平台操作
安装与验证 通过 go install 安装
go install github.com/go-task/task/v3/cmd/task@latest验证安装 task --version
Task 直观的语法和强大的功能 适合自动化构建和任务管理
示例:创建并运行任务
简单的 Taskfile.yml 示例,用于打印问候语
version: '3'
tasks:
hello:
desc: 打印问候语
cmds:
- echo "你好,Task!"运行以下命令执行 hello 任务:
task hello
输出:
你好,Task!
核心功能
任务依赖 定义任务依赖关系,确保先执行依赖任务。例如
tasks:
build:
desc: 构建项目
deps: [test]
cmds:
- go build -o bin/app
test:
desc: 运行测试
cmds:
- go test ./环境变量 支持为任务设置环境变量:
tasks:
greet:
desc: 打印问候语
env:
NAME: "开发者"
cmds:
- echo "你好,$NAME!"变量支持 使用变量动态生成内容:
tasks:
greet:
desc: 打印问候语
vars:
NAME: "测试者"
cmds:
- echo "你好,{{.NAME}}!"条件执行 在执行任务前检查条件是否满足:
tasks:
deploy:
desc: 部署应用
preconditions:
- test -f config.yaml
cmds:
- echo "开始部署..."在项目根目录创建 Taskfile.yml 文件
cmds 属性应当包含 task 要执行的命令
编译 Go 的应用和一个用 Minify 压缩合并 CSS 的应用
version: "3"
tasks:
build:
cmds:
- go build -v -i main.go
assets:
cmds:
- minify -o public/style.css src/css执行 task
task assets buildTask 采用了 mvdan.cc/sh Go 原生的 sh 编译器 //github.com/mvdan/sh
所以 sh/bash 命令甚至能在 windows 上通用
命令必须是操作系统支持的 如果不传 task 的名字,默认会调用 "default"
配置文件的命名
Task 会按顺序寻找配置文件
Taskfile.yml
Taskfile.yaml
Taskfile.dist.yml
Taskfile.dist.yaml
允许 .dist 扩展文件是为了可以在项目中提交一个(.dist)配置文件
同时允许 自定义的 Taskfile.yml 文件进行覆盖 (自定义文件应该在.gitignore中标记忽略)
环境变量
Task
env 给每个 task 设置自定义环境变量
version: "3"
tasks:
greet:
cmds:
- echo $GREETING
env:
GREETING: Hey, there!设置全局环境变量 给所有 task 使用
version: "3"
env:
GREETING: Hey, there!
tasks:
greet:
cmds:
- echo $GREETINGenv 用 shell 命令的结果,就像变量那样
.env 文件
通过 dotenv: 让 Task 引用类似 .env 的文件
title=".env"
KEYNAME=VALUEtitle="testing/.env"
ENDPOINT=testing.comtitle="Taskfile.yml"
version: "3"
env:
ENV: testing
dotenv: [".env", "{{.ENV}}/.env.", "{{.HOME}}/.env"]
tasks:
greet:
cmds:
- echo "Using $KEYNAME and endpoint $ENDPOINT"引用其它 Taskfile 文件
想要在不同项目复用 (Taskfiles),引用机制, 设置 includes 属性
version: "3"
includes:
docs: ./documentation # will look for ./documentation/Taskfile.yml
docker: ./DockerTasks.yml调用引用文件的 task 需要添加前缀使用
如用 task docs:serve 来调用 documentation/Taskfile.yml 文件中的 serve 任务
或使用 task docker:build 来调用 DockerTasks.yml 文件中的 build 任务
相对路径的解析是相对 Taskfile 所在的目录
依赖操作系统的 Taskfile
在 version: '2' 中
task 会自动尝试引入 Taskfile_{{OS}}.yml 文件
如Taskfile_windows.yml, Taskfile_linux.yml 或 Taskfile_darwin.yml
但是因为过于隐晦,在版本 3 中被移除了
在版本 3 可以通过明确的引用来实现类似功能
version: "3"
includes:
build: ./Taskfile_{{OS}}.yml被引用 Taskfile 的目录
默认引用文件中的 task 也是在当前路径执行的, 尽管被引用文件可能在另一个目录下, 但是可以强制指定引用文件的执行目录
version: "3"
includes:
docs:
taskfile: ./docs/Taskfile.yml
dir: ./docs被引用文件必须使用与主 Taskfile 的版本相同
可选引用
引用被标记为可选以后,如果文件不存在,Task 会继续执行其它命令
version: "3"
includes:
tests:
taskfile: ./tests/Taskfile.yml
optional: true
tasks:
greet:
cmds:
- echo "This command can still be successfully executed if ./tests/Taskfile.yml does not exist"内部引用
引用被标记为内部以后,引用文件中的 task 也都被标记为内部 task (查看 内部任务 内容)。 它的用途是引用工具类型的 task,同时避免任务被用户直接调用
version: "3"
includes:
tests:
taskfile: ./taskfiles/Utils.yml
internal: true引入文件中的变量
引用 Taskfile 文件时可以设置变量
在复用 Taskfile 时进行设置,即使多次引用相同文件
version: "3"
includes:
backend:
taskfile: ./taskfiles/Docker.yml
vars:
DOCKER_IMAGE: backend_image
frontend:
taskfile: ./taskfiles/Docker.yml
vars:
DOCKER_IMAGE: frontend_image命名空间的别名
引用 Taskfile 后 给命名空间设置 aliases 列表 工作方式与 Task 别名 相同, 共同使用 可创造简短易用的命令
version: "3"
includes:
generate:
taskfile: ./taskfiles/Generate.yml
aliases: [gen]引入文件中的变量优先级高于主文件!
如果想要覆盖引入文件中的变量, 用 default function
MY_VAR: '{{.MY_VAR | default "my-default-value"}}'
内部任务
内部任务 不能直接调用的任务 不会出现在 task --list|--list-all 的列表中
其它任务可以正常调用内部任务 有助于创建可复用、类函数、不会直接执行的任务
version: "3"
tasks:
build-image-1:
cmds:
- task: build-image
vars:
DOCKER_IMAGE: image-1
build-image:
internal: true
cmds:
- docker build -t {{.DOCKER_IMAGE}} .任务目录
默认情况下,task 会在 Taskfile 所在目录执行任务
需要变更执行目录时,可以设置 dir:
version: "3"
tasks:
serve:
dir: public/www
cmds:
# run http server
- caddy如果目录不存在,task 会自动创建
任务依赖
依赖是并发执行的,所以依赖中的任务无法互相依赖。 如果要按顺序执行,查看 调用其它任务
有些 task 可能依赖其它 task
只要在 deps 中标记, 就可以自动提前执行这些依赖:
version: "3"
tasks:
build:
deps: [assets]
cmds:
- go build -v -i main.go
assets:
cmds:
- minify -o public/style.css src/css如果运行 task build,那么 assets 总是会在 build 前执行
task 可以只包含依赖,不包含命令,这样可以对任务进行聚合:
version: "3"
tasks:
assets:
deps: [js, css]
js:
cmds:
- minify -o public/script.js src/js
css:
cmds:
- minify -o public/style.css src/css有多个依赖时,会并发执行来提高效率。
还可以通过命令行参数 --parallel (简写 -p) 来并行执行任务。 例如: task --parallel js css
如果想要给依赖任务传递参数,可以采取 调用其它任务 中的方法:
version: "3"
tasks:
default:
deps:
- task: echo_sth
vars: { TEXT: "before 1" }
- task: echo_sth
vars: { TEXT: "before 2" }
cmds:
- echo "after"
echo_sth:
cmds:
- echo {{.TEXT}}调用其它任务
存在多个依赖时,这些依赖任务会并发执行。这样任务执行的会更快
但有时 需要按顺序执行 task
这种情况下可以使用下面的语法
version: "3"
tasks:
main-task:
cmds:
- task: task-to-be-called
- task: another-task
- echo "Both done"
task-to-be-called:
cmds:
- echo "Task to be called"
another-task:
cmds:
- echo "Another task"覆盖调用 task 中的变量 只需要设置 vars 属性
version: "3"
tasks:
greet:
vars:
RECIPIENT: '{{default "World" .RECIPIENT}}'
cmds:
- echo "Hello, {{.RECIPIENT}}!"
greet-pessimistically:
cmds:
- task: greet
vars: { RECIPIENT: "Cruel World" }上面的语法也可以用在 deps 中
NOTE 注意:如果在引用文件中调用主文件中的 task 需要添加
:前缀 例如task: :task-name
减少非必要工作
文件指纹
如果 task 有产出物,那么可以标记源文件和生成文件关系, 这样 Task 就可以减少非必要的任务
version: "3"
tasks:
build:
deps: [js, css]
cmds:
- go build -v -i main.go
js:
cmds:
- minify -o public/script.js src/js
sources:
- src/js/**/*.js
generates:
- public/script.js
css:
cmds:
- minify -o public/style.css src/css
sources:
- src/css/**/*.css
generates:
- public/style.csssources 和 generates 可以配置具体文件 可以以使用匹配模式
设置后, Task 会根据源文件的 checksum 来确定是否需要执行当前任务
如果不需要执行, 则会输出像 Task "js" is up to date 这样的信息
对比文件的方法如果想用时间戳而不是 checksum, 只需要把 method 属性设置为 timestamp
version: "3"
tasks:
build:
cmds:
- go build .
sources:
- ./*.go
generates:
- app{{exeExt}}
method: timestamp更复杂的情况可以使用 status 关键字
甚至可以多个进行组合使用。在文档 程序判断 task 是否已更新 中查看例子
默认 task 在本地项目的 .task 目录保存 checksums 值
一般都会在 .gitignore(或类似配置)中忽略掉这个目录
(如果 task 中有生成代码的功能,那么提交这个目录似乎也有些道理)
如果想要在其它目录保存这些文件,可以配置环境变量 TASK_TEMP_DIR
可以使用相对路径,比如 tmp/task 相对项目根目录 也可以用绝对路径 用户目录路径,比如 /tmp/.task 或 ~/.task(每个项目单独创建子目录)
export TASK_TEMP_DIR='~/.task'每个 task 只有一个 sources 的 checksum
如果需要根据输入变量来区分 task, 就需要把变量作为 task 的一个标签,这样就会被当做一个不同的 task
这种方式用于在不同入参下触发 task,直到 resource 发生的变化
如想让 resource 依赖某个变量,或虽然 resource 没变, 但参数变化时依然重新执行 task
配置为 none 会跳过校验,每次都会执行 task
想要 checksum(默认)方法生效,只需要配置 source 文件, 但如果想用 timestamp 方法,还需要通过 generates 来标记生成的文件
程序判断 task 是否已更新
还有一种方法,你可以使用一系列 test 作为 status
如果正常返回(退出信号是 0), 那么任务就被当做已更新
version: "3"
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
status:
- test -d directory
- test -f directory/file1.txt
- test -f directory/file2.txt一般情况,需要 generates 和 sources 一起配合使用
但如果 存在远程生成物(Docker 镜像,部署,持续发布),checksum 和 timestamp 就需要有处理远程文件的权限,或能够刷新 .checksum 指纹文件的外挂
这两个特殊变量 {{.CHECKSUM}} 和 {{.TIMESTAMP}} 可以作为 status 的 插值,具体取决于生成 source 指纹的方法
只有 source 块才能生成指纹
注意, {{.TIMESTAMP}} 变量在 Go 语音 time.Time 结构体, 可以被任意 time.Time 的格式化方法
查看 the Go Time documentation 了解更多信息
可以使用 --force 或 -f 来强制 task 执行
task --status [tasks]... 会返回非 0 状态码,只要有任何 task 没有更新到最新
status 可以与 文件指纹 配合使用, 来判断一个 task 是否根据 source/generated 变化或程序检查失败的条件来执行
version: "3"
tasks:
build:prod:
desc: Build for production usage.
cmds:
- composer install
# Run this task if source files changes.
sources:
- composer.json
- composer.lock
generates:
- ./vendor/composer/installed.json
- ./vendor/autoload.php
# But also run the task if the last build was not a production build.
status:
- grep -q '"dev": false' ./vendor/composer/installed.json程序检查任务执行条件
相比 status 检查,preconditions 是逻辑上相反的检查
如果你需要前提条件为 true ,可以使用 preconditions 字段preconditions 与 status 用法类似, 不仅都支持 sh 表达式,并且表达式返回值都应该是 0
version: "3"
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
preconditions:
- test -f .env
- sh: "[ 1 = 0 ]"
msg: "One doesn't equal Zero, Halting"前置条件可以设置错误信息,提示用户下一步操作,只要配置 msg 字段
如果某个 task 依赖了带有前置条件的子 task,并且前置条件并未满足,则 task 会失败
注意,前置条件失败的 task 不会执行,除非使用了 --force 标记
与 status 判断 task 是最新状态时会跳过并继续执行不同, precondition 失败会导致 task 失败,以及所有依赖它的 task 都会失败
version: "3"
tasks:
task-will-fail:
preconditions:
- sh: "exit 1"
task-will-also-fail:
deps:
- task-will-fail
task-will-still-fail:
cmds:
- task: task-will-fail
- echo "I will not run"限制 task 执行次数
如果 task 被多个 cmds 调用,或被多个 deps 依赖, 那么可以通过 run 来控制执行过程run 也可以设置为 Taskfile 的根属性,设置全局默认行为
run 支持参数:
always(默认) 每次调用都会执行,不论之前是否被调用过once只掉一次,不论之后是否再次被调用when_changed每组变量仅调用一次
version: "3"
tasks:
default:
cmds:
- task: generate-file
vars: { CONTENT: "1" }
- task: generate-file
vars: { CONTENT: "2" }
- task: generate-file
vars: { CONTENT: "2" }
generate-file:
run: when_changed
deps:
- install-deps
cmds:
- echo {{.CONTENT}}
install-deps:
run: once
cmds:
- sleep 5 # long operation like installing packages变量
使用变量进行插值时,Task 会按照以下优先级进行查找(高优先级在前)
task 内部定义的变量
被其它 task 调用时传入的变量(查看 调用其它任务)
引用 Taskfile中的变量 (当引入 task 时)
主文件中的变量中的变量 (当引入 task 时)
全局变量 (在 Taskfile 的
vars:中声明)系统环境变量
利用环境变量传递参数的例子:
$ TASK_VARIABLE=a-value task do-something变量 .TASK 始终代表 task 的名字
因为有些 shell 不支持上面的语法(Windows)task 还支持环境变量不在开头的命令
$ task write-file FILE=file.txt "CONTENT=Hello, World!" print "MESSAGE=All done!"声明内部变量例子:
version: "3"
tasks:
print-var:
cmds:
- echo "{{.VAR}}"
vars:
VAR: Hello! Taskfile.yml 中声明全局变量例子
version: "3"
vars:
GREETING: Hello from Taskfile!
tasks:
greet:
cmds:
- echo "{{.GREETING}}"动态变量
下面语法 (使用 sh: 属性) 声明动态变量
命令输出结果会赋给变量。如果输出结果是多行,最后的空行会被忽略掉
version: "3"
tasks:
build:
cmds:
- go build -ldflags="-X main.Version={{.GIT_COMMIT}}" main.go
vars:
GIT_COMMIT:
sh: git log -n 1 --format=%h这种方式适用于所有类型的变量
透传命令行参数
如果命令行中使用了 -- 后面的参数会被赋给内置变量 .CLI_ARGS
可用于透传参数给另一个命令
下面是运行 yarn install 的例子
$ task yarn -- installversion: "3"
tasks:
yarn:
cmds:
- yarn {{.CLI_ARGS}}使用 defer 做任务清理
defer 关键字可以在 task 结束时进行清理
与放在最后一行的命令之间的差别是,即使 task 失败了,它也会执行
下面例子中,即使第三行的命令执行失败,rm -rf tmpdir/ 依然会执行
version: "3"
tasks:
default:
cmds:
- mkdir -p tmpdir/
- defer: rm -rf tmpdir/
- echo 'Do work on tmpdir/'使用其它 task 作为清理任务的命令时,可以这样:
version: "3"
tasks:
default:
cmds:
- mkdir -p tmpdir/
- defer: { task: cleanup }
- echo 'Do work on tmpdir/'
cleanup: rm -rf tmpdir/根据 Go's 的 defer 工作方式, 被推迟的任务是按相反的顺序执行的,如果你定义了多个清理任务
Go 的模板引擎
Task 在执行命令前会先用 Go 模板引擎 进行解析
变量也支持点语法 (.VARNAME)
所有 slim-sprig lib 中的方法都支持 https://sprig.taskfile.dev/
指定格式显示时间的例子
version: "3"
tasks:
print-date:
cmds:
- echo {{now | date "2006-01-02"}}Task 还支持下面这些方法:
OS: 当前操作系统,返回值可能是: "windows", "linux", "darwin" (macOS) 和 "freebsd"ARCH: 返回 Task 的编译架构:"386", "amd64", "arm" 或 "s390x"splitLines: 拆分 Unix (\n) 和 Windows (\r\n) 风格的换行catLines: 将 Unix (\n) 和 Windows (\r\n) 的换行替换成空格toSlash: 不操作 Unix, 但在 Windows 上将路径符\转换为/fromSlash: 与toSlash相反,不操作 Unix, 但在 Windows 上将路径符/转换为\exeExt: 返回当前操作系统执行文件结尾类型 (Windows 是".exe", 其它的是"")shellQuote: 给字符串加引号,以完成 shell 脚本运行, Task 用 Go 方法 进行转义。采用 Bash 语法。
例
version: "3"
tasks:
print-os:
cmds:
- echo '{{OS}} {{ARCH}}'
- echo '{{if eq OS "windows"}}windows-command{{else}}unix-command{{end}}'
# This will be path/to/file on Unix but path\to\file on Windows
- echo '{{fromSlash "path/to/file"}}'
enumerated-file:
vars:
CONTENT: |
foo
bar
cmds:
- |
cat << EOF > output.txt
{{range $i, $line := .CONTENT | splitLines -}}
{{printf "%3d" $i}}: {{$line}}
{{end}}EOF帮助
task --list (或 task -l) 列出全部任务和描述
下面的 Taskfile
version: "3"
tasks:
build:
desc: Build the go binary.
cmds:
- go build -v -i main.go
test:
desc: Run all the go tests.
cmds:
- go test -race ./...
js:
cmds:
- minify -o public/script.js src/js
css:
cmds:
- minify -o public/style.css src/css输出结果:
* build: Build the go binary.
* test: Run all the go tests.查看全部 task 用 --list-all (简写为 -a)
显示 task 总览
执行 task --summary task-name 显示 task 的总览
下面的 Taskfile
version: "3"
tasks:
release:
deps: [build]
summary: |
Release your project to github
It will build your project before starting the release.
Please make sure that you have set GITHUB_TOKEN before starting.
cmds:
- your-release-tool
build:
cmds:
- your-build-tool执行 task --summary release 会输出:
task: release
Release your project to github
It will build your project before starting the release.
Please make sure that you have set GITHUB_TOKEN before starting.
dependencies:
- build
commands:
- your-release-tool如果没有总览,则会使用描述代替
如果总览和描述都没有,会显示一个警告。
注意:显示总览并不会执行命令
Task 别名
别名是 task 的另一个名字,方便引用那些又长又难写的名字
命令行使用,当 调用其它任务 或 引用其它 Taskfile 文件 中有别名时,可以与 命名空间 一同使用
version: "3"
tasks:
generate:
aliases: [gen]
cmds:
- task: gen-mocks
generate-mocks:
aliases: [gen-mocks]
cmds:
- echo "generating..."覆盖 task 名字
有时想要在概述、更新等信息中覆写 task 名字
可以设置一个label:,它可以使用变量
version: "3"
tasks:
default:
- task: print
vars:
MESSAGE: hello
- task: print
vars:
MESSAGE: world
print:
label: "print-{{.MESSAGE}}"
cmds:
- echo "{{.MESSAGE}}"安静模式
安静模式下 Task 执行前不会再打印执行命令
如 Taskfile
version: "3"
tasks:
echo:
cmds:
- echo "Print something"正常会输出:
echo "Print something"
Print something安静模式下,则会输出:
Print something有 4 种方法开启安静模式
命令级别:
version: "3"
tasks:
echo:
cmds:
- cmd: echo "Print something"
silent: truetask 级别:
version: "3"
tasks:
echo:
cmds:
- echo "Print something"
silent: true在 Taskfile 全局:
version: "3"
silent: true
tasks:
echo:
cmds:
- echo "Print something"全局执行参数
--silent或-s
如果只想代替标准输出 可以将命令输出重定向到 /dev/null:
version: "3"
tasks:
echo:
cmds:
- echo "This will print nothing" > /dev/nullDry run 模式
Dry run 模式 (--dry) 会逐条编译、输出每个 task 的命令, 但不会真的执行,用于调试 Taskfile
忽略错误
可以配置选项忽略命令执行过程中的错误。 参考下面 Taskfile
version: "3"
tasks:
echo:
cmds:
- exit 1
- echo "Hello World"Task 执行 exit 1 后会终止,因为状态码 1 表示 EXIT_FAILURE。 但是配置 ignore_error 可以忽略错误继续执行:
version: "3"
tasks:
echo:
cmds:
- cmd: exit 1
ignore_error: true
- echo "Hello World"ignore_error 也可以在 task 中设置,表示忽略所有命令的错误
注意 这个配置不会影响 deps 或 cmds 中的 task
输出模式
默认 Task 只会将执行命令的 STDOUT 和 STDERR 实时重定向到终端。 这有助于实时反馈和记录命令执行,但是有多个命令同时打印大量日志时就变得很凌乱。
为了方便定制,有三种输出模式可选:
interleaved(默认)groupprefixed
选择不同模式,只需要在 Taskfile 根属性上进行设置:
version: "3"
output: "group"
tasks:
# ...group 模式会在命令结束后输出全部日志,这种模式下反馈会有延迟时间。
使用group输出,可以提供一个可选消息模板,在消息分组的开始和结尾显示。 可用于 CI 系统对一个任务的输出进行分组, 比如与 GitHub Actions' ::group:: command 或 Azure Pipelines 一同使用。
version: "3"
output:
group:
begin: "::group::{{.TASK}}"
end: "::endgroup::"
tasks:
default:
cmds:
- echo 'Hello, World!'
silent: true$ task default
::group::default
Hello, World!
::endgroup::prefix 选项会给每一行输出添加一个 [task-name] 前缀, 也可以通过 prefix: 自定义前缀:
version: "3"
output: prefixed
tasks:
default:
deps:
- task: print
vars: { TEXT: foo }
- task: print
vars: { TEXT: bar }
- task: print
vars: { TEXT: baz }
print:
cmds:
- echo "{{.TEXT}}"
prefix: "print-{{.TEXT}}"
silent: true$ task default
[print-foo] foo
[print-bar] bar
[print-baz] bazoutput 选项也可以通过命令行参数--output 或 -o 指定
交互式命令行应用
Task 执行包含交互式的命令时会出现奇怪的结果, 尤其当 输出模式 设置的不是 interleaved (默认), 或者当交互式应用与其它 task 并发执行时
设置 interactive: true 让 Task 知道这是个交互式应用,并自动优化:
version: "3"
tasks:
default:
cmds:
- vim my-file.txt
interactive: true如果在运行交互式应用时遇到其它问题,请提交 issue。
短 task 语法
从 v3 版本开始,如果有默认值就可以使用断语法 (比如,env:, vars:, desc:, silent: 等等)
version: "3"
tasks:
build: go build -v -o ./app{{exeExt}} .
run:
- task: build
- ./app{{exeExt}} -h localhost -p 8080文件监控
使用 --watch 或 -w 参数可以监控文件变化,然后重新执行 task
这需要配置 sources 属性,task 才知道监控哪些文件
默认监控的时间间隔是 5 秒,即可以通过 Taskfile 中 根属性 interval: '500ms' 设置
也可以通过命令行 参数 --interval=500ms 设置
上一篇: Go 语言开发工具
