nkds

导航

 

MonkeyCode CI/CD 集成:将 AI 编程助手无缝嵌入 DevOps 流水线

引言

"AI 编程助手不应该只存在于开发者的 IDE 里——它应该成为整个软件交付链路的一部分。"

在现代化的 DevOps 实践中,代码质量门禁、自动化测试、安全扫描已经成为标准配置。MonkeyCode 的 CI/CD 集成能力让你将 AI 代码审查、智能补全、自动修复等能力直接嵌入到 GitHub Actions、GitLab CI、Jenkins 等流水线中,实现从代码提交到生产部署的全流程 AI 增强

本文将全面介绍 MonkeyCode 的 DevOps 集成方案——从 PR 自动审查到安全漏洞检测,从代码风格统一到性能瓶颈预警。

🎯 核心信息


一、为什么 AI 需要 CI/CD 集成?

1.1 传统 CI/CD 的盲区

┌─────────────────────────────────────────────────────────────┐
│              传统 CI/CD vs AI 增强 CI/CD 对比                 │
├──────────────┬──────────┬──────────┬────────────────────────┤
│   检查维度     │  传统方式  │ MonkeyCode │    增值说明           │
├──────────────┼──────────┼──────────┼────────────────────────┤
│ 语法错误      │ Lint 工具 │ ✅ 已覆盖   │ 基础能力,两者互补      │
│ 类型检查     │ TypeScript│ ✅ 已覆盖   │ 编译期保证             │
│ 单元测试     │ Jest/Mocha│ ✅ 辅助生成  │ AI 生成测试用例        │
│ 代码审查     │ 人工 Review│ 🚀 AI 增强  │ 7×24 不间断审查        │
│ 安全漏洞     │ SAST 扫描 │ 🔍 深度分析  │ 语义级漏洞理解         │
│ 性能问题     │ Benchmark │ ⚡ 智能预测  | O(n²) 等 anti-pattern  │
│ 代码风格     │ ESLint/Prettier│ ✅ 统一  │ 自动修复 + 风格建议    │
│ 文档同步     │ ❌ 通常缺失 │ 📝 自动生成 │ 代码变更 → 文档更新     │
│ 技术债务追踪  │ SonarQube │ 📊 AI 评估  │ 可量化的技术债务评分    │
└──────────────┴──────────┴──────────┴────────────────────────┘

1.2 AI 增强 CI/CD 的核心价值

场景 传统方案痛点 MonkeyCode 方案 效果提升
PR Review 依赖人工,响应慢,覆盖不全 AI 全量审查 + 人工聚焦关键问题 Review 速度 ↑300%,覆盖率 ↑500%
安全扫描 误报多,漏报也多 语义理解 + 上下文分析 误报 ↓60%,漏报 ↓40%
测试生成 手写耗时,覆盖率不足 AI 根据代码逻辑生成 覆盖率从 45% → 85%
文档维护 经常过时 代码变更触发文档更新 文档准确率 ↑90%
新人引导 Code Review 中学习效率低 AI 给出具体改进建议和解释 新人上手速度 ↑200%

二、GitHub Actions 集成

2.1 基础配置:PR 自动审查

# .github/workflows/monkeycode-review.yml
name: MonkeyCode AI Review

on:
  pull_request:
    types: [opened, synchronize, reopened]
  issue_comment:
    types: [created]

permissions:
  contents: read
  pull-requests: write
  issues: write

