# ============================================================================= # 浠艾福 (XAF) — 最小 CI 管线(GitLab CI,仓库根,自动加载) # ----------------------------------------------------------------------------- # 落地:2026-11-06(最小 CI 管线搭建) # 追加:2026-11-07(EVD-6 持续回归:契约 JUnit / langgraph pytest / Playwright e2e / tests jest) # 重设计:2026-11-07(B-40 修复——CI 软门禁升级为「两级硬门禁」) # 前置:remote = http://git.iwintrue.com/liaoxg/cfc(GitLab 系自托管) # # ███ B-40 两级门禁设计(2026-11-07 落地,替代原 4 个 allow_failure 软门禁)███ # - test-blocking(全绿子集):必须过。任一 blocking job 红 → 流水线红。 # 含 backend-g0-compile / frontend-jest / web-jest / backend-blocking-junit # / langgraph-blocking-pytest。 # - test-observed(已知红 / 环境依赖):allow_failure: true,失败不拦截, # 但始终产出 junit 趋势(artifacts when: always),供测试侧逐轮跟踪。 # 含 backend-observed-junit / e2e-playwright-observed / tests-jest-observed。 # - 「integration 包按类白名单逐批转入 blocking」机制: # * 后端 blocking 用 `-Dtest='!${BACKEND_OBSERVED_CLASSES}'` 排除「已知红类」, # 其余全跑——即所有**当前全绿**的 integration 类已自动进入 blocking(首批); # * 每修好一个红类,就从 BACKEND_OBSERVED_CLASSES 移除该类 → 下轮自动转入 # blocking(逐批增量);blocking 覆盖例数随之上升,直至 observed 清空。 # - 覆盖口径:blocking 覆盖 = 全量 − observed(已知红)≥ 当前全量 80% # (当前红类 33 类 / 红例 115 例 [failures+errors,第 69 轮干净 HEAD 实测], # blocking 覆盖例数 ≥92%,满足 B-40 闭环条件)。 # # 🔴 关键坑(必须保留):cfc-backend/pom.xml 第 28 行 true, # 直接 `mvn test` 会**静默跳过全部测试(假绿)**。因此后端 job 必须显式 # `-DskipTests=false`,并加「0 XML 即失败」守卫(B-39)。 # # 🔴 启用前置(本机实测,runner 侧待平台核实): # 1) runner 需 JDK8 + Maven + node18/20/22 + npm + Python3.11+ + 可拉依赖网络 # 2) blocking job 已改 `when: on_success`(自动触发,硬门禁);首次跑通前若 # runner 能力未备,可临时将 blocking job 改回 `when: manual` 人工验证一次, # 但「红即拦」语义不变(blocking 均未设 allow_failure)。 # 3) 若 runner .m2 已预热且无外网,可在 mvn 命令加 `-o`(离线) # 4) e2e-playwright-observed 需 runner 具备 chromium 系统依赖(或 root 跑 # `npx playwright install --with-deps chromium`) # 5) tests-jest-observed 依赖 npm>=10 解析 "npm:@vue/babel-plugin-jsx@^1.1.1"(node22 自带 npm10) # ----------------------------------------------------------------------------- # 设计说明(G0/G1 分层基线): # - G0 编译门禁(backend-g0-compile,必绿,最快信号) # - G1 两级回归(B-40): # * test-blocking:全绿子集(前端 jest / 管理端 jest / 后端非红类 / langgraph pytest) # * test-observed:已知红或环境依赖(后端红类 / e2e CI 安全模式 / tests jest) # - e2e-playwright-observed:**CI 安全模式**——只做 `--list` 收集校验 + headless 冒烟; # 真实 UI e2e 依赖部署环境 + DB(globalSetup 会重置 192.168.16.251 测试库, # **严禁**在 CI 开启 playwright.config.js 默认 globalSetup) # ============================================================================= stages: - build - test variables: MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository" # --------------------------------------------------------------------------- # 后端「已知红类」(test-observed 白名单,逗号分隔的简单类名)。 # 依据:第 69 轮全量实跑(干净 HEAD)红类快照(33 类,surefire-reports 提取)+ B-38 归因; # 修好一个类就从下方列表移除一个 → 该类下轮自动转入 test-blocking(逐批增量)。 # 说明:`-Dtest='!${BACKEND_OBSERVED_CLASSES}'` 只排除这些类,其余全跑(含全绿 integration 类)。 # --------------------------------------------------------------------------- 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" cache: key: "$CI_COMMIT_REF_SLUG" paths: - .m2/repository - cfc-frontend/node_modules/ - cfc-web/node_modules/ - tests/node_modules/ # --------------------------------------------------------------------------- # Job 1 — 后端 G0 编译门禁(test-blocking) # --------------------------------------------------------------------------- backend-g0-compile: stage: build when: on_success # B-40:硬门禁自动触发(红即拦) cache: key: "$CI_COMMIT_REF_SLUG" paths: - .m2/repository script: - cd cfc-backend # 必须 -DskipTests=false 覆盖 pom 默认假绿(pom.xml:28) - mvn test-compile -DskipTests=false artifacts: when: always expire_in: 7 days paths: - cfc-backend/target/classes/ # --------------------------------------------------------------------------- # Job 2 — 小程序前端(cfc-frontend)jest 单测(test-blocking) # --------------------------------------------------------------------------- frontend-jest: stage: test when: on_success # B-40:硬门禁自动触发(红即拦) cache: key: "$CI_COMMIT_REF_SLUG" paths: - cfc-frontend/node_modules/ script: - cd cfc-frontend - npm ci || npm install - npx jest --ci artifacts: when: always expire_in: 7 days reports: junit: - cfc-frontend/junit.xml paths: - cfc-frontend/coverage/ # --------------------------------------------------------------------------- # Job 3 — 管理端(cfc-web)jest 单测(test-blocking) # --------------------------------------------------------------------------- web-jest: stage: test when: on_success # B-40:硬门禁自动触发(红即拦) cache: key: "$CI_COMMIT_REF_SLUG" paths: - cfc-web/node_modules/ script: - cd cfc-web - npm ci || npm install - npx jest --ci artifacts: when: always expire_in: 7 days reports: junit: - cfc-web/junit.xml paths: - cfc-web/coverage/ # --------------------------------------------------------------------------- # Job 4 — 后端契约/单测 JUnit · test-blocking(全绿子集,必须过) # --------------------------------------------------------------------------- # - 必须 -DskipTests=false 覆盖 pom:28 假绿(否则 mvn test 静默跳测 = 假绿) # - 仅排除 BACKEND_OBSERVED_CLASSES(已知红类);其余全跑,**含全绿 integration 类**, # 即「integration 按类白名单逐批转入 blocking」的首批。 # - B-39 守卫:surefire 0 个 XML 视为假绿,直接失败(红即拦)。 # --------------------------------------------------------------------------- backend-blocking-junit: stage: test when: on_success # B-40:硬门禁自动触发(红即拦) cache: key: "$CI_COMMIT_REF_SLUG" paths: - .m2/repository script: - cd cfc-backend # 必须 -DskipTests=false(pom.xml:28 默认 true = 假绿);只排除已知红类,其余全跑 - mvn -B test -DskipTests=false "-Dtest=!${BACKEND_OBSERVED_CLASSES}" # B-39 守卫:0 个 surefire XML = 零执行假绿 → 失败 - test "$(find target/surefire-reports -name 'TEST-*.xml' | wc -l)" -gt 0 || (echo "B-39 假绿守卫:surefire 未产出任何 TEST-*.xml" && exit 1) artifacts: when: always expire_in: 7 days reports: junit: - cfc-backend/target/surefire-reports/TEST-*.xml paths: - cfc-backend/target/surefire-reports/ # --------------------------------------------------------------------------- # Job 4b — 后端「已知红类」JUnit · test-observed(allow_failure,产出趋势) # --------------------------------------------------------------------------- # - 只跑 BACKEND_OBSERVED_CLASSES(红类),allow_failure 不拦截; # 每轮产生 junit 供测试侧看红例数是否下降(趋势)。 # - 修好一个类 → 从 BACKEND_OBSERVED_CLASSES 移除 → 下轮自动转入 blocking。 # --------------------------------------------------------------------------- backend-observed-junit: stage: test when: on_success allow_failure: true # 已知红,失败不拦截,仅产出趋势 cache: key: "$CI_COMMIT_REF_SLUG" paths: - .m2/repository script: - cd cfc-backend - mvn -B test -DskipTests=false "-Dtest=${BACKEND_OBSERVED_CLASSES}" after_script: - echo "observed 红类趋势见 surefire-reports(红例数应逐轮下降)" || true artifacts: when: always expire_in: 7 days reports: junit: - cfc-backend/target/surefire-reports/TEST-*.xml paths: - cfc-backend/target/surefire-reports/ # --------------------------------------------------------------------------- # Job 5 — cfc-langgraph pytest · test-blocking(B-34 闭环后全绿,硬门禁) # --------------------------------------------------------------------------- # - conftest 自动 import app.main(需全量依赖,requirements_frozen.txt 锁版本) # - --ignore=tests/qna(test_graph.py 引用不存在的 src.qna → collection error) # - B-34/B-35/B-37 修复后 67 轮起实测 51 passed / 12 skipped / 0 failed → 转硬门禁。 # - 注意:tests_contract/ 下的 B-41 flaky(TestGrowthReportInterpretB04)不在本 job # 的 `tests/` 范围内,不影响门禁;该 flaky 由测试侧单独跟踪(B-41)。 # --------------------------------------------------------------------------- langgraph-blocking-pytest: stage: test when: on_success # B-40:硬门禁自动触发(红即拦) cache: key: "$CI_COMMIT_REF_SLUG" paths: - cfc-langgraph/.venv/ script: - cd cfc-langgraph - python3 -m venv .venv || true - . .venv/bin/activate - pip install -q -r requirements_frozen.txt - python -m pytest tests/ -v --ignore=tests/qna --junitxml=junit.xml -p no:cacheprovider artifacts: when: always expire_in: 7 days reports: junit: - cfc-langgraph/junit.xml paths: - cfc-langgraph/junit.xml # --------------------------------------------------------------------------- # Job 6 — Playwright e2e(CI 安全模式)· test-observed(allow_failure,产出趋势) # --------------------------------------------------------------------------- # ⚠️ 严禁在 CI 使用 tests/playwright.config.js(其 globalSetup 会重置远程 # 192.168.16.251 测试库)。CI 采用安全模式: # 1) `npx playwright test --list` — 校验 74 spec / 2418 用例可收集 # 2) `-c playwright.config.smoke.js ci-smoke` — headless chromium 真实拉起冒烟 # 真实 UI e2e 依赖部署环境 + DB(cfc.iwintrue.com),由测试负责人手动跑。 # B-40:真实 e2e 未授权/环境依赖 → 归 test-observed(allow_failure), # 但 test-results 每轮归档供趋势跟踪。 # --------------------------------------------------------------------------- e2e-playwright-observed: stage: test when: on_success allow_failure: true # 环境依赖(部署环境+DB),失败不拦截,仅产出趋势 cache: key: "$CI_COMMIT_REF_SLUG" paths: - tests/node_modules/ script: - cd tests - npm ci || npm install - npx playwright install chromium || true # CI 安全模式:只做收集校验 + headless 冒烟(均不触发远程 DB 重置) - npx playwright test --list - npx playwright test e2e/ci-smoke.spec.js -c playwright.config.smoke.js artifacts: when: always expire_in: 7 days paths: - tests/test-results/ # --------------------------------------------------------------------------- # Job 7 — tests/ 目录 jest(unit + integration)· test-observed(allow_failure) # --------------------------------------------------------------------------- # - tests/jest.config.js 依赖 cfc-frontend/__tests__/setup.js(vue), # 需先装 cfc-frontend 依赖;npm>=10 才能解析 "npm:@vue/babel-plugin-jsx@^1.1.1" # 本机 npm9.2.0 验证失败(node22 自带 npm10 可装) # - integration 部分需后端 API(localhost:9082)在 CI 无后端时仅跑能跑的; # 当前 B-36 下根级黑盒 23 例全 skip → 归 test-observed(allow_failure)。 # --------------------------------------------------------------------------- tests-jest-observed: stage: test when: on_success allow_failure: true # 需 npm10 + cfc-frontend(vue) 依赖;integration 需后端;B-36 全 skip cache: key: "$CI_COMMIT_REF_SLUG" paths: - tests/node_modules/ - cfc-frontend/node_modules/ script: - cd cfc-frontend && npm ci || npm install # setup.js 需要 vue - cd ../tests && npm ci || npm install - npx jest --ci