#yaml #yaml-config #snippets #configuration #action #input #input-file

app yambler

Yambler是一个将可重用YAML片段拼接在一起的工具

3个不稳定版本

0.2.1 2021年5月27日
0.2.0 2021年5月27日
0.1.0 2020年9月17日

#781 in 配置

自定义许可

18KB
216

Yambler

这是一个简单的YAML拼接程序,它操作在YAML事件级别,而不是字符流级别。这具有优势,因为所有输入文件本身就是有效的YAML,使其易于编辑和理解。

运行方式如下

yambler -i <input file> -o <output file> -s <snippet files ...>

或者这样

yambler -i <input dir> -o <output dir> -s <snippet dir>

它将输入文件(s)中的占位符字符串替换为片段文件中定义的YAML对象,并将结果文档写入输出文件(s)。

入门指南

安装您平台上的最新版本,并开始拼接一些YAML。

对于GitHub Actions

你要知道何时编写它们,
知道何时加载它们,
知道何时使用"uses"标签,
以及知道何时"运行"。

在您为GitHub Actions构建的各种工作流程中重用常见逻辑很难。您要么陷入发布自定义操作(这些操作本身在可重用性方面有限),要么拼接一些shell脚本,或者进行大量的复制粘贴,并希望您在需要更改时记得所有复制的位置。

Yambler就是为了解决这个问题而编写的:这不是一个理想的解决方案(GitHub正在研究更优雅的解决方案),但这至少可以让您的工作流程相对DRY(不要重复自己)。

示例

Yambler被用于Versio发布管理器的CI/CD管道中,另一款实用的开发者工具。我的.github目录看起来像这样

.github
├─ workflows-src
│  ├─ pr.yml
│  └─ release.yml
├─ snippets
│  ├─ check-versio.yml
│  ├─ common-env.yml
│  ├─ job-premerge-checks.yml
│  └─ <other snippet files ...>
└─ workflows
   ├─ pr.yml
   └─ release.yml

我直接不接触workflows中的任何内容:那里的所有内容都是生成的。相反,workflows-src是我进行顶层编辑的地方。例如,.github/workflows-src/pr.yml看起来像这样

---
name: pr
on:
  - pull_request
env: SNIPPET_common-env

jobs:
  create-matrixes: SNIPPET_job-create-matrixes
  premerge-checks: SNIPPET_job-premerge-checks

然后我将我的代码片段,每个文件一个,保存在 .github/snippets 中。这里有一个 common-env.yml

key: common-env
value:
  RUSTFLAGS: '-D warnings'
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  GITHUB_USER: ${{ github.actor }}

在我推送我的仓库之前,通过调用 yambler 来生成实际的工作流程。

yambler \
    -i .github/workflows-src \
    -o .github/workflows \
    -s .github/snippets

当然有一个脚本可以自动完成这个操作。还有一个辅助脚本,用来检查你的工作流程是否是最新的;将其复制到本地仓库中的 .git/hooks/pre-push 文件中,这样你就不会再推送过时的工作流程了。

通常,你希望忽略由 .gitignore 生成的文件,但对于工作流程文件来说,你不能这样做,否则会破坏它们的整个目的。我相信从理论上讲,可以自动保持这些文件的更新,但我发现每次更改输入或代码片段时,手动执行 yamble + 提交它们要容易得多。

操作

选项

--snippet (-s) 参数可以是一个文件列表,或者一个包含许多 YAML 文件的单一目录。同样,--input (-i) 参数可以是单个文件,或者包含许多 YAML 输入文件的目录。如果输入是目录,输出 (--output / -o) 也必须是目录,在这种情况下,将为每个输入文件生成一个输出文件。

算法

这是 Yambler 的工作方式:首先,读取每个输入文件的所有文档,并在遇到形式为 "SNIPPET_<代码片段名称>" 的 占位符字符串 时,将其替换为在代码片段文件中定义的 YAML 代码片段值。这个过程是递归的,因此代码片段可以包含其他代码片段等。运行时会检测无限循环,导致程序终止而不写入任何内容。所有占位符替换后,将结果 YAML 写入输出文件。

由于处理是在 YAML 层面上完成的,生成的输出不尊重输入文件的格式或样式决策,而是优先考虑 Yambler 内部 YAML 发射器的样式。输入中的任何注释都可能被丢弃;裸字符串、块字符串或继续风格的字符串可能被替换为简单引号等。这通常不是问题,因为你很少想查看生成的文件,生成的输出在语义上相同(相对于 YAML 规范)。

使用 Yambler 约等于使用类似于 C/C++ 宏、VBA 或 ML/1 的宏语言;具有许多相同的优点和缺点。没有堆叠参数传递或模板:代码片段基本上是原样插入到文本中,所以要记住这一点。另一方面,这使得判断最终的输出内容变得非常容易。

代码片段

代码片段文件可以包含多个 YAML 文档,每个文档被视为一个单独的代码片段:每个代码片段必须是一个至少包含两个键 "key" 和 "value" 的哈希表。(你可以有其他键:它们只是被忽略。)"key" 必须是一个字符串,用于定义代码片段键(该键在占位符字符串中标识);"value" 是 YAML 值本身,可以是任何 YAML 类型。

代码片段文件的名称在很大程度上无关紧要,但将文件名与其中包含的代码片段至少有一些关联是良好的做法,这样就可以快速找到特定的代码片段。

如果定义了多个具有相同键的片段,则行为未定义,尽管可能发生的情况是最后一个定义的片段“胜出”那个键。不要这样做!

拼接

上述替换的一个例外是拼接规则:如果占位符字符串是直接数组元素,并且替换片段也是一个数组,那么直接拼接片段数组,而不是替换单个元素。这使得将片段直接放置在数组中或连接多个片段以形成更长的列表变得容易。

示例

  • 简单字符串替换

    输入

    first_name: "John"
    last_name: SNIPPET_family
    

    片段

    key: family
    value: Smith
    

    输出

    first_name: John
    last_name: Smith
    
  • 对象替换

    输入

    job_1: SNIPPET_job1
    

    片段

    key: job1
    value:
      name: 'complex job'
      run: |
        Something something
        is strange
    

    输出

    job_1:
      name: complex job
      run: |
        Something something
        is strange
    
  • 拼接

    输入

    steps:
      - SNIPPET_setup
      - run: echo custom
      - SNIPPET_teardown
    

    片段

    ---
    key: setup
    value:
      - run: curl http://setmeup.com/now
      - name: finish setup
        use: my-actions/finish-setup@v1
    ---
    key: teardown
    value:
      - name: start teardown
        use: my-actions/start-teardown@v1
      - run: curl http://tearmedown.com/now
    

    输出

    steps:
      - run: "curl http://setmeup.com/now"
      - name: finish setup
        use: my-actions/finish-setup@v1
      - run: echo custom
      - name: start teardown
        use: my-actions/start-teardown@v1
      - run: "curl http://tearmedown.com/now"
    

依赖关系

~7–17MB
~217K SLoC