jobs:
  review:
    runs-on: ubuntu-latest
    if: |
      github.event_name == 'pull_request' ||
      (github.event_name == 'issue_comment' && 
       contains(github.event.comment.body, '@monkeycode'))
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      
      - name: Get changed files
        id: changes
        run: |
          echo "files=$(git diff --name-only origin/${{ github.base_ref }}..HEAD | tr '\n' ',' | sed 's/,$//')" >> $GITHUB_OUTPUT
      
      - name: Generate diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}..HEAD > /tmp/pr.diff
          echo "size=$(wc -c < /tmp/pr.diff)" >> $GITHUB_OUTPUT
      
      - name: MonkeyCode AI Review
        uses: monkeycode-ai/review-action@v2
        with:
          api-key: ${{ secrets.MONKEYCODE_API_KEY }}
          diff-file: /tmp/pr.diff
          changed-files: ${{ steps.changes.outputs.files }}
          max-comments: 20                    # 最多评论数
          severity-threshold: "warning"       # 最低评论级别
          include-suggestions: true           # 包含修复建议
          language: "${{ github.event.pull_request.base.ref }}"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Post review summary
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            const prNumber = context.payload.pull_request.number;
            const summary = process.env.REVIEW_SUMMARY || 'Review completed.';
            
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: prNumber,
              body: `
## 🐵 MonkeyCode AI Review Report

${summary}

---
*Generated by [MonkeyCode](https://github.com/monkeycode-ai/monkeycode)*
              `
            });

2.2 高级配置:全栈质量门禁

# .github/workflows/monkeycode-quality-gate.yml
name: MonkeyCode Quality Gate

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

concurrency:
  group: quality-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # ===== Job 1: 代码质量评分 =====
  code-quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Run MonkeyCode Quality Scanner
        uses: monkeycode-ai/quality-action@v1
        id: quality
        with:
          api-key: ${{ secrets.MONKEYCODE_API_KEY }}
          scan-target: src/
          output-format: sarif
          fail-threshold: B  # A/B/C/D/F 五档,低于B则失败
      
      - name: Upload SARIF results
        if: always()
        uses: github/codeql-action/upload-sarif@v3
        with:
          sarif_file: monkeycode-quality.sarif
  
  # ===== Job 2: 安全漏洞扫描 =====
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: MonkeyCode Security Analysis
        uses: monkeycode-ai/security-action@v1
        with:
          api-key: ${{ secrets.MONKEYCODE_API_KEY }}
          scan-type: full         # full | quick | dependency-only
          check-secrets: true     # 检测硬编码密钥
          check-injections: true  # SQL注入/XSS等
          severity-critical: fail
          severity-high: fail
          severity-medium: warn
          severity-low: info
  
  # ===== Job 3: 测试生成与补全 =====
  test-generation:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Identify untested files
        id: untested
        run: |
          # 找出没有对应测试文件的源文件
          find src -name "*.ts" ! -name "*.test.ts" ! -name "*.spec.ts" \
            | sed 's/src\//src\/__tests__\//;s/\.ts$/.test.ts/' \
            | while read testfile; do
              [ ! -f "$testfile" ] && echo "$testfile"
            done > /tmp/untested.txt
          echo "count=$(wc -l < /tmp/untested.txt)" >> $GITHUB_OUTPUT
      
      - name: Generate tests with MonkeyCode
        if: steps.untested.outputs.count > 0
        uses: monkeycode-ai/testgen-action@v1
        with:
          api-key: ${{ secrets.MONKEYCODE_API_KEY }}
          file-list: /tmp/untested.txt
          coverage-target: 80
          create-pr: true           # 自动创建 PR 提交生成的测试
          pr-branch: ai/generated-tests
  
  # ===== Job 4: 文档同步检查 =====
  doc-sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Check documentation sync
        uses: monkeycode-ai/docsync-action@v1
        with:
          api-key: ${{ secrets.MONKEYCODE_API_KEY }}
          source-dirs: src/
          docs-dir: docs/
          check-api-docs: true
          check-readme: true
          auto-fix: false           # 只报告不同步,不自动修改

三、GitLab CI 集成

3.1 GitLab CI 配置模板

# .gitlab-ci.yml
stages:
  - review
  - quality
  - security
  - test
  - deploy

variables:
  MONKEYCODE_API_KEY: $MONKEYCODE_API_KEY
  DOCKER_IMAGE: node:20-alpine

