.gitlab-ci.yml 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # =============================================================================
  2. # 浠艾福 (XAF) — 最小 CI 管线(GitLab CI,仓库根,自动加载)
  3. # -----------------------------------------------------------------------------
  4. # 落地:2026-11-06(最小 CI 管线搭建)
  5. # 追加:2026-11-07(EVD-6 持续回归:契约 JUnit / langgraph pytest / Playwright e2e / tests jest)
  6. # 重设计:2026-11-07(B-40 修复——CI 软门禁升级为「两级硬门禁」)
  7. # 前置:remote = http://git.iwintrue.com/liaoxg/cfc(GitLab 系自托管)
  8. #
  9. # ███ B-40 两级门禁设计(2026-11-07 落地,替代原 4 个 allow_failure 软门禁)███
  10. # - test-blocking(全绿子集):必须过。任一 blocking job 红 → 流水线红。
  11. # 含 backend-g0-compile / frontend-jest / web-jest / backend-blocking-junit
  12. # / langgraph-blocking-pytest。
  13. # - test-observed(已知红 / 环境依赖):allow_failure: true,失败不拦截,
  14. # 但始终产出 junit 趋势(artifacts when: always),供测试侧逐轮跟踪。
  15. # 含 backend-observed-junit / e2e-playwright-observed / tests-jest-observed。
  16. # - 「integration 包按类白名单逐批转入 blocking」机制:
  17. # * 后端 blocking 用 `-Dtest='!${BACKEND_OBSERVED_CLASSES}'` 排除「已知红类」,
  18. # 其余全跑——即所有**当前全绿**的 integration 类已自动进入 blocking(首批);
  19. # * 每修好一个红类,就从 BACKEND_OBSERVED_CLASSES 移除该类 → 下轮自动转入
  20. # blocking(逐批增量);blocking 覆盖例数随之上升,直至 observed 清空。
  21. # - 覆盖口径:blocking 覆盖 = 全量 − observed(已知红)≥ 当前全量 80%
  22. # (当前红类 33 类 / 红例 115 例 [failures+errors,第 69 轮干净 HEAD 实测],
  23. # blocking 覆盖例数 ≥92%,满足 B-40 闭环条件)。
  24. #
  25. # 🔴 关键坑(必须保留):cfc-backend/pom.xml 第 28 行 <skipTests>true</skipTests>,
  26. # 直接 `mvn test` 会**静默跳过全部测试(假绿)**。因此后端 job 必须显式
  27. # `-DskipTests=false`,并加「0 XML 即失败」守卫(B-39)。
  28. #
  29. # 🔴 启用前置(本机实测,runner 侧待平台核实):
  30. # 1) runner 需 JDK8 + Maven + node18/20/22 + npm + Python3.11+ + 可拉依赖网络
  31. # 2) blocking job 已改 `when: on_success`(自动触发,硬门禁);首次跑通前若
  32. # runner 能力未备,可临时将 blocking job 改回 `when: manual` 人工验证一次,
  33. # 但「红即拦」语义不变(blocking 均未设 allow_failure)。
  34. # 3) 若 runner .m2 已预热且无外网,可在 mvn 命令加 `-o`(离线)
  35. # 4) e2e-playwright-observed 需 runner 具备 chromium 系统依赖(或 root 跑
  36. # `npx playwright install --with-deps chromium`)
  37. # 5) tests-jest-observed 依赖 npm>=10 解析 "npm:@vue/babel-plugin-jsx@^1.1.1"(node22 自带 npm10)
  38. # -----------------------------------------------------------------------------
  39. # 设计说明(G0/G1 分层基线):
  40. # - G0 编译门禁(backend-g0-compile,必绿,最快信号)
  41. # - G1 两级回归(B-40):
  42. # * test-blocking:全绿子集(前端 jest / 管理端 jest / 后端非红类 / langgraph pytest)
  43. # * test-observed:已知红或环境依赖(后端红类 / e2e CI 安全模式 / tests jest)
  44. # - e2e-playwright-observed:**CI 安全模式**——只做 `--list` 收集校验 + headless 冒烟;
  45. # 真实 UI e2e 依赖部署环境 + DB(globalSetup 会重置 192.168.16.251 测试库,
  46. # **严禁**在 CI 开启 playwright.config.js 默认 globalSetup)
  47. # =============================================================================
  48. stages:
  49. - build
  50. - test
  51. variables:
  52. MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  53. # ---------------------------------------------------------------------------
  54. # 后端「已知红类」(test-observed 白名单,逗号分隔的简单类名)。
  55. # 依据:第 69 轮全量实跑(干净 HEAD)红类快照(33 类,surefire-reports 提取)+ B-38 归因;
  56. # 修好一个类就从下方列表移除一个 → 该类下轮自动转入 test-blocking(逐批增量)。
  57. # 说明:`-Dtest='!${BACKEND_OBSERVED_CLASSES}'` 只排除这些类,其余全跑(含全绿 integration 类)。
  58. # ---------------------------------------------------------------------------
  59. BACKEND_OBSERVED_CLASSES: "AdminFamilyOverviewControllerTest,ArticleControllerTest,ArticleWorkflowTest,AuthControllerTest,CfCommissionServiceSettleTest,DailyCheckinStreakFlowTest,DimensionRegistryMapContractTest,DimensionScoreServiceImplTest,EnergySystemFlowTest,FamilyAdminOverviewServiceTest,FamilyControllerTest,FamilyJoinRequestServiceReferrerBindTest,FortuneServiceTest,GrowthRecordSyncFlowTest,GuideFamilyTaskControllerTest,GuideModuleIntegrationTest,GuideOrderControllerTest,GuideRolePermissionTest,GuideTeamControllerTest,MembershipPaySwitchTest,MindFortuneControllerTest,OrchestrationEngineTest,OrchestrationIntegrationTest,PaymentControllerTest,PaymentServiceORDRoutingTest,PlannerBindInviteFlowTest,ProductControllerTest,ProductOrderControllerTest,ProductOrderServiceFulfillmentMultiModeTest,ProductOrderServiceUpdateExpressFieldTest,StatsControllerTest,UserServiceFamilyNameLinkTest,WishControllerTest"
  60. cache:
  61. key: "$CI_COMMIT_REF_SLUG"
  62. paths:
  63. - .m2/repository
  64. - cfc-frontend/node_modules/
  65. - cfc-web/node_modules/
  66. - tests/node_modules/
  67. # ---------------------------------------------------------------------------
  68. # Job 1 — 后端 G0 编译门禁(test-blocking)
  69. # ---------------------------------------------------------------------------
  70. backend-g0-compile:
  71. stage: build
  72. when: on_success # B-40:硬门禁自动触发(红即拦)
  73. cache:
  74. key: "$CI_COMMIT_REF_SLUG"
  75. paths:
  76. - .m2/repository
  77. script:
  78. - cd cfc-backend
  79. # 必须 -DskipTests=false 覆盖 pom 默认假绿(pom.xml:28)
  80. - mvn test-compile -DskipTests=false
  81. artifacts:
  82. when: always
  83. expire_in: 7 days
  84. paths:
  85. - cfc-backend/target/classes/
  86. # ---------------------------------------------------------------------------
  87. # Job 2 — 小程序前端(cfc-frontend)jest 单测(test-blocking)
  88. # ---------------------------------------------------------------------------
  89. frontend-jest:
  90. stage: test
  91. when: on_success # B-40:硬门禁自动触发(红即拦)
  92. cache:
  93. key: "$CI_COMMIT_REF_SLUG"
  94. paths:
  95. - cfc-frontend/node_modules/
  96. script:
  97. - cd cfc-frontend
  98. - npm ci || npm install
  99. - npx jest --ci
  100. artifacts:
  101. when: always
  102. expire_in: 7 days
  103. reports:
  104. junit:
  105. - cfc-frontend/junit.xml
  106. paths:
  107. - cfc-frontend/coverage/
  108. # ---------------------------------------------------------------------------
  109. # Job 3 — 管理端(cfc-web)jest 单测(test-blocking)
  110. # ---------------------------------------------------------------------------
  111. web-jest:
  112. stage: test
  113. when: on_success # B-40:硬门禁自动触发(红即拦)
  114. cache:
  115. key: "$CI_COMMIT_REF_SLUG"
  116. paths:
  117. - cfc-web/node_modules/
  118. script:
  119. - cd cfc-web
  120. - npm ci || npm install
  121. - npx jest --ci
  122. artifacts:
  123. when: always
  124. expire_in: 7 days
  125. reports:
  126. junit:
  127. - cfc-web/junit.xml
  128. paths:
  129. - cfc-web/coverage/
  130. # ---------------------------------------------------------------------------
  131. # Job 4 — 后端契约/单测 JUnit · test-blocking(全绿子集,必须过)
  132. # ---------------------------------------------------------------------------
  133. # - 必须 -DskipTests=false 覆盖 pom:28 假绿(否则 mvn test 静默跳测 = 假绿)
  134. # - 仅排除 BACKEND_OBSERVED_CLASSES(已知红类);其余全跑,**含全绿 integration 类**,
  135. # 即「integration 按类白名单逐批转入 blocking」的首批。
  136. # - B-39 守卫:surefire 0 个 XML 视为假绿,直接失败(红即拦)。
  137. # ---------------------------------------------------------------------------
  138. backend-blocking-junit:
  139. stage: test
  140. when: on_success # B-40:硬门禁自动触发(红即拦)
  141. cache:
  142. key: "$CI_COMMIT_REF_SLUG"
  143. paths:
  144. - .m2/repository
  145. script:
  146. - cd cfc-backend
  147. # 必须 -DskipTests=false(pom.xml:28 默认 true = 假绿);只排除已知红类,其余全跑
  148. - mvn -B test -DskipTests=false "-Dtest=!${BACKEND_OBSERVED_CLASSES}"
  149. # B-39 守卫:0 个 surefire XML = 零执行假绿 → 失败
  150. - test "$(find target/surefire-reports -name 'TEST-*.xml' | wc -l)" -gt 0 || (echo "B-39 假绿守卫:surefire 未产出任何 TEST-*.xml" && exit 1)
  151. artifacts:
  152. when: always
  153. expire_in: 7 days
  154. reports:
  155. junit:
  156. - cfc-backend/target/surefire-reports/TEST-*.xml
  157. paths:
  158. - cfc-backend/target/surefire-reports/
  159. # ---------------------------------------------------------------------------
  160. # Job 4b — 后端「已知红类」JUnit · test-observed(allow_failure,产出趋势)
  161. # ---------------------------------------------------------------------------
  162. # - 只跑 BACKEND_OBSERVED_CLASSES(红类),allow_failure 不拦截;
  163. # 每轮产生 junit 供测试侧看红例数是否下降(趋势)。
  164. # - 修好一个类 → 从 BACKEND_OBSERVED_CLASSES 移除 → 下轮自动转入 blocking。
  165. # ---------------------------------------------------------------------------
  166. backend-observed-junit:
  167. stage: test
  168. when: on_success
  169. allow_failure: true # 已知红,失败不拦截,仅产出趋势
  170. cache:
  171. key: "$CI_COMMIT_REF_SLUG"
  172. paths:
  173. - .m2/repository
  174. script:
  175. - cd cfc-backend
  176. - mvn -B test -DskipTests=false "-Dtest=${BACKEND_OBSERVED_CLASSES}"
  177. after_script:
  178. - echo "observed 红类趋势见 surefire-reports(红例数应逐轮下降)" || true
  179. artifacts:
  180. when: always
  181. expire_in: 7 days
  182. reports:
  183. junit:
  184. - cfc-backend/target/surefire-reports/TEST-*.xml
  185. paths:
  186. - cfc-backend/target/surefire-reports/
  187. # ---------------------------------------------------------------------------
  188. # Job 5 — cfc-langgraph pytest · test-blocking(B-34 闭环后全绿,硬门禁)
  189. # ---------------------------------------------------------------------------
  190. # - conftest 自动 import app.main(需全量依赖,requirements_frozen.txt 锁版本)
  191. # - --ignore=tests/qna(test_graph.py 引用不存在的 src.qna → collection error)
  192. # - B-34/B-35/B-37 修复后 67 轮起实测 51 passed / 12 skipped / 0 failed → 转硬门禁。
  193. # - 注意:tests_contract/ 下的 B-41 flaky(TestGrowthReportInterpretB04)不在本 job
  194. # 的 `tests/` 范围内,不影响门禁;该 flaky 由测试侧单独跟踪(B-41)。
  195. # ---------------------------------------------------------------------------
  196. langgraph-blocking-pytest:
  197. stage: test
  198. when: on_success # B-40:硬门禁自动触发(红即拦)
  199. cache:
  200. key: "$CI_COMMIT_REF_SLUG"
  201. paths:
  202. - cfc-langgraph/.venv/
  203. script:
  204. - cd cfc-langgraph
  205. - python3 -m venv .venv || true
  206. - . .venv/bin/activate
  207. - pip install -q -r requirements_frozen.txt
  208. - python -m pytest tests/ -v --ignore=tests/qna --junitxml=junit.xml -p no:cacheprovider
  209. artifacts:
  210. when: always
  211. expire_in: 7 days
  212. reports:
  213. junit:
  214. - cfc-langgraph/junit.xml
  215. paths:
  216. - cfc-langgraph/junit.xml
  217. # ---------------------------------------------------------------------------
  218. # Job 6 — Playwright e2e(CI 安全模式)· test-observed(allow_failure,产出趋势)
  219. # ---------------------------------------------------------------------------
  220. # ⚠️ 严禁在 CI 使用 tests/playwright.config.js(其 globalSetup 会重置远程
  221. # 192.168.16.251 测试库)。CI 采用安全模式:
  222. # 1) `npx playwright test --list` — 校验 74 spec / 2418 用例可收集
  223. # 2) `-c playwright.config.smoke.js ci-smoke` — headless chromium 真实拉起冒烟
  224. # 真实 UI e2e 依赖部署环境 + DB(cfc.iwintrue.com),由测试负责人手动跑。
  225. # B-40:真实 e2e 未授权/环境依赖 → 归 test-observed(allow_failure),
  226. # 但 test-results 每轮归档供趋势跟踪。
  227. # ---------------------------------------------------------------------------
  228. e2e-playwright-observed:
  229. stage: test
  230. when: on_success
  231. allow_failure: true # 环境依赖(部署环境+DB),失败不拦截,仅产出趋势
  232. cache:
  233. key: "$CI_COMMIT_REF_SLUG"
  234. paths:
  235. - tests/node_modules/
  236. script:
  237. - cd tests
  238. - npm ci || npm install
  239. - npx playwright install chromium || true
  240. # CI 安全模式:只做收集校验 + headless 冒烟(均不触发远程 DB 重置)
  241. - npx playwright test --list
  242. - npx playwright test e2e/ci-smoke.spec.js -c playwright.config.smoke.js
  243. artifacts:
  244. when: always
  245. expire_in: 7 days
  246. paths:
  247. - tests/test-results/
  248. # ---------------------------------------------------------------------------
  249. # Job 7 — tests/ 目录 jest(unit + integration)· test-observed(allow_failure)
  250. # ---------------------------------------------------------------------------
  251. # - tests/jest.config.js 依赖 cfc-frontend/__tests__/setup.js(vue),
  252. # 需先装 cfc-frontend 依赖;npm>=10 才能解析 "npm:@vue/babel-plugin-jsx@^1.1.1"
  253. # 本机 npm9.2.0 验证失败(node22 自带 npm10 可装)
  254. # - integration 部分需后端 API(localhost:9082)在 CI 无后端时仅跑能跑的;
  255. # 当前 B-36 下根级黑盒 23 例全 skip → 归 test-observed(allow_failure)。
  256. # ---------------------------------------------------------------------------
  257. tests-jest-observed:
  258. stage: test
  259. when: on_success
  260. allow_failure: true # 需 npm10 + cfc-frontend(vue) 依赖;integration 需后端;B-36 全 skip
  261. cache:
  262. key: "$CI_COMMIT_REF_SLUG"
  263. paths:
  264. - tests/node_modules/
  265. - cfc-frontend/node_modules/
  266. script:
  267. - cd cfc-frontend && npm ci || npm install # setup.js 需要 vue
  268. - cd ../tests && npm ci || npm install
  269. - npx jest --ci