十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

actions-runner-controller 项目工作流重构实战:从 Go 验证到 GHA E2E 测试的自动化质量体系

actions-runner-controller 项目工作流重构实战:从 Go 验证到 GHA E2E 测试的自动化质量体系 actions-runner-controller 项目工作流重构实战从 Go 验证到 GHA E2E 测试的自动化质量体系【免费下载链接】actions-runner-controllerKubernetes controller for GitHub Actions self-hosted runners项目地址: https://gitcode.com/GitHub_Trending/ac/actions-runner-controller本文基于仓库 docs/adrs/2023-03-17-workflow-improvements.md 这一份已完成的架构决策记录ADR展开梳理 actions-runner-controller 在“Go 代码验证、Kubernetes 清单 / Helm Chart 验证、端到端测试”三条主线上对 GitHub Actions 工作流进行重构的动机、方案与落地结果。读完本文你将掌握 ARC 项目当前 CI/CD 流水线的整体布局、每个工作流的职责边界与触发条件以及如何把“验证代码正确性”从“跑一堆测试”升级为“一套可解释、可维护、不产生重复劳动”的质量体系。背景一个仓库里住着两个项目actions-runner-controller 仓库里实际上并存着两个项目一套是社区延续下来的“legacy” actions-runner-controller基于 RunnerDeployment / RunnerReplicaSet / RunnerSet 等 CRD另一套是 GitHub 官方支持推进的 gha-runner-scale-set基于 AutoscalingRunnerSet / EphemeralRunnerSet 等 CRD。为了加快进度团队在既有工作流之上陆续添加了一些自己的流水线例如端到端测试结果逐渐出现三类问题职责混乱多个工作流各管一摊又互相重叠难以说清“这个检查为什么在这里、为什么由它做”运行时间膨胀部分工作流运行时长不断增加跨模式相互干扰与 GitHub Actions 相关的偶发 flaky 测试会挡住 legacy ARC 的合并反之亦然。基于此ADR 圈定了三个需要重点治理的领域Go 代码、Kubernetes 清单 / Helm Chart、E2E 测试。Go 代码从三个工作流收敛为一个重构前仓库里用于校验 Go 代码的工作流有三个工作流职责golangci-lint一组 linter 集合运行于所有 PR 与 push 到 masterValidate ARC“大杂烩”兜底工作流除了 Go 测试还校验 Kubernetes 清单、执行go generate、go fmt、go vetRun CodeQL静态安全分析ADR 的提议是只保留一个Go工作流把所有与 Go 相关的事收拢到一起。理由很朴素——统一入口能大幅提升可靠性与可理解性而且这个工作流不应只服务于 GHA 支持的运行模式因为即便改动发生在 GHA 代码库之外比如一次依赖升级同样可能影响整个项目。关键设计约束有二触发面收敛该工作流只在*.go、go.mod、go.sum变更时运行这正是“把资源花在刀刃上”的思路职责划分内部拆成若干个独立 job既覆盖原有全部功能又消除重复。test全量测试 清单新鲜度检查testjob 的目标是运行项目内全部 Go 测试。ADR 特别指出了两个当时在用但价值存疑的 flag-short用于跳过 legacy ARC 的 E2E 测试即 test/e2e/e2e_test.go 中以testing.Short()跳过的那部分这个要保留-coverprofile只增加测试时长却换不来多少收益建议去掉。同时建议启用actions/setup-gov5以利用其内置缓存大幅加速测试或若必须留在 v3 则显式打开缓存。此外test必须依赖make manifests先执行先重新生成清单再检查是否存在 diff——有 diff 就失败从而保证“生成产物永远与源码同步”。这一设计在仓库当前实现 .github/workflows/go.yaml 的testjob 中完整落地checkout 后先make manifests随后git diff --exit-code校验再安装 envtest 二进制并执行- run: make manifests - name: Check diff run: git diff --exit-code - name: Setup envtest run: | go install sigs.k8s.io/controller-runtime/tools/setup-envtest$(go list -m -f {{ .Version }} sigs.k8s.io/controller-runtime | awk -F[v.] {printf release-%d.%d, $2, $3}) ENVTEST_K8S_VERSION$(go list -m -f {{ .Version }} k8s.io/api | awk -F[v.] {printf 1.%d, $3}) echo KUBEBUILDER_ASSETS$(setup-envtest use ${ENVTEST_K8S_VERSION} -p path) $GITHUB_ENV - name: Run go tests run: | go test -short go list ./... | grep -v ./test_e2e_arc从 Makefile 可以印证测试命令的底层构成test: generate fmt vet manifests shellcheck setup-envtest测试体为go test $(GO_TEST_ARGS) go list ./... | grep -v ./test_e2e_arc -coverprofile cover.out其中GO_TEST_ARGS默认就是-short。也就是说E2E 测试被排除在常规测试之外交给专门的工作流去跑——这与 ADR “E2E 应该在别处运行”的意图一致。fmt从“跑了没结果”到“diff 即失败”重构前go fmt ./...作为 Validate ARC 的一部分执行但结果没有任何人消费——格式化了却不检查差异。ADR 要求fmt job 必须对 diff 失败且此 job 无需缓存。当前实现正是如此- name: fmt run: go fmt ./... - name: Check diff run: git diff --exit-codelintgolangci-lint 一统天下lint job 对应原来的 golangci-lint 工作流同时覆盖了原来在 Validate ARC 中由go vet负责的部分golangci-lint 本身包含 vet 类检查。仓库现在的 lint job 使用golangci/golangci-lint-action版本固定在 v2.11.2并开启only-new-issues: true只报告新增问题Makefile 中也保留了本地等价入口lint: docker run --rm -v $(PWD):/app -w /app golangci/golangci-lint:v2.11.2 golangci-lint run方便开发者在提交前用同一版本、同一配置在本地复现。generate堵住“生成的代码没被检查入库”的漏洞ADR 明确指出旧行为的风险Validate ARC 在流程里生成代码并用生成结果跑测试却从不校验生成的代码是否已及时提交——这意味着任何人忘记提交zz_generated.*.go或更新的 CRD 都不会被 CI 拦截。新设计下 generate job 运行go generate并对 diff 失败。.github/workflows/go.yaml 的实现为make generate对应 Makefile 中$(CONTROLLER_GEN) object:headerFile./hack/boilerplate.go.txt paths./...git diff --exit-code。此外仓库还额外增加了一个mocksjob 运行 mockery 并同样检查 diff把“生成物一致性”的防线又前推了一步。vulncheck被 CodeQL 覆盖的“编辑注记”ADR 原本计划为 Go 团队维护的govulncheck递归分析代码中所有函数调用、在调用栈上定位漏洞的工具增设一个vulncheckjob但文中以 EDIT 批注的方式留下结论这一需求由 CodeQL 覆盖。仓库中 .github/workflows/global-run-codeql.yaml 确实在 push、PR 之外还通过cron: 30 1 * * 0定时调度对go, actions两类语言执行 CodeQL 初始化、自动构建与分析并申请security-events: write权限写入安全告警。这也印证了“一个需求可以有多种实现路径ADR 负责把决策过程记录下来”的价值。Kubernetes 清单 / Helm Chart验证彻底分离ADR 提到团队“近期已把 Helm chart 验证独立出来”并让“清单是否最新”成为Go / testjob 的一部分。当前仓库中这一分离已经非常彻底形成两套互不干扰的 Chart 验证流水线.github/workflows/gha-validate-chart.yaml针对 gha 系列 Chartgha-runner-scale-set、gha-runner-scale-set-controller、两个 experimental Chart通过!charts/actions-runner-controller/**显式排除 legacy Chart用helm/chart-testing-action做 lint安装helm-unittest插件执行helm unittest再用go test ./charts/gha-runner-scale-set/...直接跑模板 Go 测试.github/workflows/arc-validate-chart.yaml针对 legacy 的actions-runner-controllerChart同样做ct lint并在list-changed输出为 true 时拉起 kind 集群、预装 cert-manager、执行ct install做真实安装验证。两者均通过paths限定触发范围形成“legacy 与 gha 各自的 Chart 改动只触发各自的验证”的隔离格局正是 ADR 所追求的“消除互相干扰”。而“清单最新性”则由 Makefile 的manifests目标manifests-gen-crds用 controller-gen 生成 CRD 后由chart-crds同步到各 Chart 的crds/目录配合Go / testjob 中的git diff --exit-code双重保证。端到端测试GHA E2E 的命名与结果验证E2E 测试承担着“决定我们是否敢发版”的重任ADR 提出两项改进均已在仓库落地改进一重命名为 GHA E2E让场景名可读自资源改名以来gha前缀被用来标识“GitHub 官方支持模式”相关的一切而 E2E job 恰好只验证 GitHub 模式因此工作流应命名为GHA E2E更短的名字也让各类场景一目了然如GHA E2E / single-namespace-setup。仓库当前 .github/workflows/gha-e2e-tests.yaml 的name即为(gha) E2E Tests且场景与 ADR 描述的命名风格完全一致。改进二从“数 Pod”到“验证工作流能成功跑完”旧测试只监控并校验工作流执行期间派生出的 Pod 数量不校验工作流本身的执行结果。ADR 认为至少应保证工作流能成功跑完而不必深究 Pod 细节。当前这套测试体系已经做到了“结果验证”而且做得比 ADR 设想的更完整。整体架构是驱动器hack/e2e-test.sh 扫描 test/actions.github.com 目录下所有*.test.sh脚本并逐个执行也支持./hack/e2e-test.sh test_name单独运行某一个要求GITHUB_TOKEN、TARGET_ORG、TARGET_REPO三个环境变量齐备任何一个测试失败即整体退出非零场景脚本例如 test/actions.github.com/default-setup-v2.test.sh 的流程为——构建镜像 → 创建 kind 集群 → helm 安装 controllergha-runner-scale-set-controller-experimental→ helm 安装 scale setgha-runner-scale-set-experimental→run_workflow→ 清理 scale set → 收集日志 → 删除集群 → 汇总结果结果校验test/actions.github.com/helper.sh 中的run_workflow拆成start_workflow与wait_for_run_completion前者用gh workflow run触发目标仓库中的测试工作流支持按文件名 / 名称 / ID 解析并自动兜底.yml与.yaml后缀差异轮询gh run list拿到 run id后者用gh run watch --exit-status阻塞等待直至 run 完成非零退出码即视为测试失败——这正对应 ADR “至少保证 workflow 能成功 conclude”的要求CI 编排.github/workflows/gha-e2e-tests.yaml 为每个场景default-setup、single-namespace-setup、dind-mode-setup、kubernetes-mode-setup、auth-proxy-setup、anonymous-proxy-setup、self-signed-ca-setup、update-gha-runner-scale-set、init-with-min-runners均含-v2变体声明独立 job每个 job 通过peter-murray/workflow-application-token-action用E2E_TESTS_ACCESS_APP_ID/E2E_TESTS_ACCESS_PK两个 secret 换取 GitHub App 令牌再注入GITHUB_TOKEN供脚本调用ghtimeout-minutes: 20限时同时用concurrencycancel-in-progress防止 PR 上堆积冗余运行。与之并行的 legacy E2E需要特别说明ADR 讨论的“E2E 测试”指 GitHub 模式的那套脚本化测试而 legacy 模式对应的 Go E2Etest/e2e/e2e_test.go则通过-short跳过、由专门的make e2ego test -count1 -v -timeout 600s -run ^TestE2E$$ ./test/e2e驱动。从源码结构看该测试覆盖 RunnerSets 与 RunnerDeployments 两类部署方式持续对 runner 做滚动更新并通过verifyActionsWorkflowRun校验真实 GitHub Actions 工作流 run 的结果——与 GHA E2E 的wait_for_run_completion思路同构说明“验证结果而非只看 Pod 数量”已成为整个仓库 E2E 的通用准则。落地总览一份工作流地图将 ADR 的提议与仓库现状对照可以得到 ARC 当前 CI 工作流的最终格局均位于 .github/workflows工作流管辖范围触发路径go.yamlfmt / lint / generate / mocks / test含 manifests diff**.go、go.mod、go.sum、workflow 自身global-run-codeql.yaml安全静态分析替代 govulncheck 职责push、PR、每周定时gha-validate-chart.yamlgha 系列 Chart 的 lint / unittest / Go 模板测试charts/**排除 legacy Chartarc-validate-chart.yamllegacy Chart 的 lint / kind 安装测试charts/**排除 gha 系列arc-validate-runners.yamlrunner 容器脚本 shellcheck 与 entrypoint 启动测试runner/**、test/startup/**gha-e2e-tests.yamlGitHub 模式端到端测试结果级校验push / PR / 手动 dispatch回看这份 2023 年 3 月的 ADR其价值不在于它预言了每一个细节例如 setup-go 最终用到了 v7 而非文中的 v5而在于它确立了一套至今有效的工程原则按技术栈收敛触发、按模式隔离职责、用 diff 兜住所有生成物、用真实运行结果替代间接观测。对任何“一个仓库承载多套实现、多套 CI”的项目这套治理思路都值得直接借鉴。【免费下载链接】actions-runner-controllerKubernetes controller for GitHub Actions self-hosted runners项目地址: https://gitcode.com/GitHub_Trending/ac/actions-runner-controller创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表