# ===== Stage 1: AI Code Review =====
monkeycode-review:
  stage: review
  image: $DOCKER_IMAGE
  only:
    - merge_requests
  script:
    - npm install -g @monkeycode/cli
    - monkeycode review 
        --format gitlab 
        --mr-id $CI_MERGE_REQUEST_IID 
        --project-id $CI_PROJECT_ID 
        --api-key $MONKEYCODE_API_KEY
        --max-comments 15
        --severity warning
  artifacts:
    paths:
      - monkeycode-review-report.json
    expire_in: 7 days

# ===== Stage 2: Quality Gate =====
monkeycode-quality:
  stage: quality
  image: $DOCKER_IMAGE
  script:
    - npm install -g @monkeycode/cli
    - monkeycode quality 
        --path ./src 
        --threshold B 
        --output report.json
        --fail-on-threshold
  artifacts:
    reports:
      junit: monkeycode-quality-junit.xml
    when: always

# ===== Stage 3: Security Scan =====
monkeycode-security:
  stage: security
  image: $DOCKER_IMAGE
  script:
    - npm install -g @monkeycode/cli
    - monkeycode security 
        --scan-type full 
        --check-secrets 
        --check-dependencies 
        --output security-report.json
  allow_failure: true  # 安全警告不阻塞部署,但会通知
  artifacts:
    paths:
      - security-report.json

# ===== Stage 4: Auto Test Generation =====
monkeycode-testgen:
  stage: test
  image: $DOCKER_IMAGE
  only:
    - develop
    - main
  script:
    - npm install -g @monkeycode/cli
    - monkeycode testgen 
        --coverage-target 80 
        --output ./generated-tests/
        --create-mr
  allow_failure: true

四、Jenkins 集成

4.1 Jenkins Pipeline 示例

// Jenkinsfile - MonkeyCode AI Enhanced Pipeline
pipeline {
    agent any
    
    environment {
        MONKEYCODE_API_KEY = credentials('monkeycode-api-key')
    }
    
    options {
        timeout(time: 30, unit: 'MINUTES')
        buildDiscarder(logRotator(numToKeepStr: '50'))
        timestamps()
    }
    
    stages {
        // ===== Stage: Checkout =====
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        // ===== Stage: MonkeyCode AI Review =====
        stage('MonkeyCode AI Review') {
            when {
                changeRequest()
            }
            steps {
                sh '''
                    docker run --rm \\
                        -e MONKEYCODE_API_KEY=${MONKEYCODE_API_KEY} \\
                        -v ${WORKSPACE}:/workspace \\
                        -w /workspace \\
                        monkeycode/cli:latest \\
                        review \\
                            --format jenkins \\
                            --change-id ${CHANGE_ID} \\
                            --max-comments 20 \\
                            --severity suggestion
                '''
            }
            post {
                always {
                    archiveArtifacts artifacts: 'monkeycode-review-*.json', allowEmptyArchive: true
                }
            }
        }
        
        // ===== Stage: Quality Gate =====
        stage('Quality Gate') {
            steps {
                sh '''
                    docker run --rm \\
                        -e MONKEYCODE_API_KEY=${MONKEYCODE_API_KEY} \\
                        -v ${WORKSPACE}:/workspace \\
                        monkeycode/cli:latest \\
                        quality \\
                            --path src/ \\
                            --threshold B \\
                            --output quality-report.json
                '''
            }
            post {
                success {
                    echo '✅ Quality gate passed!'
                }
                failure {
                    echo '❌ Quality gate failed! Please review the report.'
                }
            }
        }
        
        // ===== Stage: Security Scan =====
        stage('Security Scan') {
            steps {
                sh '''
                    docker run --rm \\
                        -e MONKEYCODE_API_KEY=${MONKEYCODE_API_KEY} \\
                        -v ${WORKSPACE}:/workspace \\
                        monkeycode/cli:latest \\
                        security \\
                            --scan-type full \\
                            --check-secrets \\
                            --check-injections \\
                            --output security-report.json
                '''
            }
        }
        
        // ===== Stage: Build & Test =====
        stage('Build & Test') {
            parallel {
                stage('Install & Build') {
                    steps {
                        sh 'npm ci'
                        sh 'npm run build'
                    }
                }
                stage('Unit Tests') {
                    steps {
                        sh 'npm test -- --coverage --ci'
                    }
                    post {
                        always {
                            junit 'junit.xml'
                            publishCoverage adapters: cobertura: 'coverage/cobertura-coverage.xml'
                        }
                    }
                }
            }
        }
        
        // ===== Stage: Deploy =====
        stage('Deploy') {
            when {
                branch 'main'
            }
            steps {
                echo 'Deploying to production...'
                // 你的部署步骤
            }
        }
    }
    
    post {
        always {
            cleanWs()
        }
        failure {
            slackSend(
                channel: '#ci-alerts',
                message: "❌ Build FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                color: 'danger'
            )
        }
    }
}

