在 ArchGuard 2023 Roadmap 里,我们计划设计一个轻量级的任务引擎。在阅读了 Gradle 源码的 Task 设计之后,我们决定改用 GitHub Action 的方式:基于 Yaml 编排任务。在设计 Runner 时,设想的场景是::
- 自定义分析 + 治理任务。提供了一种基于脚本的灵活解决方案,能够支持各种自定义分析和治理任务。用户可以编写自己的脚本来完成各种工作,例如代码质量检查、代码扫描、安全检查、容器镜像扫描等等
- 架构适应度函数分析。用于评估软件架构的优劣,从而帮助开发人员识别和改进软件中存在的问题。
- CI/CD质量门禁。在 CI/CD 流程中,该工具可以作为质量门禁,帮助团队在持续集成和持续交付中保证软件质量。
- 开发时治理。与再有的 ArchGuard Gradle 插件使用,从而实现在开发阶段的架构分析和治理。
在这篇文章里,我们将详细介绍:
- \~GitHub\~ ArchGuard Runner 的设计,其核心是围绕 Yaml 的类型系统构建
- Registry 设计。类似于 NPM 的机制
- 第一个 Runner 示例,Git 管理,参考(复制)了 GitHub 里的 Checkout
其它的总是,问题 Kotlin 构建出来的体积,在 ArchGuard Scanner 我们通常构建了统一的依赖来解决。
PS:如果你也是参考(复制/粘贴)代码的话,记得注意一下开源软件的 License 问题。
Runner
起先,由于过去只熟悉 Gradle 源码及其 Task 机制,便想围绕 Gradle Task 设计,但是 Gradle 的机制过于复杂。于是,过了一段时间后,想起了类似于 GitHub Action 会是一种更简单的机制,而且 GitHub Action 也是开源的。
GitHub Action 是一个自动化工作流程,它由多个步骤和操作组成,主要由 Yaml 写成,可以在 GitHub 上触发和运行。而其 Runner 是 GitHub Actions 运行的环境,它负责从 GitHub 仓库下载工作流程、安装和配置需要的软件和工具、运行工作流程中的命令和操作,并将结果报告给 GitHub。
最重要的是 GitHub Action Runner 包含了一个开源版本,于是,我们设计(复制)了其 yaml 配置:
jobs:
steps:
- name: "Source Code with Linter"
uses: archguard/scanner@v2
with:
type: "source_code"
language: kotlin
features: [ "datamap" ]
output: [ "json", "arrow" ]
rules: [ "webapi", "test", "sql" ]
path: "."
要达到非常接近 GitHub Action 的设计,就需要去看源码,学习(抄) C# 代码可不是一件容易的事,还好我们有 IDE。其基本的几个 JobService 是:
- Worker:GitHub Action 的执行环境,可以理解为一个虚拟机,每个 Worker 可以执行一个或多个 Job。
- JobRunner:运行在 Worker 上的一个进程,负责协调 Job 中多个 Step 的执行顺序,管理 StepRunner 的运行和通信。
- StepRunner:每个 Job 中的一个 Step 对应一个 StepRunner,StepRunner 负责运行一个或多个 ActionRunner。
- ActionRunner:每个 Step 中的一个 Action 对应一个 ActionRunner,ActionRunner 负责执行一个 Action 中的具体命令或脚本。
考虑到我们当前并没有执行环境、进程等,只需要好好写 ActionRunner 即可,即解析上面 Yaml 中的 steps 里的 Action 字段。根据不同的 action 来执行,并将上面的 with
字段转换成对应的参数,因此上面的配置在执行时会变成:
java -jar plugins/scanner-v2 --output json --output arrow --features datamap --path . --language kotlin --rules webapi --rules test --rules sql --type source_code
如此一来,我们就有了更多的想象空间,可以自定义分析任务,并与架构实现更好的解耦:
- uses: linter/architecture@v1
- uses: linter/database@v1
- used: metric/oo@v1
- used: metric/complexity@v1
# for translation custom rules
- used: factor/sonarqube@v1
详细代码见:https://github.com/archguard/codedb-poc
Registry 机制设计
我们参考了 NPM Registry、Maven Central 等制品仓库的思想,构建了基本的 Runner registry。它包含了以下的功能:
- 中央化的 Registry 机制:使用一个中央化的方式来存储和管理软件包或制品,可以方便地实现版本控制和依赖管理。
- 多版本存储和管理:支持多版本的软件包或制品,每个版本都可以通过一个唯一的标识符进行访问和管理,这样可以方便地实现版本回滚和升级等操作。
- API 接口:所有工具都提供了一组 API 接口,可以通过这些接口来实现自动化构建、发布和部署等操作,方便自动化流程的集成和实现。
在 ArchGuard Runner 中,Registry 的运行机制如下:
- 解析 Runner。首先从配置中获取 Registry 的 URL,然后使用 HTTP 或 HTTPS 协议从该 URL 获取 Registry 的元数据 JSON 文件。
- 获取 Runner。根据从获取到的 JSON 文件中解析出所需的信息,包括名称、版本、制品下载 URL、制品类型等。然后,ArchGuard Runner 下载对应版本的制品,并保存到本地的缓存目录中。
- 执行 Runner。下载制品后,ArchGuard Runner 调用对应的脚本执行器来执行脚本。脚本执行器会根据制品的类型和脚本类型选择合适的执行方式,比如在 Linux 上执行 Shell 脚本或在 Windows 上执行批处理脚本。
于是乎,我们需要在 archguard.yml
中引入新的配置:
env:
registry:
url: https://registry.archguard.org/
local: true
server:
url: http://localhost:8084
将
@Serializable
data class ActionRegistry(
val name: String,
val description: String,
val version: String,
val author: String,
val time: Time,
val versions: Map<String, Version>,
val homepage: String,
val repository: Repository,
val license: String,
val readme: String
)
其中,versions
字段是一个 Map 类型,键是版本号,值是 Version
对象,Repository
表示代码仓库信息。每个 Version
对象包含了该版本的元数据信息,包括制品类型、制品下载 URL、制品 SHA256 校验和(还未实现)等。
未完成的部分:Registry 服务器
现在的 Registry 是一个放在 GitHub Pages 的 JSON 文件,相当于是一个 fake server。尽管,我们可能并不需要一个真正的服务器,但是需要一个能生成 JSON 文件的机制。而为了根据制品生成 JSON,我们需要在制品构建和发布的过程中将制品的信息写入一个描述文件中,然后将该描述文件打包成制品一起发布。该描述文件应该包含制品的元数据,例如名称、版本、作者、发布时间、许可证等信息,以及制品所需的运行时环境和依赖项的描述。也因此 ChatGPT 建议我们可以考虑:
- 使用静态网站生成器(如 Jekyll、Hugo 或 Hexo)
- 使用自定义后端服务(如 Spring Boot、Express 或 Flask)
- 使用第三方 Registry 服务(如 JFrog Artifactory 或 Sonatype Nexus)
如果你也有兴趣,欢迎一起来设计 registr
第一个 runner:Checkout
由于先前的 ArchGuard Clone 逻辑比较简单,并且缺少 SshKey 等的支持,所以我们决定参考(复制)GitHub Action 编写一个新的。GitHub 的 Checkout Action 的主要处理逻辑可以概括为以下几个步骤:
- 从参数中解析 Git 仓库的设置,包括仓库路径、URL、身份验证信息等。
- 配置身份验证信息(按需)。
- 初始化 Git 仓库,并添加远程仓库。
- 获取默认分支并拉取所有历史记录。
- 检出指定的提交,并更新子模块(按需)。
- 完成后,删除临时全局配置。
每一步都是封装 Git 的命令完成的,诸如于 checkout 是:
git -c protocol.version=2 fetch --prune --progress --no-recurse-submodules origin +refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*
在这些参数里包含了非常多的 Git 使用细节,可惜我用的是 GitHub Copilot 来转换 TypeScript 代码为 Kotlin 的,所以并没有展开详细的研究。
然后,将构建的 jar 上传到 registry,并编写一个 Json,就实现了完成的 Runner DEMO。
更多的 runner:等你来实现
如果你喜欢编写和优化软件构建流程,那么你一定会喜欢 ArchGuard Runner!它是一个全新的构建工具,旨在为开发人员提供更快、更可靠的软件构建和部署体验。现在,我们正在寻找有志于贡献自己力量的人加入我们的设计团队,一起开发这个充满活力的项目。
作为 ArchGuard Runner 的设计者之一,你将参与构建这个新工具的核心部分,包括运行器、插件、配置等等。你将与来自不同背景的开发人员、测试人员和其他设计师合作,共同探索新技术、解决问题并改进我们的软件开发流程。你还将有机会与各种软件开发团队和项目合作,了解他们的需求并提供解决方案。
或许您还需要下面的文章: