ECC Quarkus 验证循环实战指南:用十阶段流水线把好 PR 与发布前的质量关
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
本文系统讲解 ECC 仓库中以 [quarkus-verification](https://link.gitcode.com/i/613f1e0d335255f06a7d71138a6fe356) 技能沉淀的 Quarkus 验证循环(Verification Loop):它把「构建 → 静态分析 → 测试与覆盖率 → 安全扫描 → 原生编译 → 性能 → 健康检查 → 镜像 → 配置 → 文档」组织成一份可随时复用的阶段化检查清单,供开发者在打开 Pull Request、完成大型重构、升级依赖以及向 staging/生产环境发布前按序执行。读完本文,你将能复刻出这套端到端质量门禁,并把关键阶段自动化进 CI/CD。
技能定位:一份面向 Agent 与人的可执行质量清单
quarkus-verification是 ECC 技能体系中面向 Quarkus 项目的验证技能(skill),其定义元数据(front matter)明确其用途为:Verification loop for Quarkus projects: build, static analysis, tests with coverage, security scans, native compilation, and diff review before release or PR(见 SKILL.md 源文件)。
在 ECC 的目录组织里,它与同族技能形成互补闭环:
- quarkus-patterns:讲解 Quarkus 惯用编码模式;
- quarkus-tdd:聚焦测试驱动开发流程;
- quarkus-security:专注安全配置与防护;
- quarkus-verification:负责交付前的整体验证与放行判断。
manifests/install-modules.json 将skills/quarkus-verification与上述兄弟技能一并编入安装模块清单,说明该技能被当作可随 ECC 一起安装的「标准验证闸门」对外提供;本仓库同时维护了 西班牙语版、日文版 与 土耳其文版 等多语言译本,方便不同语种的开发者或 Agent 直接加载使用。
需要特别说明的是:ECC 是一个技能与自动化仓库,本文所有命令都假定你已进入某个真正的 Quarkus 工程目录(包含pom.xml或build.gradle)执行,并具备对应的 JDK、Maven/Gradle、GraalVM 或容器环境。该技能给出的是放行门槛,而非空谈的最佳实践。
何时激活验证循环
技能的适用时机非常明确(对应 西班牙语文档 的 "Cuándo Activar"):
- 为某个 Quarkus 服务打开 Pull Request 之前;
- 大型重构或依赖升级完成之后;
- 向 staging 或生产环境部署前的预发布验证;
- 需要完整跑一遍 build → lint → test → 安全扫描 → 原生编译流水线;
- 需要确认测试覆盖率满足 80%+ 门槛;
- 需要验证 GraalVM 原生镜像兼容性。
一句话概括触发原则:凡是代码要离开本地、进入评审或生产通道之前,先跑一遍这个循环。
阶段一:构建(Build)
任何验证都从「能否编译通过」开始。技能给出 Maven 与 Gradle 两套等价命令:
# Maven:跳过测试,只验证编译与打包链路 mvn clean verify -DskipTests # Gradle:跳过测试的等价目标 ./gradlew clean assemble -x test两个要点值得展开:
- 这里刻意先跳过测试,是因为验证循环要求问题分层定位——先确认编译与资源处理没有错误,再进入测试阶段,避免「编译失败」与「测试失败」混在一起互相干扰;
mvn clean verify不止执行编译,还会顺带触发 Maven 生命周期中verify之前的插件(如 surefire/failsafe 之前绑定到 verify 阶段的各插件)。技能明确强调:若构建失败,立即停止,先修复编译错误,不要带着红构建继续往下走。
阶段二:静态分析(Static Analysis)
构建通过后,用静态分析工具在「不运行程序」的前提下扫描代码异味与潜在缺陷。
Maven 工具链:Checkstyle、PMD、SpotBugs
mvn checkstyle:check pmd:check spotbugs:check三个工具职责互补,实际使用时可结合 pom 中对应插件的配置调整规则集与 failOnError 行为:
- Checkstyle:检查编码风格与约定一致性(缩进、命名、import 顺序等);
- PMD:检测反模式与复杂代码,如过高的圈复杂度(cyclomatic complexity);
- SpotBugs:做字节码级缺陷分析,能发现空指针解引用、资源未关闭等真实 Bug。
SonarQube(可选,若已配置)
mvn sonar:sonar \ -Dsonar.projectKey=my-quarkus-project \ -Dsonar.host.url=http://localhost:9000 \ -Dsonar.login=${SONAR_TOKEN}注意:这里的projectKey需替换为你自己的工程标识,SONAR_TOKEN应来自环境变量或 CI 密钥库而非硬编码。
静态分析阶段应解决的常见问题
技能列出四类高优先问题,这些问题也应是 Code Review 时人工关注的同类信号:
- 未使用的 import 或变量;
- 复杂方法(高圈复杂度);
- 潜在空指针解引用;
- SpotBugs 标记的安全问题。
阶段三:测试与覆盖率(Tests + Coverage)
这是整个循环里最重的阶段。先看命令,再看三种测试形态的写法。
命令与覆盖率门槛
# 运行全部测试 mvn clean test # 生成覆盖率报告 mvn jacoco:report # 强制覆盖率门槛(80%) mvn jacoco:check # 或 Gradle 等价目标 ./gradlew test jacocoTestReport jacocoTestCoverageVerification覆盖率报告生成于target/site/jacoco/index.html,技能定义的验收口径是:
- 行覆盖率(line coverage)目标80%+;
- 分支覆盖率(branch coverage)目标70%+;
- 重点识别未被覆盖的关键路径(如认证分支、异常路径、边界条件)。
(80%/70% 是技能文档设定的默认目标,团队可按业务风险自行调整jacoco:check中的 thresholds 配置。)
单元测试:Mock 依赖、验证交互
技能用 Quarkus 生态中最常见的组合——Mockito + AssertJ——示范服务层测试。注释特别点出一个 Panache 细节:persist()返回 void,因此要用doNothing().when(...)打桩 +verify(...)验证交互:
@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock UserRepository userRepository; @InjectMocks UserService userService; @Test void createUser_validInput_returnsUser() { var dto = new CreateUserDto("Alice", "alice@example.com"); doNothing().when(userRepository).persist(any(User.class)); User result = userService.create(dto); assertThat(result.name).isEqualTo("Alice"); verify(userRepository).persist(any(User.class)); } }集成测试:真实数据库(Testcontainers)
集成测试用@QuarkusTest拉起完整 Quarkus 运行时,并通过@QuarkusTestResource注入真实数据库(如 PostgresTestResource,内部通常基于 Testcontainers 启动容器):
@QuarkusTest @QuarkusTestResource(PostgresTestResource.class) class UserRepositoryIntegrationTest { @Inject UserRepository userRepository; @Test @Transactional void findByEmail_existingUser_returnsUser() { User user = new User(); user.name = "Alice"; user.email = "alice@example.com"; userRepository.persist(user); Optional<User> found = userRepository.findByEmail("alice@example.com"); assertThat(found).isPresent(); assertThat(found.get().name).isEqualTo("Alice"); } }要点:@Transactional确保测试数据在事务内回滚,多个用例之间互不污染。
API 测试:REST Assured 打接口
API 测试同样基于@QuarkusTest,用 REST Assured 直接对 HTTP 端点断言状态码与响应体,覆盖正常路径与校验失败路径两个方向:
@QuarkusTest class UserResourceTest { @Test void createUser_validInput_returns201() { given() .contentType(ContentType.JSON) .body(""" {"name": "Alice", "email": "alice@example.com"} """) .when().post("/api/users") .then() .statusCode(201) .body("name", equalTo("Alice")); } @Test void createUser_invalidEmail_returns400() { given() .contentType(ContentType.JSON) .body(""" {"name": "Alice", "email": "invalid"} """) .when().post("/api/users") .then() .statusCode(400); } }注意 Java 15+ 文本块(""")在测试中的使用,能让 JSON 请求体保持可读。这三类测试分别对应三份不同的保障:单测锁定业务逻辑、集成测试锁定数据访问、API 测试锁定契约与输入校验。
阶段四:安全扫描(Security Scanning)
安全不是事后补救,而是循环中独立的一整阶段,包含依赖漏洞、扩展审计与 API 渗透三个层次。
依赖漏洞扫描(OWASP Dependency-Check)
mvn org.owasp:dependency-check-maven:check扫描结果落在target/dependency-check-report.html,按 CVE 编号列出存在已知漏洞的依赖及可升级版本。
Quarkus 扩展安全审计
# 检查存在已知问题的扩展版本 mvn quarkus:audit # 列出当前工程启用的全部扩展,便于核对最小化原则 mvn quarkus:list-extensionsquarkus:audit会对照 Quarkus 官方的扩展漏洞情报检查工程依赖;list-extensions帮助你发现是否引入了超出业务必需、从而扩大攻击面的扩展。
OWASP ZAP 对 OpenAPI 的 API 渗透测试
docker run -t ghcr.io/zaproxy/zaproxy:stable zap-api-scan.py \ -t http://localhost:8080/q/openapi \ -f openapiQuarkus 的smallrye-openapi扩展会自动暴露/q/openapi端点,ZAP 直接以 OpenAPI 契约文件作为扫描入口(-f openapi),对全部声明的接口做主动安全测试。前提是应用已在localhost:8080运行且 OpenAPI 端点已启用。
人工安全核对清单
技能同时给出八条任何 Quarkus 服务上线前都该过一遍的检查项:
- 所有密钥放入环境变量(绝不写进代码/配置文件入库);
- 所有端点都有输入校验(Bean Validation:
@Valid、@NotNull、@Email等); - 已配置认证/授权(如
quarkus-oidc、quarkus-security-jpa); - CORS 正确配置(按环境限定 origin 白名单);
- 安全响应头已设置(如
quarkus.http.header.*或 Vert.x 路由过滤器); - 密码使用 BCrypt 哈希(
quarkus-security-jpa默认即 BCrypt); - 防 SQL 注入:统一走 Panache/JPA 参数化查询,不用字符串拼接;
- 公开端点配置了速率限制。
阶段五:GraalVM 原生编译(Native Compilation)
Quarkus 的核心卖点之一是秒级启动的原生镜像。这一阶段专门验证「能否从 JVM 形态编译为原生可执行文件」。
编译与冒烟测试
# 构建原生可执行文件 mvn package -Dnative # 或借助容器构建(本地无需安装 GraalVM) mvn package -Dnative -Dquarkus.native.container-build=true # 直接运行原生可执行文件 ./target/*-runner # 基础冒烟测试 curl http://localhost:8080/q/health/live curl http://localhost:8080/q/health/ready-Dquarkus.native.container-build=true是本阶段最实用的开关:它让构建在容器内完成,本地只要装 Docker,无需安装完整 GraalVM 与 native-image 工具链。
三类高频踩坑与对策
技能总结了三类「JVM 上好好的、一打原生镜像就挂」的经典问题:
| 问题 | 现象 | 对策 |
|---|---|---|
| Reflection | 运行时ClassNotFoundException/ 属性丢失 | 为动态加载的类补充反射配置 |
| Resources | 运行时读不到 classpath 资源 | 用quarkus.native.resources.includes显式包含 |
| JNI | 调用本地库失败 | 使用原生库时注册 JNI 类 |
反射问题的标准解法是@RegisterForReflection,把运行期会被反射触达的类在编译期登记进镜像:
@RegisterForReflection(targets = {MyDynamicClass.class}) public class ReflectionConfiguration {}阶段六:性能测试(Performance Testing)
发布前用轻量压测工具 K6 验证接口在持续负载下的表现,而不是只测「单次调用快不快」。
// load-test.js import http from 'k6/http'; import { check } from 'k6'; export const options = { stages: [ { duration: '30s', target: 50 }, // 30 秒内爬升到 50 并发 { duration: '1m', target: 100 }, // 维持 100 并发 1 分钟 { duration: '30s', target: 0 }, // 30 秒内降到 0 ], }; export default function () { const res = http.get('http://localhost:8080/api/markets'); check(res, { 'status is 200': (r) => r.status === 200, 'response time < 200ms': (r) => r.timings.duration < 200, }); }k6 run load-test.jsstages的阶梯式并发设计(爬坡 → 峰值 → 回落)能暴露「并发升高后延迟劣化、错误率抬升」这类冷启动/连接池问题。技能强调压测要看趋势而非单点,英文原版进一步明确了应持续跟踪的核心指标:响应时间分位值 p50/p95/p99、吞吐量(req/s)、错误率、内存与 CPU 占用。示例中的/api/markets与 200ms 阈值需替换为你自己的端点与 SLO。
阶段七:健康检查(Health Checks)
部署后第一时间验证服务「是否活着、是否就绪」:
# Liveness:进程是否存活 curl http://localhost:8080/q/health/live # Readiness:是否可接收流量(依赖如 DB 是否就绪) curl http://localhost:8080/q/health/ready # 全部健康检查聚合视图 curl http://localhost:8080/q/health # 指标端点(若已启用 microprofile-metrics) curl http://localhost:8080/q/metrics健康检查在 Quarkus 中默认挂载在/q管理根路径下,live与ready的拆分让 K8s 探针可以分别配置 livenessProbe 与 readinessProbe。技能英文版补充了预期响应的结构,用于在脚本中做断言:
{ "status": "UP", "checks": [ { "name": "Database connection", "status": "UP" } ] }若加入quarkus-smallrye-health的数据库健康检查,数据库不可用时checks中对应项即为DOWN,ready整体状态也会随之翻转为DOWN。
阶段八:容器镜像构建与镜像安全扫描
# 构建容器镜像 mvn package -Dquarkus.container-image.build=true # 指定仓库、组与 tag mvn package \ -Dquarkus.container-image.build=true \ -Dquarkus.container-image.registry=docker.io \ -Dquarkus.container-image.group=myorg \ -Dquarkus.container-image.tag=1.0.0 # 本地运行容器做最后验证 docker run -p 8080:8080 myorg/my-quarkus-app:1.0.0镜像构建后,还要对镜像本身做一次安全扫描,捕捉基础镜像或安装层引入的漏洞:
# Trivy trivy image myorg/my-quarkus-app:1.0.0 # Grype(Anchore 出品) grype myorg/my-quarkus-app:1.0.0这里两个扫描器二选一即可,重点是扫描对象从「依赖清单」升级为「可交付产物镜像」——依赖漏洞扫描(阶段四)与镜像扫描(阶段八)覆盖的是攻击面的不同位置。
阶段九:配置验证(Configuration Validation)
“在别处能跑”不等于“在目标环境能跑”。Quarkus 的配置体系(application.properties+ 环境变量 + 系统属性分层覆盖)很容易出现「本地默认值上线后覆盖错误」的问题,因此技能要求显式验证配置:
# 打印工程全部配置属性与来源 mvn quarkus:info # 运行时查看生效中的配置项来源(dev 模式端点) curl http://localhost:8080/q/dev/io.quarkus.quarkus-vertx-http/config按环境逐项核对清单:
- 数据库 URL 按环境配置(不要共享本地
localhost默认值); - 密钥外部化(Vault、环境变量、K8s Secret),不进
application.properties版本库; - 日志级别与生产环境匹配(生产不打印 DEBUG);
- CORS 来源按环境白名单设置;
- 速率限制已配置;
- 监控/链路追踪(Tracing)已启用。
阶段十:文档审查(Documentation Review)
代码、行为与文档三者一旦脱节,接手的下一个开发者(或 Agent)就会踩坑。收尾阶段检查:
- OpenAPI/Swagger 文档是最新的(查看
/q/swagger-ui); - README 包含完整的环境配置说明;
- API 变更已记录;
- 破坏性变更(breaking changes)有迁移指南;
- 配置属性已有文档说明。
并导出 OpenAPI 契约留档:
curl http://localhost:8080/q/openapi -o openapi.json该文件既是 Swagger UI 的数据源,也是前端、测试与文档自动化的单一事实来源。
综合放行清单(Verification Checklist)
技能在十个阶段之外,把验收标准收敛成四组「上线前勾选」清单:
代码质量
- 构建无警告通过
- 静态分析干净(无 high/medium 级问题)
- 代码符合团队约定
- PR 中无注释掉的代码与 TODO 遗留
测试
- 全部测试通过
- 代码覆盖率 ≥ 80%
- 集成测试使用真实数据库
- 安全测试通过
- 性能在可接受范围
安全
- 依赖无已知漏洞
- 认证/授权已测试
- 输入校验完整
- 源码中无密钥
- 安全响应头已配置
部署
- 原生编译成功
- 容器镜像构建成功
- 健康检查响应正常
- 目标环境配置有效
- 原生可执行文件可构建、原生测试通过、启动时间 < 100ms、内存占用可接受(英文原版对原生镜像补充的验收项,见 SKILL.md)
一键跑完全流程:自动化验证脚本
把十个阶段串成单个脚本,适合本地交付前与 CI 中复用(技能原文完整收录于 SKILL.md):
#!/bin/bash set -e echo "=== Fase 1: Build ===" mvn clean verify -DskipTests echo "=== Fase 2: Static Analysis ===" mvn checkstyle:check pmd:check spotbugs:check echo "=== Fase 3: Tests + Coverage ===" mvn test jacoco:report jacoco:check echo "=== Fase 4: Security Scan ===" mvn org.owasp:dependency-check-maven:check echo "=== Fase 5: Native Compilation ===" mvn package -Dnative -Dquarkus.native.container-build=true echo "=== All Phases Complete ===" echo "Review reports:" echo " - Coverage: target/site/jacoco/index.html" echo " - Security: target/dependency-check-report.html" echo " - Native: target/*-runner"set -e保证任一阶段失败立即中断——验证循环的设计哲学就是尽早失败、就地修复,而不是把问题一路带进生产。
接入 CI/CD:GitHub Actions 示例
本地脚本化之后,进一步把核心阶段搬进 CI(英文原版提供的 GitHub Actions 工作流,见 SKILL.md),让每次 push / PR 自动触发:
name: Verification on: [push, pull_request] jobs: verify: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - name: Set up JDK 21 uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Cache Maven packages uses: actions/cache@v6 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - name: Build run: mvn clean verify -DskipTests - name: Test with Coverage run: mvn test jacoco:report jacoco:check - name: Security Scan run: mvn org.owasp:dependency-check-maven:check - name: Upload Coverage uses: codecov/codecov-action@v7 with: token: ${{ secrets.CODECOV_TOKEN }} files: target/site/jacoco/jacoco.xml三点说明:工作流展示的是构建、覆盖率门槛与依赖安全扫描这三个在云端可稳定复现的阶段;原生编译与 ZAP 渗透测试这类重活建议按需在带 Docker 的 runner 上单独开启;JDK 版本与 actions 版本号应根据你的工程实际锁定。
最佳实践与使用提醒
技能文档(英文版、西班牙语版)收束的日常纪律,恰好也是把它用好最关键的习惯:
- 每次 PR 前运行验证循环;
- 在 CI/CD 流水线中自动化(而非仅靠人肉回忆);
- 发现问题立即修复,不积累技术债;
- 保持覆盖率高于 80%;
- 定期升级依赖并复查安全扫描结果;
- 周期性执行原生编译测试(防止「临发布才发现打不了 native」);
- 持续监控性能趋势(单次压测的数字会过时,趋势不会);
- 文档化破坏性变更;
- 为每个目标环境单独做配置校验。
最后提醒两点边界:其一,本技能是给Quarkus 工程用的验证脚本,ECC 仓库本身只承载技能定义与多语言译本,并不包含可运行的 Quarkus 示例工程,实际执行时请在你的 Quarkus 项目根目录运行;其二,文中所有命令涉及的项目名(如my-quarkus-project、myorg/my-quarkus-app)、端点路径(/api/markets)与性能阈值均为占位示例,请替换为你工程的真实值与既定 SLO。把这份十阶段循环当作发布前放行的默认基线,你的 PR 与上线将不再依赖运气。
【免费下载链接】ECCThe agent harness performance optimization system. Skills, instincts, memory, security, and research-first development for Claude Code, Codex, Opencode, Cursor and beyond.项目地址: https://gitcode.com/GitHub_Trending/ev/ECC
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考