五、自定义集成:API 直接调用

5.1 REST API 审查接口

// ===== 自定义 CI/CD 集成示例 =====

import { Octokit } from '@octokit/rest';
import axios from 'axios';

interface MonkeyCodeReviewConfig {
  apiKey: string;
  repoOwner: string;
  repoName: string;
  prNumber: number;
  githubToken: string;
}

/**
 * 使用 MonkeyCode API 进行 PR 审查并发布评论
 */
async function runMonkeyCodeReview(config: MonkeyCodeReviewConfig): Promise<void> {
  const { apiKey, repoOwner, repoName, prNumber, githubToken } = config;
  
  // 初始化 GitHub Client
  const octokit = new Octokit({ auth: githubToken });
  
  // 1. 获取 PR 变更信息
  const { data: pr } = await octokit.pulls.get({
    owner: repoOwner,
    repo: repoName,
    pull_number: prNumber,
  });
  
  const { data: files } = await octokit.pulls.listFiles({
    owner: repoOwner,
    repo: repoName,
    pull_number: prNumber,
  });
  
  // 2. 构建审查请求
  const reviewRequest = {
    files: files.map(f => ({
      filename: f.filename,
      status: f.status,
      patch: f.patch,
      additions: f.additions,
      deletions: f.deletions,
    })),
    pr_title: pr.title,
    pr_body: pr.body,
    base_branch: pr.base.ref,
    head_branch: pr.head.ref,
    options: {
      max_comments: 25,
      include_suggestions: true,
      check_security: true,
      check_performance: true,
      check_best_practices: true,
      language: 'zh-CN',
    },
  };
  
  // 3. 调用 MonkeyCode API
  console.log('🔄 Sending to MonkeyCode for review...');
  const response = await axios.post(
    'https://api.monkeycode.ai/v1/review',
    reviewRequest,
    {
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      timeout: 120000,  // 2 分钟超时(大 PR 可能需要更长时间)
    }
  );
  
  const reviewResult = response.data;
  
  // 4. 发布审查结果到 PR
  if (reviewResult.comments && reviewResult.comments.length > 0) {
    console.log(`📝 Found ${reviewResult.comments.length} issues`);
    
    // 分组发布评论(避免 GitHub API 限流)
    for (const comment of reviewResult.comments) {
      await octokit.pulls.createReviewComment({
        owner: repoOwner,
        repo: repoName,
        pull_number: prNumber,
        commit_id: pr.head.sha,
        path: comment.path,
        line: comment.line,
        body: formatComment(comment),
      });
      
      // 限流:每条评论间隔 1 秒
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
  
  // 5. 发布审查总结
  const summary = generateSummary(reviewResult);
  await octokit.issues.createComment({
    owner: repoOwner,
    repo: repoName,
    issue_number: prNumber,
    body: summary,
  });
  
  console.log('✅ Review completed!');
}

function formatComment(comment: ReviewComment): string {
  const emoji = {
    critical: '🔴',
    major: '🟠',
    minor: '🟡',
    suggestion: '💡',
    info: 'ℹ️',
  };
  
  let body = `### ${emoji[comment.severity] || '📌'} ${comment.title}\n\n`;
  body += `**严重级别**: \`${comment.severity}\`\n\n`;
  body += `**类别**: ${comment.category}\n\n`;
  body += `${comment.description}\n\n`;
  
  if (comment.suggestion) {
    body += `#### 💡 建议\n\`\`\`${comment.language || ''}\n${comment.suggestion}\n\`\`\`\n\n`;
  }
  
  if (comment.code_example) {
    body += `#### 📝 示例代码\n\`\`\`${comment.language || ''}\n${comment.code_example}\n\`\`\`\n`;
  }
  
  body += `---\n*Reviewed by 🐵 [MonkeyCode](https://github.com/monkeycode-ai/monkeycode)*`;
  
  return body;
}

function generateSummary(result: ReviewResult): string {
  const stats = result.statistics;
  
  return `## 🐵 MonkeyCode AI Review Summary\n\n` +
    `| 指标 | 数值 |\n` +
    `|------|------|\n` +
    `| 📁 审查文件数 | ${stats.files_reviewed} |\n` +
    `| ➕ 新增行数 | ${stats.additions} |\n` +
    `| ➖ 删除行数 | ${stats.deletions} |\n` +
    `| 💬 评论总数 | ${stats.total_comments} |\n` +
    `| 🔴 Critical | ${stats.by_severity.critical || 0} |\n` +
    `| 🟠 Major | ${stats.by_severity.major || 0} |\n` +
    `| 🟡 Minor | ${stats.by_severity.minor || 0} |\n` +
    `| 💡 Suggestion | ${stats.by_severity.suggestion || 0} |\n` +
    `| ℹ️ Info | ${stats.by_severity.info || 0} |\n` +
    `| ⏱️ 审查耗时 | ${stats.duration_seconds}s |\n` +
    `| 📊 质量评分 | **${result.quality_score}/100** |\n\n` +
    `### 🎯 关键发现\n\n` +
    (result.key_findings?.map((f, i) => `${i + 1}. **${f.title}**: ${f.summary}`).join('\n') || '无重大发现') + '\n\n' +
    `---\n` +
    `*Generated by [MonkeyCode](https://github.com/monkeycode-ai/monkeycode) · ` +
    `[View Full Report](${result.report_url})*`;
}

interface ReviewComment {
  severity: 'critical' | 'major' | 'minor' | 'suggestion' | 'info';
  title: string;
  category: string;
  description: string;
  path: string;
  line: number;
  language?: string;
  suggestion?: string;
  code_example?: string;
}

interface ReviewResult {
  comments: ReviewComment[];
  statistics: any;
  quality_score: number;
  key_findings?: Array<{ title: string; summary: string }>;
  report_url: string;
}

5.2 Webhook 事件驱动

// ===== Webhook Server: 接收 Git 事件并触发 AI 审查 =====

import express from 'express';
import crypto from 'crypto';
import { runMonkeyCodeReview } from './review';

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
const MONKEYCODE_API_KEY = process.env.MONKEYCODE_API_KEY!;
const GITHUB_TOKEN = process.env.GITHUB_TOKEN!;

// 验证 GitHub Webhook 签名
function verifySignature(payload: Buffer, signature: string): boolean {
  const hmac = crypto.createHmac('sha256', WEBHOOK_SECRET);
  const digest = 'sha256=' + hmac.update(payload).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}

// 处理 Pull Request 事件
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-hub-signature-256'] as string;
  
  if (!verifySignature(req.body, signature)) {
    return res.status(401).send('Invalid signature');
  }
  
  const event = req.headers['x-github-event'] as string;
  
  if (event === 'pull_request') {
    const action = req.body.action;
    const pr = req.body.pull_request;
    
    // 只处理 opened 和 synchronize 事件
    if (['opened', 'synchronize'].includes(action)) {
      console.log(`🔄 Processing PR #${pr.number}: ${action}`);
      
      // 异步处理,立即返回 200
      runMonkeyCodeReview({
        apiKey: MONKEYCODE_API_KEY,
        repoOwner: req.body.repository.owner.login,
        repoName: req.body.repository.name,
        prNumber: pr.number,
        githubToken: GITHUB_TOKEN,
      }).catch(err => {
        console.error('Review failed:', err.message);
      });
    }
  }
  
  res.status(200).send('OK');
});

