18 个版本
0.7.3 | 2024 年 6 月 17 日 |
---|---|
0.7.0 | 2024 年 3 月 30 日 |
0.6.2 | 2023 年 12 月 18 日 |
0.6.1 | 2023 年 11 月 6 日 |
0.4.1 | 2022 年 11 月 26 日 |
#987 在 开发工具 中排名
在 2 crates 中使用
105KB
783 行
Precious - 一款统治所有代码质量工具
谁不喜欢代码检查器和整理器(又称美化器)呢?我当然喜欢。我非常喜欢它们。在我的一些项目中,我可能会有五到十个!
如果只需一个命令就能运行所有这些工具,那不是很好吗?如果这个命令只有一个配置文件来定义要在项目的每个部分运行哪些工具,那不是很好吗?如果索伦是我们的统治者,那不是很好吗?
现在,有了 Precious,你可以对这些所有问题都说“是的”。
简而言之
Precious 是一款代码质量工具,允许您使用单个命令运行所有代码检查器和整理器。其功能包括
- 一个文件,
precious.toml
,定义了所有代码检查器和整理器的命令,以及它们操作的文件。 - 尊重版本控制系统的忽略文件,并允许全局和按命令排除。
- 语言无关,它可以以相同的方式与单语言或多语言项目一起工作。
- 易于与提交钩子和 CI 系统集成。
- 默认情况下,命令以并行方式执行,每个 CPU 一个进程。
- 命令可以用标签分组,例如,只为提交钩子运行子集命令,或在 CI 中运行所有命令。
安装
安装此工具有几种方法。
使用 ubi
安装我的 通用二进制安装程序 (ubi) 工具,您可以使用它下载 precious
以及许多其他工具。
$> ubi --project houseabsolute/precious --in ~/bin
二进制发布
您可以从 发布页面 获取二进制发布。解压缩 tarball,并将包含的可执行文件放在您的路径中的某个位置,然后您就可以使用了。
Cargo
您还可以通过运行以下命令使用cargo
安装此工具:cargo install precious
。有关二进制文件安装位置的详细信息,请参阅cargo 文档。
入门指南
precious
二进制文件有一个名为 config init
的子命令,该命令将为您生成配置文件。此子命令接受以下标志:
标志 | 描述 |
---|---|
-a ,--auto |
自动确定要创建的组件 |
-c ,‑‑component <COMPONENT> |
要为哪些组件生成配置(请参阅下文) |
- ,‑‑path <PATH> |
配置文件应写入的路径。默认为 ./precious.toml |
您必须传递--auto
或至少一个--component
。在--auto
模式下,precious
将查看您的项目中的所有文件,并根据找到的文件类型生成配置。
以下是一个Rust项目的示例
$> precious config init --component rust --component gitignore --component yaml
组件
以下组件受支持:
go
- 为使用golangci-lint
进行代码检查和整理的Go项目生成配置。perl
- 为使用perlcritic
和perltidy
等工具的Perl项目生成配置。rust
- 为使用rustfmt
进行整理和clippy
进行代码检查的Rust项目生成配置。shell
- 生成的配置将使用shfmt
进行整理,并使用shellcheck
进行代码检查。gitignore
- 使用omegasort
为.gitignore
文件进行代码检查和整理(通过排序)。markdown
- 使用prettier
为Markdown文件进行代码检查和整理。toml
- 使用taplo
为TOML文件进行代码检查和整理。yaml
- 使用prettier
为YAML文件进行代码检查和整理。
示例
此存储库的示例目录包含多个语言的precious.toml
配置文件。欢迎为其他语言做出贡献!
示例中的配置与precious config init
生成的配置相同,文件中包含有关如何更改此配置的更多详细信息。
还可以查看示例脚本install-dev-tools.sh
,该脚本用于安装项目中的所有代码检查和整理依赖项。您可以根据需要自定义此脚本,以仅安装项目所需的工具。
配置
珍贵是通过单个 precious.toml
或 .precious.toml
文件进行配置的,该文件位于项目根目录中。文件格式为 TOML 格式。
配置文件顶层表中只有一个键可以设置
键 | 类型 | 是否必需? | 描述 |
---|---|---|---|
exclude |
字符串数组 | 否 | 每个数组成员都是一个模式,当运行 precious 时,这些模式将与潜在文件进行匹配。这些模式与 gitignore 文件 中的模式匹配方式相同。可以使用以 ! 开头的行来否定列表中先前规则的含义,即使匹配到先前规则,匹配的任何内容也不会被排除。 |
所有其他配置都是基于每个命令的。命令是指那些可以整理(即格式化或美化)、检查或两者都做的操作。这些命令是外部程序,珍贵会根据需要执行这些程序。
每个命令都在一个类似 [commands.command-name]
的块中进行定义。在 commands.
前缀之后的所有名称必须是唯一的。只要每个命令都有一个唯一的名称,就可以使用不同的命令以不同的方式运行相同的可执行文件。
命令的运行顺序与它们在配置文件中的出现顺序相同。
命令调用
有三个配置键用于命令调用。它们都是可选的。如果没有指定,precious
默认为此
invoke = "per-file"
working-dir = "root"
path-args = "file"
这将在每个文件上运行一次命令,以命令的工作目录作为项目根目录。命令将作为单个参数传递给命令的相对路径。
invoke
invoke
键告诉 precious
如何调用命令。
值 | 描述 |
---|---|
"per-file" |
为每个匹配的文件运行此命令。 这是默认值。 |
"per-dir" |
为每个匹配的目录运行此命令一次。 |
"once" |
只运行一次此命令。 |
invoke
键还有一些实验性选项。 它们的名称或操作的详细信息可能在未来的版本中更改。
值 | 描述 |
---|---|
.per‑file‑or‑dir = n |
如果匹配的文件数量小于 n ,则为每个匹配的文件运行此命令一次。否则,为每个匹配的目录运行一次。 |
.per‑file‑or‑once = n |
如果匹配的文件数量小于 n ,则为每个匹配的文件运行此命令一次。否则,只运行一次。 |
.per‑dir‑or‑dir = n |
如果匹配的目录数量小于 n ,则为每个匹配的目录运行此命令一次。否则,只运行一次。 |
这些选项的格式如下所示
[commands.some-command]
invoke.per-file-or-dir = 42
这些实验性选项可用于优化命令的运行速度。在某些情况下,命令可以以多种方式运行,它完成的速度取决于需要检查或整理的文件或目录数量。
例如,golangci-lint
工具。对几个目录多次调用它可能比在整个存储库中调用它要快得多。然而,一旦有足够的目录进行检查,对整个存储库进行一次调用将会更快。
请注意,path-args
设置需要与这些选项的可能情况一起工作。对于 golangci-lint
,这意味着在使用 per-dir-or-once
时将其设置为 dir
。
工作目录
working-dir
键告诉 precious 命令运行时应该使用的工作目录。
值 | 描述 |
---|---|
"根目录" |
工作目录是项目根目录。 这是默认设置。 |
"dir" |
工作目录是包含匹配文件的目录。这意味着 precious 将逐个将 chdir 进入执行命令的每个匹配目录。 |
.chdir—to = "路径" |
执行命令时,工作目录将是给定的路径。 此路径必须是项目根目录的相对路径。 |
working-dir.chdir-to= "路径"
working-dir
的最后一个选项是将显式路径设置为工作目录。
使用此选项时,执行命令时工作目录将设置为给定的子目录。传递给命令的相对路径将相对于此子目录,而不是项目根目录。
path-args
path-args
键告诉 precious 命令运行时应该如何传递路径。
值 | 描述 |
---|---|
"file" |
通过根目录相对于匹配文件的路径传递。 这是默认设置。 使用 working-directory.chdir-to 时,路径相对于给定的工作目录。 |
"dir" |
通过根目录相对于包含匹配文件的目录的路径传递。 使用 working-directory.chdir-to 时,路径相对于给定的工作目录。 |
"none" |
根本不向命令传递任何路径。 |
"dot" |
始终传递 . 作为路径。当使用 working-dir = "dir" 并且命令仍然需要传递路径时,这很有用。 |
"absolute—file" |
将匹配文件的路径作为从文件系统根目录的绝对路径传递。 |
"absolute—dir" |
将包含匹配文件的目录的路径作为从文件系统根目录的绝对路径传递。 |
不合理的组合
大多数这些配置键的组合都是允许的,但也有一些不合理的组合会导致 precious
以错误退出。
invoke = "per-file"
path-args = "dir", "none", "dot", or "absolute-dir"
如果没有传递文件名,则无法对每个文件调用一次命令。
invoke = "per-dir"
path-args = "none" or "dot"
working-dir = "root"
# ... or ...
working-dir.chdir-to = "whatever"
您不能从根目录调用一次每个目录的命令,而不传递目录名或文件名列表。如果您想在没有路径参数或使用 .
作为路径的情况下对每个目录调用一次命令,那么您 必须 将 working-dir = "dir"
设置为。
invoke = "once"
working-dir = "dir"
如果工作目录设置为逐个匹配的目录,则无法调用一次命令。
调用示例
请参阅调用示例文档,其中包含每个可能选项集的全面示例。
其他每个命令的配置键
每个命令允许的其他键如下
键 | 类型 | 是否必需? | 适用范围 | 默认 | 描述 |
---|---|---|---|---|---|
type |
字符串 | 是 | 所有 | 这必须是 lint 、tidy 或 both 。这定义了此命令的类型。一个 both 的命令 必须 还定义 lint-flags 或 tidy-flags 。 |
|
include |
字符串或字符串数组 | 是 | 所有 | 每个数组成员都是一个gitignore模式,它告诉precious 哪些文件适用于此命令。您可以使用以 ! 开头的行来否定列表中之前规则的含义,即使匹配到之前规则,匹配的内容也不会被包括。 |
|
exclude |
字符串或字符串数组 | 否 | 所有 | 每个数组成员都是一个gitignore模式,它告诉precious 哪些文件不应适用于此命令。可以使用以 ! 开头的行来否定列表中先前规则的含义,即使匹配到先前规则,匹配的任何内容也不会被排除。 |
|
cmd |
字符串或字符串数组 | 是 | 所有 | 这是要运行的可执行文件,后面跟着应始终传递的任何参数。 | |
env |
table - 值是字符串 | 否 | 所有 | 此键允许您设置一个或多个环境变量,当运行命令时,这些变量将被设置。此表中的值必须是字符串。 | |
path-flag |
字符串 | 否 | 所有 | 默认情况下,precious 将操作路径作为它执行的命令的最后一个、位置参数传递。如果命令通过标志接受路径,您需要使用此键指定该标志。 |
|
lint-flags |
字符串或字符串数组 | 否 | combined linter & tidier | 如果命令既是linter又是tidier,它可能需要额外的标志来在linting模式下操作。这就是设置该标志的方法。 | |
tidy-flags |
字符串或字符串数组 | 否 | combined linter & tidier | 如果命令既是linter又是tidier,它可能需要额外的标志来在tidying模式下操作。这就是设置该标志的方法。 | |
ok-exit-codes |
整数或整数数组 | 是 | 所有 | 任何不指示异常退出的退出代码都应该在这里。对于大多数命令,这只是一个0 ,但某些命令可能即使对于正常退出也使用其他退出代码。 |
|
lint-failure-exit-codes |
整数或整数数组 | 否 | linters | 如果命令是linter,那么这些是表示lint失败的状态代码。这些需要被指定,以便precious 可以区分由于lint失败而退出的退出,以及由于某些意外问题而退出的退出。 |
|
ignore-stderr |
字符串或字符串数组 | 所有 | 所有 | 默认情况下,precious 假设当命令将输出发送到stderr 时,这表示lint或tidy失败。此参数可以指定一个或多个正则表达式。这些正则表达式将与命令的stderr输出进行匹配。如果任何正则表达式匹配,则忽略stderr输出。 |
|
labels |
字符串或字符串数组 | 所有 | 所有 | 用于分类命令的一个或多个标签。下面有更多详细信息。 |
引用项目根目录
对于可以从子目录运行的命令,您可能需要以项目根目录的形式指定配置文件。您可以通过在cmd
配置键的任何元素中使用字符串$PRECIOUS_ROOT
来实现这一点。例如,您可能写出如下内容
cmd = ["some-tidier", "--config", "$PRECIOUS_ROOT/some-tidier.conf"]
字符串$PRECIOUS_ROOT
将被替换为项目根目录的绝对路径。
运行Precious
要获取帮助,请运行precious --help
。
根命令接受以下标志
标志 | 描述 |
---|---|
-c ,--config <config> |
珍贵配置文件的路径 |
-j ,--jobs <jobs> |
要运行的并行作业(线程)数量(默认每个核心一个) |
-q ,--quiet |
抑制大部分输出 |
-a ,--ascii |
将超级有趣的Unicode符号替换为无聊的ASCII |
-v ,--verbose |
启用详细输出 |
-V ,--version |
打印版本信息 |
-d ,--debug |
启用调试输出 |
-t ,--trace |
启用跟踪输出(最大日志) |
-h ,--help |
打印帮助信息 |
并行执行
Precious 将始终并行执行命令,默认情况下每个CPU一个进程。执行基于命令的调用配置进行并行化。例如,在一个12个CPU的系统上,具有 invoke = "per-file"
的命令将并行执行多达12次,每个命令执行接收一个文件。
您可以通过传递 --jobs 1
来禁用并行执行。
子命令
precious
命令有三个子命令,lint
、tidy
和 config
。您必须始终指定其中之一。命令 lint
和 tidy
使用相同的标志
选择操作路径
当您运行 precious
时,您必须告诉它要操作哪些路径。为此有几个标志
模式 | 标志 | 描述 |
---|---|---|
所有路径 | -a ,--all |
在项目根目录下的所有文件上运行(包含珍贵配置文件的目录)。 |
根据git修改的文件 | -g ,--git |
在git报告为已修改的所有文件上运行,包括暂存文件。 |
根据git暂存的文件 | -s ,--staged |
在git报告为已暂存的所有文件上运行。 |
与给定git ref不同的文件 | -d <REF> ,‑‑git‑diff‑from |
在当前HEAD 中运行所有与给定<REF> 不同的文件。值<REF> 可以是分支名,如master ,或者引用名,如HEAD~6 或master@{2.days.ago} 。有关更多信息,请参阅git help rev-parse 。请注意,此操作将不会看到本地工作目录中未提交更改的文件。 |
根据git暂存的文件,未暂存的更改被暂存 | ‑‑staged‑with‑stash |
这类似于--stashed ,但在运行过程中它会暂存未暂存的更改,并在结束时弹出暂存。这确保了命令只针对您的代码库的暂存版本运行。这可能会与许多监视文件更改的编辑器或其他工具出现问题,因此在使用此标志时要小心。由于这个问题,在脚本中使用此选项时要小心。 |
在CLI上提供的路径 | 如果您没有传递上述任何标志,那么precious 将期望在所有其他标志之后在命令行上传递一个或多个路径。如果这些路径中的任何一个是目录,则将包括整个目录树。 |
运行一个命令
您可以通过传递--command
标志使用单个命令来整理或检查。
$> precious lint --command some-command --all
传递给--command
的名称必须与您的配置文件中命令的名称匹配。因此,在上面的示例中,这将寻找配置中定义的[commands.some-command]
命令。
使用标签选择命令
每个命令可以分配一个或多个标签。这允许您创建任意命令组。然后,当您整理或检查时,可以通过传递一个--label
标志来选择一个标签。
$> precious lint --label some-label --all
标签的工作方式如下
- 配置中没有
labels
键的命令有一个标签,即default
。 - 在没有
--label
标志的情况下运行tidy
或lint
使用的是default
标签。 - 如果您将
labels
分配给一个命令,并且希望该命令包含在default
标签中,则必须明确包含它[command.some-command] # ... labels = [ "default", "some-label" ]
默认排除
在选择路径时,precious
始终尊重您的忽略文件。目前,它只知道这与git如何工作,并且将尊重以下所有忽略文件
- 每个目录的
.ignore
和.gitignore
文件。 .git/info/exclude
文件。- 全局gitignore globs,通常在
$XDG_CONFIG_HOME/git/ignore
中找到。
这是通过使用rust ignore
crate实现的,因此可以在那里提出对其他VCS系统的支持。
此外,您可以通过设置全局exclude
键来为所有命令指定排除项。
最后,您可以指定每个命令的 include
和 exclude
键。
如何应用 Include 和 Exclude
当 precious
运行时,它执行以下操作以确定哪些命令适用于哪些路径。
- 操作的基础文件是根据指定的命令行标志选择的。这其中包括:
--all
- 项目根目录下(包含 precious 配置文件的目录)下的所有文件。--git
- git 仓库中已修改的所有文件,包括暂存文件。--staged
- git 仓库中已暂存的文件。--git-diff-from <REF>
- 当前HEAD
与<REF>
不同的所有文件。- CLI 传递的路径 - 如果路径是一个文件,则直接添加到列表中。如果路径是一个目录,则递归地找到该目录下的所有文件。
- 将 VCS 忽略规则应用于从列表中删除文件。
- 将全局排除规则应用于从列表中删除文件。
- 根据命令的
invoke
键,生成一个待检查的文件列表,并应用命令的 include/exclude 规则。为了被包含,文件必须至少匹配一个 include 规则 且 不匹配任何 exclude 规则才能被接受。- 如果
invoke
是per-file
,则逐个文件应用规则。 - 如果
invoke
是per-dir
,则如果目录中的任何文件符合规则,则在该目录上运行该命令。 - 如果
invoke
是once
,则一次性将规则应用于所有文件。如果其中任何一个文件匹配 include 规则,则运行该命令。
- 如果
config
子命令
除了 init
子命令外,此命令还有一个 list
子命令。该子命令打印一个 Unicode 表,描述配置文件中的命令。
Found config file at: /home/autarch/projects/precious/precious.toml
┌─────────────────────┬──────┬────────────────────────────────────────────────────────┐
│ Name ┆ Type ┆ Runs │
╞═════════════════════╪══════╪════════════════════════════════════════════════════════╡
│ rustfmt ┆ both ┆ rustfmt --edition 2021 │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ clippy ┆ lint ┆ cargo clippy --locked --all-targets --all-features │
│ ┆ ┆ --workspace -- -D clippy::all │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ prettier ┆ both ┆ ./node_modules/.bin/prettier --no-config --print-width │
│ ┆ ┆ 100 --prose-wrap always │
├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤
│ omegasort-gitignore ┆ both ┆ omegasort --sort path --unique │
└─────────────────────┴──────┴────────────────────────────────────────────────────────┘
配置建议
以下是获得 precious 最佳体验的一些建议。
选择如何调用命令 invoke
一些命令在将 invoke
设置为 per-dir
或 once
时可能效果相同。正确的运行模式取决于您如何使用 precious。
通常,如果您有非常少的目录,或者 您一次运行 precious 的目录数量很多,那么 once
将更快。
但是,如果您有更多的目录,并且通常只需要一次同时检查这些目录中的一个小子集,那么 per-dir
模式将更快。
您还可以使用实验性的 invoke.per-dir-or-once = n
选项,根据 precious 将要操作的目录数量以两种方式之一调用命令。
命令的静默标志
许多命令将接受某种形式的“静默”标志。一般来说,您可能 不 想要在静默模式下使用 precious 运行命令。
在 tidy 或 lint 命令成功执行的情况下,precious 已经隐藏了它运行的所有命令的 stdout。如果命令以某种方式失败,precious 将打印出命令的 stdout 和 stderr 输出。
默认情况下,珍贵的奖励会将任何输出到stderr的内容都视为命令错误(与linting失败相反)。您可以使用ignore-stderr
来指定一个或多个允许的stderr输出正则表达式。
此外,您可以通过运行珍贵的--debug
模式来查看命令的所有stdout和stderr输出。
总的来说,使用珍贵的静默模式运行命令通常是没有价值的。这样只会使得在lint检查失败或其他问题发生时更难以调试该命令的问题。
退出代码
在--tidy
模式下运行时,如果整理过程中没有错误,珍贵的退出代码始终为0
,无论是否整理了任何文件。
在--lint
模式下运行时,如果所有文件都通过了linting,珍贵的退出代码将为0
。如果任何lint命令失败,它将以1
退出。
在这两种模式下,如果任何命令失败,无论是通过返回未列出的ok退出代码还是意外地打印到stderr,则退出代码将不会是0
或1
。
常见场景
有一些配置场景您可能需要处理。以下是一些示例
命令在整个源树中只运行一次
一些命令,例如rust-clippy,期望在整个源树中只运行一次,而不是每个文件或目录运行一次。
为了实现这一点,您应该使用以下配置
include = "**/*.rs"
invoke = "once"
path-args = "dot" # or "none"
这将导致precious
在项目根目录中恰好运行一次命令。
命令在其lint的文件相同的目录中运行,并且不接受路径参数
如果您想在不将操作的路径传递给命令的情况下运行命令,设置invoke = "per-dir"
、working-dir = "dir"
和path-args = "none"
include = "**/*.rs"
invoke = "per-dir"
working-dir = "dir"
path-args = "none"
您希望命令排除整个目录(树)除了一个或多个文件外
在exclude
列表中使用以!
开头的忽略模式
[commands.rustfmt]
type = "both"
include = "**/*.rs"
exclude = [
"path/to/dir",
"!path/to/dir/included.rs",
]
cmd = ["rustfmt"]
lint-flags = "--check"
ok-exit-codes = [0]
lint-failure-exit-codes = [1]
您希望将珍贵的作为提交钩子运行
只需在您的钩子中运行precious lint -s
。如果任何lint命令指示存在linting问题,它将退出并带有非零状态。
您希望以特定的顺序运行命令
截至版本0.1.2,命令的运行顺序与它们在配置文件中出现的顺序相同。
构建状态
构建和测试
Cargo Audit Nightly
Cargo Audit On Push
依赖项
~5–14MB
~190K SLoC