1 个不稳定版本

0.3.0 2022年10月2日

#1876开发工具

MIT/Apache

18KB
508

Precious - 一款统治所有代码质量工具

谁不喜欢代码检查器和整理器(也称为美化打印器)呢?我确实很喜欢。我非常喜欢它们,在我的许多项目中可能有五到十个!

如果可以只通过一个命令运行所有的检查器,那岂不是很好?如果这个命令只需要一个配置文件来定义项目每个部分上运行哪些工具,那岂不是很好?如果索伦是我们的统治者,那岂不是很好?

现在,使用 Precious,你可以对这些问题的所有回答都是“是”。

TLDR

Precious 是一款代码质量工具,允许您通过单个命令运行所有的检查器和整理器。它的功能包括

  • 一个文件,precious.toml,定义了所有的检查器和整理器命令,以及它们操作哪些文件。
  • 尊重 VCS 忽略文件,并允许全局和按命令排除。
  • 语言无关,可以与单语言或多语言项目以相同的方式工作。
  • 易于与提交钩子和 CI 系统集成。
  • 默认情况下,命令通过每个 CPU 的一个进程并行执行。
  • 可以通过标签对命令进行分组,例如仅运行提交钩子的一组命令和在 CI 中运行的所有命令。

安装

安装此工具有几种方法。

使用 ubi

安装我的 通用二进制安装器 (ubi) 工具,然后可以使用它下载 precious 和许多其他工具。

$> ubi --project houseabsolute/precious --in ~/bin

二进制发布

您可以从 发布页面 获取二进制发布。解压缩 tarball,并将其中包含的可执行文件放置在您的路径中的某个位置即可。

Cargo

您还可以通过运行 cargo install precious 命令通过 cargo 安装此工具。有关二进制文件将安装的位置的信息,请参阅 Cargo 文档

入门指南

precious》二进制程序有一个config init子命令,可以为您生成配置文件。此子命令接受以下标志:

标志 描述
-a--auto 自动确定要创建哪些组件
---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 - 为使用perlcriticperltidy等工具的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通过位于项目根目录的单个precious.toml.precious.toml文件进行配置。该文件使用TOML格式

配置文件顶级表中只能设置一个键。

类型 必需? 描述
exclude 字符串数组 当运行precious时,每个数组成员都是一个模式,该模式将与潜在文件进行匹配。这些模式与gitignore文件中的模式以相同的方式进行匹配。
您可以使用以!开头的行来否定列表中先前规则的含义,这样即使匹配先前的规则,匹配的任何内容也不会被排除。

所有其他配置都是基于每个命令的。命令是指进行整理(又称美化打印或美化)、进行代码检查,或两者都做的操作。这些命令是precious需要时执行的独立程序。

