4 个稳定版本
1.3.0 | 2023年6月14日 |
---|---|
1.2.0 | 2023年6月5日 |
1.1.0 | 2023年6月1日 |
1.0.0 | 2023年5月23日 |
#261 in 构建工具
165KB
3.5K SLoC
mom-task
适用于团队和个人的任务运行器。使用 Rust 编写。
索引
灵感来源
灵感来自不同的工具,如 cargo-make、go-task、doskey、bash 和 docker-compose。
此项目是我之前的项目 yamis 的分支,该项目最初是一个简单的用于工作和个人项目的任务运行器,也是学习 Rust 的方式。我决定将其分支出来,以去除一些不必要的复杂性并改进 YAML 文件结构,即更接近现有工具。这也使用了更熟悉的二进制名称,寓意双关。
安装
Homebrew
如果您已安装 homebrew,可以使用以下命令安装 mom
$ brew tap adrianmrit/mom
$ brew install mom
使用 cargo
如果您已安装 cargo,可以使用以下命令安装 mom
$ cargo install mom-task
提示:请确保 ~/.cargo/bin
目录已添加到您的 PATH
环境变量中。
二进制发布
Windows、Linux和macOS平台下也有可用的二进制文件,请访问发布页面。安装方法:下载适合您系统的zip文件,解压,然后将二进制文件复制到所需位置。请确保包含二进制文件的文件夹已添加到PATH
环境变量中。
JSON 架构
任务文件的JSON模式可在此处找到:链接。
您可以将此模式配置到您的IDE中,以便自动完成和验证。
VSCode
要将模式与VSCode集成,您可以使用YAML扩展。安装后,您可以将以下内容添加到您的settings.json
文件中
"yaml.schemas": {
"https://raw.githubusercontent.com/adrianmrit/mom/main/json-schema/mom.json": [
"mom.*.yml",
"mom.*.yaml",
"mom.yml",
"mom.yaml",
]
}
您还可以通过在文件顶部添加以下内容将模式添加到特定文件中
# yaml-language-server: $schema=https://raw.githubusercontent.com/adrianmrit/mom/main/json-schema/mom.json
version: 1
快速开始
在项目的根目录下创建一个名为mom.root.yml
的文件。
以下是一个非常基本的任务文件示例
# mom.root.yml
version: 1
vars:
greeting: Hello World
tasks:
hi:
cmds:
- echo {{ vars.greeting }}
hi.windows:
cmds:
- echo {{ vars.greeting }} from Windows
sum:
cmds:
- echo "{{ args.0 }} + {{ args.1 }} = {{ args.0 | int + args.1 | int }}"
swear:
condition: |
{{ input(label="Are you sure you want to say that? (yes/no)") | lower == 'yes' }}
cmds:
- echo "!@#$%^&*()"
拥有mom文件后,您可以通过调用mom
(任务名称)、任何参数来运行任务,例如:mom hi
。参数可以直接在任务名称后传递,无论是按名称还是按位置,例如:mom sum 1 2
。
用法
命令行选项
如果您想将命令行选项传递给mom
本身,则必须在任务名称之前传递,任何任务名称之后的参数都将被视为任务的参数。例如,如果您想运行全局任务,您需要在任务名称之前传递-g
或--global
标志,例如:mom -g say_hi
,而不是mom say_hi -g
。
某些命令行选项可以组合使用,例如:mom -gt
(或mom -g -t
)将列出全局任务,而mom -gl
将给出全局任务文件的路径。
如果您想使用非标准任务文件,可以使用-f
或--file
选项,例如:mom -f my_tasks.yml say_hi
。
要在干运行模式下运行任务,即不执行任何命令,可以使用--dry
标志,例如:mom --dry say_hi
。
运行mom -h
或mom --help
可以查看一些额外的命令行选项。
任务文件
任务使用YAML格式定义。
在调用任务时,程序将从工作目录开始,一直继续到根目录,按照一定顺序查找配置文件,直到找到任务、找到mom.root.{yml,yaml}
文件或没有更多的父文件夹(到达根目录)。在区分大小写的系统中,这些文件的名称是大小写敏感的,例如,在Linux中mom.root.yml
将无法工作。
优先顺序如下
mom.private.yml
:应包含私有任务,且不应提交到仓库中。mom.private.yaml
:与上面相同,但为yaml格式。mom.yml
:应在项目的子文件夹中使用,用于特定文件夹和子文件夹的任务。mom.yaml
:与上面相同,但为yaml格式。mom.root.yml
:应包含整个项目的任务。mom.root.yaml
:与上面相同,但为yaml格式。
可以在~/mom/mom.global.yml
或~/mom/mom.global.yaml
定义一个特殊任务文件,用于全局任务。要运行全局任务,需要传递--global
或-g
标志,例如mom -g say_hi
。这对于与特定项目无关的个人任务很有用。
还可以通过传递--file
或-f
标志在不同的文件中定义任务,例如mom -f my_tasks.yml say_hi
。
虽然可以添加两种格式中的任何一种,即mom.root.yml
和mom.root.yaml
,但建议只使用一种格式,以确保一致性和避免混淆。
常用属性
可以在任务文件或任务本身中定义以下属性。任务的定义值优先于文件中的定义值。
wd
wd
属性用于定义工作目录。默认为当前工作目录。可以在文件或任务中定义,任务的定义值优先于文件中的定义值。
路径可以是绝对路径或相对于文件位置的相对路径。要将工作目录设置为文件位置,使用wd: "."
。或者,要将它设置为命令执行的目录,使用wd: ""
。请注意,虽然在任务中可以设置wd: null
,但它将被视为未定义wd
,因此从父级继承。
env
env
属性用于定义所有任务都可以访问的环境变量。属性的值是键值对的映射,其中键是环境变量的名称,值是环境变量的值。
执行的任务中定义的值优先于文件中定义的值。
通常情况下,可以通过三种方式访问环境变量,即使用tera标签,例如:{{ env.VAR }}
,只要它们被支持,使用tera函数get_env,例如:{{ get_env(name="VAR", default="default") }}
,或者通过shell展开,例如:$VAR
或${VAR}
。但是请注意,在tera标签内部不支持shell展开。
注意,当使用tera标签访问环境变量时(即{{ env.VAR }}
),系统环境变量不可用,只有文件或任务中定义的变量可用。要访问系统环境变量,请使用get_env函数或使用shell展开。
参见
dotenv
dotenv
属性用于定义文件中所有任务可用的环境变量。该属性的值是一个字符串,或包含包含环境变量的文件路径的字符串列表。路径可以是绝对路径,也可以是相对于文件位置的相对路径。
env
属性中定义的值优先于使用dotenv
属性定义的值。
vars
vars
属性用于定义文件中所有任务可用的变量。这与env属性的行为类似,但变量不会被导出到环境变量中,并且可以比字符串更复杂。
例如,您可以定义一个变量如下
vars:
user:
age: 20
name: John
然后像这样在任务中使用它
tasks:
say_hi:
cmd: echo "Hi, {{ vars.user.name }}!"
incl
incl
属性用于定义文件中所有任务可用的Tera包括/模板。该属性的值是键值对映射,其中键是模板的名称,值是模板本身。然后可以在任务中使用具有名称incl.<name>
的模板。
模板可以包含其他模板,但它们的定义顺序很重要。
例如,您可以定义一个模板如下
incl:
say_hi: "Hi from {{ TASK.name }}!"
say_bye: "Bye from {% include "incl.say_hi" %}!"
但是以下将不会工作
incl:
say_bye: "Bye from {% include "incl.say_hi" %}!"
say_hi: "Hi from {{ TASK.name }}!"
模板也可以在任务中定义,并且它们将优先于文件中定义的模板。
任务文件属性
除了通用属性外,任务文件中还可以定义以下属性
tasks
使用tasks
属性来定义文件中的任务。属性的值是键值对映射,其中键是任务的名称,值是任务定义。
任务名称必须以ASCII字母字符或下划线开头,后跟任意数量的字母、数字、-
或_
。名称也可以以.windows
、.linux
或.macos
结尾,以定义一个操作系统特定的任务。您可以按照惯例,使用前导下划线命名私有任务,例如_private_task
。
文件扩展
在文件中,使用extend
属性来定义要继承的mom文件。它可能是一个相对于文件位置的路径或路径列表,或是一个绝对路径。例如
version: 1
# Extend from a file in the same directory
extend: mom.base.yml
# Extend from a file in a subdirectory
extend: base/mom.base.yml
# Extend from multiple files
extend:
- mom.base.yml
- base/mom.base.yml
继承的值是
合并的值(文件值优先)是
dotenv在从文件继承或合并到父文件之前加载并与同一文件中的env合并。这意味着它被视为env的一部分。
任务属性
除了通用属性外,任务还可以具有以下属性
- help:帮助消息。
- script:要执行的脚本。
- script_runner:用于解析脚本程序和参数的模板。
- script_extension:脚本文件的扩展名。
- script_ext:
script_extension
的别名。 - cmds:要执行的命令。
- program:要执行的程序。
- args:传递给程序的参数。
- args_extend:传递给程序的参数,如果存在基本任务,则追加到这些参数。
- args+:
args_extend
的别名。 - linux:在linux中执行的任务版本。
- windows:在windows中执行的任务版本。
- mac:在mac中执行的任务版本。
- private:任务是否为私有。
- 继承: 要继承的任务。
help
help
属性用于定义任务的帮助信息。属性的值是一个包含帮助信息的字符串。
与注释不同,当运行 mom -i <TASK>
时,会打印帮助信息。
condition
condition
属性用于定义执行任务的条件。属性的值是一个包含 Tera 模板的字符串。如果模板计算结果为 true
,则执行任务,否则跳过。
只有 true
(不区分大小写)值被视为真,所有其他值被视为假。这是因为 Tera 条件将返回 true
或 false
。
示例
tasks:
say_hi:
condition: "{{ env.ENVIRONMENT == 'production' }}" # will evaluate to true if ENVIRONMENT is production, false otherwise
script: echo "Hi!"
这也可以根据某些条件选择不同的任务,例如。
tasks:
greet:
cmds:
- task:
condition: "{{ env.ENVIRONMENT == 'production' }}"
script: echo "Hi!"
- task:
condition: "{{ env.ENVIRONMENT != 'production' }}"
script: echo "Bye!"
脚本
⚠️警告:不要在脚本中将敏感信息作为参数传递。脚本存储在系统的临时目录中的文件中,由操作系统负责删除,但无法保证何时以及是否删除。因此,任何传递的敏感参数都可能无限期地保留。
任务内的 script
值将在命令行中执行(在 Windows 上默认为 cmd,在 Unix 上默认为 bash)。脚本可以包含多行,并包含 shell 内置程序和程序。
生成的脚本存储在临时目录中,文件名将是哈希值,这样如果脚本以前曾使用相同的参数调用,我们可以重用以前的文件,本质上作为缓存。
script_runner
script_runner
属性用于定义解析脚本程序和参数的模板。必须包含一个程序和一个 {{ script_path }}
模板,例如 python {{ script_path }}
。参数的分离方式与 args 相同。此参数支持展开环境变量,如 `$A
script_extension
script_extension
属性用于定义脚本文件的扩展名。例如,对于 Python 脚本,可以是 py
或 .py
。
程序
任务内的 program
值将以单独的进程执行,如果传递了 args
,则将传递这些参数。
参数
任务内的 args
值将作为参数传递给程序,如果有的话。值是一个包含空格分隔的参数的字符串。包含空格的值可以用引号引起来作为单个参数处理,例如 "hello world"
。引号可以用反斜杠转义,例如 \"
。
参数扩展
args_extend
的值将追加到 args
(之间有空格),如果有的话。值与 args
的形式相同。
命令
cmds
的值是一个要执行的命令列表。每个命令可以是字符串,或者包含一个task
键的映射。
如果命令是字符串,它将以程序的方式执行,第一个值是程序,其余的是参数。参数的分隔方式与args相同。为了方便,echo
是mom中的一个内置命令,因此相同的命令可以在Windows和Unix中正常工作。
如果命令是映射,task
的值可以是要执行的任务的名称,或者要执行的任务的定义。
示例
tasks:
say_hi:
script: echo "hi"
say_bye:
cmds:
- echo "bye"
greet:
cmds:
- python -c "print('hello')"
- task: say_hi
- task:
extend: say_bye
私有
private
的值是一个布尔值,表示任务是否为私有。私有任务不能直接执行,但可以从其他任务继承。
任务扩展
在任务中,extend
属性用于定义要继承的任务。它可能是一个字符串或字符串列表。例如
tasks:
base_task:
program: echo
args: "Hello, world!"
task:
extend: base_task
other_task:
extend:
- base_task
- task
任务会合并,父任务优先于基础任务。
继承的值是
- wd
- help
- condition
- script
- script_runner
- script_extension
- script_ext(
script_extension
的别名) - program
- args
- cmds
合并的值(父值优先)包括
就像在文件中一样,在从任务扩展或合并到父任务之前,dotenv被加载并与同一任务中的env合并。这意味着它被视为任务中env的一部分。
没有继承的值是
特定于操作系统的任务
可以为每个任务设置不同的OS版本。如果没有找到当前OS的任务,如果存在非OS特定的任务,它将回退到该任务。即
tasks:
ls:
script: "ls {{ args.0 }}"
ls.windows:
script: "dir {{ args.0 }}"
可以在单个键中指定OS特定的任务,即以下示例与上面的示例等效。
tasks:
ls:
script: "ls {{ args.0 }}"
ls.windows:
script: "dir {{ args.0 }}"
请注意,OS特定的任务不会隐式地从非OS特定的任务继承,如果您想这样做,您必须显式地定义extend,即
tasks:
ls:
env:
DIR: "."
script: "ls {{ env.DIR }}"
ls.windows:
extend: ls
script: "dir {{ env.DIR }}"
传递参数
任务参数可以是键值对传递,例如--name "John Doe"
,或者作为位置参数传递,例如"John Doe"
。
命名参数必须以一个或两个连字符开头,后跟一个ASCII字母或下划线,后跟任意数量的字母、数字、-
或_
。值将是下一个参数或等于号后面的值,即--name "John Doe"
,--name-person1="John Doe"
,-name_person1 John
都是有效的。请注意,"--name John"
不是命名参数,因为它被引号包围且包含空格,然而"--name=John"
是一个有效的命名参数。
导出的变量是
args
:传递给任务的参数。如果任务使用mom say_hi arg1 --name "John"
调用,那么args
将变为["arg1", "--name", "John"]
。kwargs
:传递给任务的键值参数。如果任务使用mom say_hi --name "John"
调用,那么kwargs
将变为{"name": "John"}
。如果相同的参数名多次传递,则取最后一个值。pkwargs
:与kwargs
相同,但值为相同参数名的所有传递值的列表。env
:任务中定义的环境变量。注意,这不包括系统中定义的环境变量。要访问这些变量,请使用{{ get_env(name=<value>, default=<default>) }}
。vars
:任务中定义的变量。TASK
:任务对象及其属性。FILE
:文件对象及其属性。
命名参数也被视为位置参数,即如果传递--name John --surname=Doe
,那么{{ args.0 }}
将是--name
,{{ args.1 }}
将是John
,而{{ args.2 }}
将是--surname="Doe"
。因此,建议首先传递位置参数。
如果您想传递所有命令行参数,可以使用 {{ args | join(sep=" ") }}
,或者如果您想引用它们,可以使用 {% for arg in args %} "{{ arg }}" {% %}
。
参见
环境变量和变量继承
当同一环境变量(env)和变量(vars)的值存在于多个位置时,最具体(最近的)值将具有优先权。例如,使用 env 定义的值优先于使用 dotenv 定义的值,以及定义在任务中的 vars 或 env 优先于文件中定义的值。
例如,如果您有以下文件
version: 1
# Default values. The tasks can override these values.
env:
ENV1: "env1"
ENV2: "env2"
vars:
VAR1: "var1"
VAR2: "var2"
tasks:
test1:
env:
ENV2: "test1_env2"
ENV3: "test1_env3"
vars:
VAR2: "test1_var2"
VAR3: "test1_var3"
cmds:
- echo "{{ env.ENV1 }} {{ env.ENV2 }} {{ env.ENV3 }}"
- echo "{{ vars.VAR1 }} {{ vars.VAR2 }} {{ vars.VAR3 }}"
# env and vars from the parent will take precedence
- task: test2
# This subtask will inherit the env and vars from the parent
# but its own bases, envs and vars will take precedence
- task:
# Bases will take precedence over the parent task
extend: test2
# env and vars take precedence over the parent task and the bases
env:
ENV2: "subtask_env2"
vars:
VAR2: "subtask_var2"
test2:
env:
ENV2: "test2_env2"
ENV3: "test2_env3"
vars:
VAR2: "test2_var2"
VAR3: "test2_var3"
cmds:
- echo "{{ env.VAR1 }} {{ env.VAR2 }} {{ env.VAR3 }}"
- echo "{{ vars.VAR1 }} {{ vars.VAR2 }} {{ vars.VAR3 }}"
输出将是(不包括调试输出)
$ mom test1
env1 test1_env2 test1_env3
var1 test1_var2 test1_var3
env1 test1_env2 test1_env3
var1 test1_var2 test1_var3
env1 subtask_env2 test2_env3
var1 subtask_var2 test2_var3
这可能会有些令人困惑,所以让我们解释一下输出
env1 test1_env2 test1_env3
var1 test1_var2 test1_var3
这是 test1
任务中前两条命令的输出。 ENV1
和 VAR1
只在文件中定义,而任务覆盖了 ENV2
、ENV3
、VAR2
和 VAR3
。
env1 test1_env2 test1_env3
var1 test1_var2 test1_var3
这是 test1
任务中的第三条命令的输出,它调用了 test2
。同样,ENV1
和 VAR1
只在文件中定义。虽然 test2
覆盖了 ENV2
、ENV3
、VAR2
和 VAR3
,但父任务 test1
中定义的值将具有优先权。
env1 subtask_env2 test2_env3
var1 subtask_var2 test2_var3
这是 test1
任务中的第四条命令的输出,它调用了一个子任务。 ENV1
和 VAR1
只在文件中定义。虽然看起来我们正在调用 task2
,但我们实际上定义了一个从 task2
继承并覆盖 ENV2
和 VAR2
的新任务。因此,从 task2
继承的值将优先于父任务。
shell 扩展
一些任务属性支持类似于 shell 的扩展。以下字符将被扩展
~
:主目录。$VAR
:环境变量VAR
的值。${VAR}
:环境变量VAR
的值。
注意,虽然环境变量可以这样展开,但在Tera模板中不可用。也就是说,{{ $VAR }}
将引发错误。您可以使用{{ env.VAR }}
。有关详细信息,请参阅[env] (#env)。
以下任务属性支持shell展开
Tera 模板引擎
使用的模板引擎是Tera。语法基于Jinja2和Django模板。文档包含所需的所有信息,非常直观,因此此处不再重复。只需忽略Rust特定的部分。
参见
Mom 过滤器
exclude
从列表或映射中排除一个值。该值可以是字符串,也可以是字符串列表。
示例
tasks:
test:
vars:
var1: "value1"
var2: "value2"
var3: "value3"
script: echo "{{ env | exclude(val='var2') | json_encode() }} {{ [1, 2, 3] | exclude(val=2) }}"
输出
$ mom test
{"var1": "value1", "var3": "value3"} [1, 3]
Mom 函数
input
请求用户输入。需要一个label
和一个default
参数。虽然label
必须是字符串,但default
可以是任何类型。
还可以提供一个if
参数,它必须是一个布尔值,并且必须与一个default
参数一起使用。如果if
是false
,则将返回default
参数而无需请求用户输入。这是模板中if
语句的简写。
示例
tasks:
test:
script: echo "{{ input(label='What is your name?', default='John Doe') }}"
输出
$ mom test
What is your name? [John Doe]:
tasks:
test:
script: echo "{{ input(label='What is your name?', default='John Doe', if=args is containing("--default")) }}"
$ mom test --default
John Doe
密码
⚠️警告:如果在脚本中使用,密码仍然会以明文形式添加到脚本文件中。请谨慎使用。
与输入类似,但输入不会回显到终端。
get_env
我们覆盖了默认实现,以便此方法还可以返回在mom文件中定义的环境变量,这些变量优先于系统环境变量。需要一个name
参数,它必须是字符串,并且可以有一个可选的default
参数,它可以任何类型。
示例
tasks:
test:
script: echo "{{ get_env(name='VAR1', default='default') }}"
输出
$ mom test
default
$ VAR1="value1"
$ mom test
value1
贡献
欢迎贡献!请首先阅读贡献指南。
依赖关系
~12–22MB
~319K SLoC