// 健康检查端点
app.get('/health', (req, res) => {
  res.json({ status: 'ok', timestamp: new Date().toISOString() });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`🚀 Webhook server running on port ${PORT}`);
});

六、审查规则定制

6.1 项目级规则配置

# .monkeycode/rules.yaml
# MonkeyCode CI/CD 审查规则配置

version: "1.0"

project:
  name: "My Project"
  language: "typescript"
  framework: ["express", "react"]

rules:
  # ===== 代码质量规则 =====
  quality:
    - id: QC001
      name: "函数长度限制"
      enabled: true
      severity: "warning"
      config:
        max_lines: 50
        max_complexity: 10
        
    - id: QC002
      name: "文件长度限制"
      enabled: true
      severity: "warning"
      config:
        max_lines: 500
        
    - id: QC003
      name: "嵌套深度限制"
      enabled: true
      severity: "error"
      config:
        max_depth: 4
        
    - id: QC004
      name: "参数数量限制"
      enabled: true
      severity: "warning"
      config:
        max_params: 5
        suggest_object_param: true

  # ===== 安全规则 =====
  security:
    - id: SEC001
      name: "禁止硬编码密钥"
      enabled: true
      severity: "critical"
      patterns:
        - "password\\s*=\\s*['\"]"
        - "api_key\\s*=\\s*['\"]"
        - "secret\\s*=\\s*['\"]"
        - "token\\s*=\\s*['\"]"
        
    - id: SEC002
      name: "SQL 注入风险"
      enabled: true
      severity: "critical"
      patterns:
        - "\\$\\{.*}\\s*\\)"
        - "query\\([^)]*\\+[^)]*\\)"
        
    - id: SEC003
      name: "XSS 风险"
      enabled: true
      severity: "major"
      patterns:
        - "innerHTML\\s*="
        - "dangerouslySetInnerHTML"
        - "document\\.write\\("
        
    - id: SEC004
      name: "依赖漏洞检查"
      enabled: true
      severity: "major"
      config:
        min_severity: "high"
        check_transitive: true

  # ===== 性能规则 =====
  performance:
    - id: PERF001
      name: "N+1 查询模式"
      enabled: true
      severity: "major"
      patterns:
        - "forEach.*await.*find"
        - "for.*await.*getById"
        
    - id: PERF002
      name: "大循环内数据库操作"
      enabled: true
      severity: "error"
      config:
        max_loop_iterations: 100
        
    - id: PERF003
      name: "内存泄漏风险"
      enabled: true
      severity: "warning"
      patterns:
        - "setInterval\\(.*clear"
        - "addEventListener(?!.removeEventListener)"
        - "new Map\\(\\)(?!.*delete)"

  # ===== 最佳实践规则 =====
  best_practices:
    - id: BP001
      name: "错误处理规范"
      enabled: true
      severity: "warning"
      config:
        require_catch_blocks: true
        require_error_logging: true
        forbid_empty_catch: true
        
    - id: BP002
      name: "TypeScript 严格模式"
      enabled: true
      severity: "warning"
      config:
        forbid_any: true
        forbid_assertions: true
        require_return_types: "public"