每个命令都在一个命名为类似[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

working-dir键告诉precious命令运行时的工作目录是什么。

描述
"root" 工作目录是项目根目录。这是默认值。
"dir" 工作目录是包含匹配文件的目录。这意味着 precious 将在执行命令时依次将 chdir 转入每个匹配的目录。
.chdir-to = "path" 执行命令时,工作目录将是给定的路径。 这个路径必须是项目根目录的相对路径。
working-dir.chdir-to= "path"

working-dir 的最终选项是将一个显式路径设置为工作目录。

使用此选项,在执行命令时,工作目录将设置为给定的子目录。传递给命令的相对路径将相对于此子目录,而不是项目根目录。

path-args

path-args 键告诉 precious 在运行命令时如何传递路径。

描述
"file" 传递相对于根目录的匹配文件的路径。 这是默认值。
With working-directory.chdir-to 路径相对于给定的工作目录。
"dir" 传递相对于根目录的包含匹配文件的目录的路径。
With 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 string yes all 这必须是 linttidyboth 之一。这定义了此命令的类型。一个 both 的命令 必须 定义 lint-flagstidy-flags
include string 或字符串数组 yes all 数组的每个成员都是一个 gitignore 模式,它告诉 precious 此命令适用于哪些文件。
您可以使用以 ! 开头的行来否定列表中之前规则的含义,这样即使匹配先前的规则,也不会包含匹配的任何内容。
exclude string 或字符串数组 all 数组的每个成员都是一个 gitignore 模式,它告诉 precious 此命令不应用于哪些文件。
您可以使用以!开头的行来否定列表中先前规则的含义,这样即使匹配先前的规则,匹配的任何内容也不会被排除。
cmd string 或字符串数组 yes all 这是要运行的程序,后面跟着应该始终传递的任何参数。
环境变量 表 - 值是字符串 all 此键允许您在运行命令时设置一个或多个环境变量。此表中的值必须是字符串。
路径标志 string all 默认情况下,precious会将正在操作的路由传递给它执行的命令作为最终的、位置性的参数。如果命令通过标志接受路径,则需要使用此键指定该标志。
lint标志 string 或字符串数组 组合的linter和tidier 如果命令既是linter又是tidier,则它可能需要额外的标志来在linting模式下操作。这就是设置该标志的方法。
tidy标志 string 或字符串数组 组合的linter和tidier 如果命令既是linter又是tidier,则它可能需要额外的标志来在tidying模式下操作。这就是设置该标志的方法。
ok退出代码 整数或整数数组 yes all 任何不表示异常退出的退出代码都应该在这里。对于大多数命令,这仅仅是0,但某些命令可能即使对于正常退出也会使用其他退出代码。
lint失败退出代码 整数或整数数组 linters 如果命令是linter,则这些是表示lint失败的状态码。这些状态码需要指定,以便precious可以区分由于lint失败而退出的退出与由于某些意外问题而退出的退出。
忽略stderr string 或字符串数组 all all 默认情况下,precious假设当命令将输出发送到stderr时,表明lint或tidy失败。此参数可以指定一个或多个正则表达式。这些正则表达式将与命令的stderr输出进行匹配。如果任何正则表达式匹配,则忽略stderr输出。
标签 string 或字符串数组 all all 用于对命令进行分类的一个或多个标签。有关更多详细信息,请参阅下文。

引用项目根目录

对于可以从子目录运行的命令,您可能需要以项目根目录的形式指定配置文件。您可以通过在cmd配置键的任何元素中使用字符串$PRECIOUS_ROOT来实现这一点。例如,您可能编写如下内容

cmd = ["some-tidier", "--config", "$PRECIOUS_ROOT/some-tidier.conf"]

$PRECIOUS_ROOT字符串将被替换为项目根目录的绝对路径。

运行Precious

要获取帮助,请运行precious --help

根命令接受以下标志

标志 描述
-c--config <config> precious配置文件的路径
-j--jobs <jobs> 要运行的并行作业(线程)数(默认为每个核心一个)
-q--quiet 抑制大多数输出
-a--ascii 用无聊的ASCII替换超级有趣的Unicode符号
-v--verbose 启用详细输出
-V--version 打印版本信息
-d--debug 启用调试输出
-t--trace 启用跟踪输出(最大日志记录)
-h--help 打印帮助信息

并行执行

Precious 将始终并行执行命令,默认情况下每个 CPU 使用一个进程。执行将根据命令的调用配置进行并行化。例如,在一个 12 个 CPU 的系统上,具有 invoke = "per-file" 的命令将并行执行多达 12 次,每个命令执行接收一个文件。

您可以通过传递 --jobs 1 来禁用并行执行。

子命令

precious 命令有三个子命令,linttidyconfig。您必须始终指定这些之一。 linttidy 命令接受相同的标志

选择要操作的路径

当您运行 precious 时,必须告诉它要操作哪些路径。为此有多个标志

模式 标志 描述
所有路径 -a--all 在项目根目录下的所有文件上运行(包含 precious 配置文件的目录)。
根据 git 变更的文件 -g--git 在 git 报告为已变更的所有文件上运行,包括暂存文件。
根据 git 暂存的文件 -s--staged 在 git 报告为已暂存的文件上运行。
与给定 git ref 不同的文件 -d <REF>‑‑git‑diff‑from 在当前 HEAD 中与给定 <REF> 不同的所有文件上运行。 <REF> 的值可以是分支名称,如 master,或者 ref 名称,如 HEAD~6master@{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 标志的情况下运行 tidylint 使用的是 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 键来指定所有命令的排除项。

最后,您可以为每个命令指定 includeexclude 键。

如何应用包含和排除

precious 运行时,它会执行以下操作以确定哪些命令适用于哪些路径。

  • 要操作的基本文件是基于指定的命令行标志选择的。这包括以下选项之一:
    • --all - 项目根目录下的所有文件(包含 precious 配置文件的目录)。
    • --git - git 仓库中所有已修改的文件,包括暂存文件。
    • --staged - git 仓库中所有已暂存的文件。
    • --git-diff-from <REF> - 当前 HEAD 中与 <REF> 不同的所有文件。
    • CLI 上传递的路径 - 如果路径是文件,则按原样将其添加到列表中。如果路径是目录,则找到该目录下的所有文件(递归)。
  • 将应用 VCS 忽略规则以从该列表中删除文件。
  • 将应用全局排除规则以从该列表中删除文件。
  • 根据命令的 invoke 键,生成一个待检查的文件列表,并应用命令的包含/排除规则。要被包含,文件必须至少匹配一个包含规则 并且 不匹配任何排除规则才能被接受。
    • 如果 invoke 设置为 per-file,则规则逐个文件应用。
    • 如果 invoke 设置为 per-dir,那么如果目录中的任何文件匹配规则,则将在该目录上运行命令。
    • 如果 invoke 设置为 once,则规则一次性应用于所有文件。如果其中任何一个文件匹配包含规则,则将运行命令。

配置子命令 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-dironce 时可能效果相同。正确的运行模式取决于您如何使用 precious。

通常,如果您要么有非常少的目录,要么 您一次性在大多数或所有目录上运行 precious,那么 once 将更快。

但是,如果您有更多的目录,并且您通常只需要一次性检查这些目录中的小部分,那么 per-dir 模式将更快。

您还可以使用实验性的 invoke.per-dir-or-once = n 选项,根据 precious 将操作多少个目录,以两种方式之一调用命令。

命令的静默标志

许多命令将接受某种类型的“静默”标志。通常,您可能不想在静默模式下使用 precious 运行命令。

在成功执行整理或检查命令的情况下,precious 已经隐藏了它运行的命令的所有 stdout。如果命令以某种方式失败,precious 将打印出命令的 stdout 和 stderr 输出。

默认情况下,precious 将 stderr 的任何输出都视为命令中的错误(而不是检查失败)。您可以使用 ignore-stderr 指定一个或多个正则表达式来指定允许的 stderr 输出。

此外,您可以通过以 --debug 模式运行 precious 来查看命令的所有 stdout 和 stderr 输出。

所有这些都表明,在一般情况下,在静默模式下运行命令没有价值。这只会使在检查失败或出现其他问题时调试该命令的问题变得更加困难。

退出代码

--tidy 模式下运行时,如果整理没有错误,precious 总是退出代码 0,无论是否整理了任何文件。

--lint 模式下运行时,如果所有文件都通过检查,precious 将退出代码 0。如果任何检查命令失败,它将退出代码 1

在这两种模式下,如果任何命令失败,无论是返回未列出的有效退出代码还是意外打印到 stderr,则退出代码不会是 01

常见场景

您可能需要处理一些配置场景。以下是一些示例

命令只在整个源树中运行一次

某些命令,例如 rust-clippy,希望在整个源树中只运行一次,而不是每个文件或目录运行一次。

为了实现这一点,您应使用以下配置

include = "**/*.rs"
invoke = "once"
path-args = "dot" # or "none"

这将导致 precious 在项目根目录中恰好运行一次命令。

命令在与其检查的文件相同的目录中运行,并且不接受路径参数

如果您想在不对命令传递操作路径的情况下运行命令,请设置 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 作为提交钩运行

只需在您的钩子中运行 precious lint -s。如果任何检查命令指示存在检查问题,它将以非零状态退出。

您想按特定顺序运行命令

截至版本 0.1.2,命令的运行顺序与它们在配置文件中出现的顺序相同。

构建状态

构建和测试

Build Status

Cargo Audit 夜间

Cargo Audit Nightly

Cargo Audit 推送

Cargo Audit On Push

依赖项

~4–15MB
~186K SLoC