# GitLab CI 示例 - 集成测试门禁
# 放置位置: 代码仓库根目录的 .gitlab-ci.yml

stages:
  - lint          # 代码规范 + PRD/ADR 校验
  - build         # 构建
  - unit-test     # 单元测试
  - code-quality  # 代码扫描
  - integration   # 集成测试（可选）
  - submission-gate  # 【提测门禁】汇总校验
  - deploy-test   # 部署到测试环境
  - metrics       # 度量采集（定时任务）

variables:
  MIN_COVERAGE: "80"

# ============ Lint ============
lint:
  stage: lint
  script:
    - echo "运行代码规范检查"
    # - npm run lint
    # - ruff check .
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

# ============ Build ============
build:
  stage: build
  script:
    - echo "编译项目"
    # - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 day

# ============ Unit Test ============
unit-test:
  stage: unit-test
  script:
    - echo "运行单元测试"
    # - npm test -- --coverage
  coverage: '/Statements\s*:\s*([0-9.]+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    paths:
      - coverage/
    expire_in: 1 week

# ============ Code Quality ============
code-quality:
  stage: code-quality
  script:
    - echo "静态代码扫描"
    # - sonar-scanner
  allow_failure: false

# ============ Submission Gate ============
# 这是提测门禁的核心：检查提测 MR 的必要条件
submission-gate:
  stage: submission-gate
  script:
    - |
      echo "========== 提测门禁校验 =========="

      # 1. 检查 CI 是否全绿（上游依赖保证）
      echo "[1/5] CI 流水线 ✅"

      # 2. 检查单元测试覆盖率
      echo "[2/5] 检查单元测试覆盖率"
      COVERAGE=$(grep -oP '(?<=Statements\s+:\s+)[0-9.]+' coverage/lcov-report/index.html || echo "0")
      echo "  当前覆盖率: ${COVERAGE}%"
      if (( $(echo "$COVERAGE < $MIN_COVERAGE" | bc -l) )); then
        echo "❌ 覆盖率未达标（目标 ${MIN_COVERAGE}%）"
        exit 1
      fi
      echo "  ✅ 覆盖率达标"

      # 3. 检查接口文档是否与代码同步更新
      echo "[3/5] 检查接口文档同步"
      if git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | grep -qE '^(src|app|lib)/.*\.(ts|js|py|go|java)$'; then
        if ! git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | grep -qE '^docs/api/.*\.md$'; then
          echo "⚠️  代码有变更但接口文档未更新，请检查"
          # exit 1  # 可根据严格度打开
        fi
      fi
      echo "  ✅ 通过"

      # 4. 检查提测申请单是否完整（MR 描述中的 checklist）
      echo "[4/5] 检查 MR checklist"
      # 通过 GitLab API 检查 MR 描述
      echo "  ✅ 通过（需结合 MR 模板审核）"

      # 5. 检查是否有遗留的 TODO/FIXME
      echo "[5/5] 检查代码标记"
      TODO_COUNT=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD | xargs grep -l "TODO\|FIXME" 2>/dev/null | wc -l || echo 0)
      echo "  发现 ${TODO_COUNT} 个包含 TODO/FIXME 的文件"

      echo "========== 提测门禁通过 =========="
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^(release|main)/'

# ============ Deploy to Test ============
deploy-test:
  stage: deploy-test
  script:
    - echo "部署到测试环境"
    # - ./deploy.sh test
  environment:
    name: test
    url: https://test.example.com
  rules:
    - if: '$CI_COMMIT_BRANCH =~ /^release/'

# ============ PRD 校验（Sprint 2 新增）============
# 当 MR 动了 PRD 文件时,自动校验必备章节、AC、量化指标、模糊词
prd-check:
  stage: lint
  image: node:20-alpine
  script:
    - |
      echo "========== PRD 结构校验 =========="
      FILES=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD \
        | grep -E '(docs/prd/.*|.*prd.*|.*PRD.*)\.md$' || true)
      if [ -z "$FILES" ]; then
        echo "本 MR 未修改 PRD 文件,跳过"
        exit 0
      fi
      FAIL=0
      for f in $FILES; do
        echo "→ 校验 $f"
        node tools/cross-platform/scripts/check-prd.js "$f" || FAIL=$?
      done
      # 退出码 2 = 硬错误（必备章节缺失）,阻塞合入
      # 退出码 1 = 仅 warning,通过
      if [ "$FAIL" = "2" ]; then
        exit 2
      fi
      exit 0
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      changes:
        - "docs/prd/**/*.md"
        - "**/prd-*.md"
        - "**/PRD-*.md"
  allow_failure: false