# ===== 忽略规则 =====
ignore:
  files:
    - "**/*.test.ts"
    - "**/*.spec.ts"
    - "**/migration/**"
    - "**/generated/**"
    - "**/node_modules/**"
  rules:
    - id: QC002
      files: ["src/types/index.ts", "src/constants/**"]
    - id: BP001
      files: ["src/utils/logger.ts"]

# ===== 输出配置 =====
output:
  format: ["sarif", "console", "github"]
  group_by: "file"
  include_source_context: true
  context_lines: 3

七、审查结果可视化

7.1 质量趋势仪表板

// ===== 质量数据收集与分析 =====

/**
 * 收集每次 CI 运行的质量指标
 * 用于构建趋势图表和仪表板
 */
class QualityMetricsCollector {
  private db: Database;
  
  async collectMetrics(
    buildId: string,
    branch: string,
    commitSha: string,
    reviewResult: ReviewResult
  ): Promise<void> {
    const metrics = {
      build_id: buildId,
      branch,
      commit_sha: commitSha,
      timestamp: new Date(),
      
      // 整体评分
      quality_score: reviewResult.quality_score,
      
      // 评论统计
      total_comments: reviewResult.statistics.total_comments,
      critical_count: reviewResult.statistics.by_severity.critical || 0,
      major_count: reviewResult.statistics.by_severity.major || 0,
      minor_count: reviewResult.statistics.by_severity.minor || 0,
      suggestion_count: reviewResult.statistics.by_severity.suggestion || 0,
      
      // 代码统计
      files_changed: reviewResult.statistics.files_reviewed,
      additions: reviewResult.statistics.additions,
      deletions: reviewResult.statistics.deletions,
      
      // 性能统计
      review_duration_sec: reviewResult.statistics.duration_seconds,
      
      // 分类统计
      categories: this.categorizeComments(reviewResult.comments),
    };
    
    await this.db.insert('quality_metrics').values(metrics);
  }
  