# ============ PRD 可测性评分（Sprint 2 新增）============
# < 60 分阻塞合入,60-79 警告,≥ 80 通过
testability-score:
  stage: lint
  image: node:20-alpine
  script:
    - |
      echo "========== PRD 可测性评分 =========="
      FILES=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD \
        | grep -E '(docs/prd/.*|.*prd.*|.*PRD.*)\.md$' || true)
      if [ -z "$FILES" ]; then
        echo "本 MR 未修改 PRD 文件,跳过"
        exit 0
      fi
      FAIL=0
      for f in $FILES; do
        echo "→ 评分 $f"
        node tools/cross-platform/scripts/score-testability.js "$f" || FAIL=$?
      done
      # 退出码 2 = < 60 分,硬阻塞
      if [ "$FAIL" = "2" ]; then
        echo "❌ PRD 可测性 < 60,请补充验收标准 / 量化指标 / 异常场景"
        exit 2
      fi
      exit 0
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      changes:
        - "docs/prd/**/*.md"
        - "**/prd-*.md"
        - "**/PRD-*.md"
  allow_failure: false

# ============ ADR 索引自动重建（Sprint 2 新增）============
# 提交到默认分支且动了 docs/adr/ 时,重建索引并自动提 commit
adr-index:
  stage: lint
  image: node:20-alpine
  before_script:
    - apk add --no-cache git
    - git config user.email "ci-bot@example.com"
    - git config user.name "CI Bot"
  script:
    - |
      echo "========== ADR 索引重建 =========="
      node tools/cross-platform/scripts/generate-adr-index.js --target docs/adr/
      if git diff --quiet docs/adr/README.md; then
        echo "✅ 索引已是最新,无需提交"
      else
        git add docs/adr/README.md
        git commit -m "chore(adr): 自动刷新 ADR 索引 [skip ci]"
        git push "https://oauth2:${CI_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:${CI_COMMIT_REF_NAME}
      fi
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      changes:
        - "docs/adr/**/*.md"
  # 需在项目 CI/CD 变量中配置 CI_PUSH_TOKEN（有 write_repository 权限）

# ============ 业务度量周报（Sprint 2 新增）============
# 每周一 08:00 定时跑,生成周报并作为 artifact
business-metrics:
  stage: metrics
  image: node:20-alpine
  before_script:
    - apk add --no-cache git
  script:
    - echo "========== 采集业务 / 开发度量 =========="
    - node tools/metrics/business/collect.js --since "7 days ago"
    - node tools/metrics/development/collect.js --since "7 days ago"
  artifacts:
    paths:
      - METRICS-business.md
      - METRICS-development.md
    expire_in: 30 days
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
  # 在 GitLab → CI/CD → Schedules 配置每周一 08:00 触发

# ============ 需求覆盖率（Sprint 3 新增）============
# MR 动了需求或用例时,跑 coverage-analysis 提示未覆盖项（软警告）
coverage-check:
  stage: lint
  image: node:20-alpine
  script:
    - |
      CHANGED=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD \
        | grep -E '(01-business|03-testing|requirements|test-cases)/' || true)
      if [ -z "$CHANGED" ]; then
        echo "✅ 未触发覆盖率校验"
        exit 0
      fi
      node tools/cross-platform/scripts/coverage-analysis.js \
        --req examples/leave-management-system/01-business/prd-v1.0.md \
        --cases examples/leave-management-system/03-testing/ \
        --output COVERAGE.md || echo "⚠️ 有未覆盖需求"
      cat COVERAGE.md
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  allow_failure: true

# ============ 配置审计（Sprint 3 新增）============
# 改了 config/ 目录的 *.env/*.yml/*.json 时审计,prod 明文敏感值阻塞
config-audit:
  stage: lint
  image: node:20-alpine
  script:
    - |
      CHANGED=$(git diff --name-only origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD \
        | grep -E '\.(env|ya?ml|json)$' | grep -E '(config|configs|env)/' || true)
      if [ -z "$CHANGED" ]; then
        echo "✅ 未触发配置审计"
        exit 0
      fi
      DIRS=$(echo "$CHANGED" | xargs -I{} dirname {} | sort -u)
      FAIL=0
      for d in $DIRS; do
        echo "=== 审计 $d ==="
        node tools/cross-platform/scripts/config-audit.js --dir "$d" --output audit-$(basename $d).md || {
          code=$?
          if [ "$code" = "2" ]; then FAIL=1; fi
        }
      done
      exit $FAIL
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
  allow_failure: false

# ============ 测试+运维 周报（Sprint 3 新增）============
# 每周一 08:00 Schedule,产出测试+运维周报
testing-ops-metrics:
  stage: metrics
  image: node:20-alpine
  before_script:
    - apk add --no-cache git
  script:
    - node tools/metrics/testing/collect.js --since "7 days ago"
    - node tools/metrics/operations/collect.js --since "7 days ago"
  artifacts:
    paths:
      - METRICS-testing.md
      - METRICS-operations.md
    expire_in: 30 days
  rules:
    - if: '$CI_PIPELINE_SOURCE == "schedule"'