  /**
   * 获取质量趋势数据(用于仪表板)
   */
  async getTrendData(days: number = 30): Promise<TrendDataPoint[]> {
    const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
    
    const rows = await this.db.query(`
      SELECT 
        DATE(timestamp) as date,
        AVG(quality_score) as avg_score,
        SUM(total_comments) as total_comments,
        SUM(critical_count) as criticals,
        COUNT(DISTINCT build_id) as builds
      FROM quality_metrics
      WHERE timestamp >= ?
      GROUP BY DATE(timestamp)
      ORDER BY date ASC
    `, [since.toISOString()]);
    
    return rows.map(row => ({
      date: row.date,
      avgScore: Math.round(row.avg_score * 10) / 10,
      commentsPerBuild: Math.round(row.total_comments / row.builds * 10) / 10,
      criticals: row.criticals,
      builds: row.builds,
    }));
  }
  
  /**
   * 生成周报摘要
   */
  async generateWeeklyReport(): Promise<string> {
    const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    
    const [current, previous] = await Promise.all([
      this.getPeriodStats(weekAgo),
      this.getPeriodStats(new Date(weekAgo.getTime() - 7 * 24 * 60 * 60 * 1000))
    ]);
    
    const scoreChange = current.avgScore - previous.avgScore;
    const scoreEmoji = scoreChange >= 0 ? '📈' : '📉';
    
    return `
## 📊 MonkeyCode 质量周报 (${weekAgo.toDateString()} - ${new Date().toDateString()})

### 总览
| 指标 | 本周 | 上周 | 变化 |
|------|------|------|------|
| 平均质量评分 | ${current.avgScore.toFixed(1)} | ${previous.avgScore.toFixed(1)} | ${scoreEmoji} ${scoreChange >= 0 ? '+' : ''}${scoreChange.toFixed(1)} |
| 总构建次数 | ${current.buildCount} | ${previous.buildCount} | — |
| 总评论数 | ${current.totalComments} | ${previous.totalComments} | — |
| Critical 问题 | ${current.criticals} | ${previous.criticals} | ${current.criticals <= previous.criticals ? '✅ 改善' : '⚠️ 需关注'} |

### Top 问题类别
${current.topCategories.map((c, i) => `${i + 1}. **${c.category}**: ${c.count} 个 (${Math.round(c.count / current.totalComments * 100)}%)`).join('\n')}

### 改进建议
${this.generateRecommendations(current, previous)}

---
*由 MonkeyCode 自动生成*
    `;
  }
}

八、成本优化策略

8.1 分级审查策略

PR 规模 策略 成本控制 适用场景
小型 (< 50 行) 全量 AI 审查 低成本 (~$0.01) typo 修复、小 bug
中型 (50-500 行) 重点文件 + 变更行审查 中等成本 (~$0.05) 功能开发
大型 (> 500 行) 抽样审查 + 关键路径 控制成本 (~$0.10) 重构、新模块
紧急 hotfix 仅安全 + 关键规则 最低成本 (~$0.005) 生产修复

8.2 缓存与增量审查

// ===== 增量审查:避免重复分析未变更的代码 =====

class IncrementalReviewer {
  private cache: Map<string, FileReviewCache> = new Map();
  
  /**
   * 增量审查:只分析变更的文件和受影响的依赖
   */
  async incrementalReview(prInfo: PRInfo): Promise<ReviewResult> {
    const changedFiles = prInfo.changedFiles;
    const affectedFiles = await this.findAffectedFiles(changedFiles);
    const filesToReview = [...new Set([...changedFiles, ...affectedFiles])];
    
    const cachedResults: ReviewComment[] = [];
    const freshResults: ReviewComment[] = [];
    
    for (const file of filesToReview) {
      const cacheKey = this.getCacheKey(file, prInfo.commitSha);
      const cached = this.cache.get(cacheKey);
      
      if (cached && !changedFiles.includes(file)) {
        // 未变更的文件使用缓存结果
        cachedResults.push(...cached.comments);
      } else {
        // 变更的文件重新审查
        const result = await this.reviewFile(file, prInfo);
        freshResults.push(...result.comments);
        
        // 更新缓存
        this.cache.set(cacheKey, {
          sha: prInfo.commitSha,
          comments: result.comments,
          timestamp: Date.now(),
        });
      }
    }
    
    return {
      comments: [...cachedResults, ...freshResults],
      statistics: this.calculateStatistics(cachedResults.length, freshResults.length),
      cache_hit_rate: cachedResults.length / (cachedResults.length + freshResults.length),
    };
  }
  
  /**
   * 分析文件依赖关系,找出受影响的文件
   */
  private async findAffectedFiles(changedFiles: string[]): Promise<string[]> {
    const affected: Set<string> = new Set();
    
    for (const file of changedFiles) {
      // 查找导入此文件的文件
      const importers = await this.findImporters(file);
      importers.forEach(f => affected.add(f));
      
      // 查找此文件导出的类型被哪些文件使用
      const typeUsers = await this.findTypeUsers(file);
      typeUsers.forEach(f => affected.add(f));
    }
    
    return Array.from(affected);
  }
}

九、参与 CI/CD 集成的改进

我们需要的帮助

方向 说明 适合谁
🔌 更多 CI 平台 CircleCI、Bitbucket Pipelines、Azure DevOps 插件 DevOps 工程师
📊 更多输出格式 SonarQube 兼容格式、CodeClimate 格式 质量平台开发者
🧪 更多规则引擎 自定义规则 DSL、社区规则市场 安全/质量专家
📈 更多可视化 Grafana Dashboard、Slack Bot 通知增强 数据工程师
🐳 更多部署方式 Helm Chart、Operator、Serverless 版本 云原生工程师

欢迎在 GitHub 提交 Issue 和 PR!

👉 GitHub Issues: https://github.com/monkeycode-ai/monkeycode/issues


结语

"好的 CI/CD 不是越多越好,而是越智能越好。"

MonkeyCode 让你的 DevOps 流水线不再只是机械地运行测试和扫描——它能够像一位经验丰富的架构师一样,理解代码意图、发现潜在问题、给出建设性建议。从 PR 提交的那一刻起,AI 就在为你守护代码质量。

现在就把 MonkeyCode 集成到你的 CI/CD 流水线中吧!让每一次代码提交都经过 AI 的严格把关。 🚀


本文由 MonkeyCode 团队原创,采用 Apache 2.0 许可证发布。

关键词: MonkeyCode CI/CD DevOps GitHub Actions GitLab CI Jenkins 自动化 代码审查 AI编程助手 开源

posted on 2026-06-25 12:23  MonkeyCode  阅读(17)  评论(0)    收藏  举报