Skip to content

CI CD Integration

Lisa edited this page Dec 20, 2025 · 23 revisions

CI/CD Integration

CKB becomes your single source of truth for change risk in CI/CD pipelines. Instead of treating code review as a human-only process, CKB provides precomputed analysis that travels with every PR:

  • Blast radius — Know exactly what breaks before merging
  • Suggested reviewers — Based on actual code ownership, not guesswork
  • Boundary detection — Flag when "local" changes cross API contracts
  • Risk scoring — Quantified risk based on hotspots, module spread, and history

The pattern: start CKB as a local server, call it with curl, parse JSON responses.

What It Actually Does

When you call POST /pr/summary, CKB:

  1. Diffs the branches using git (base vs head)
  2. Maps files to modules based on path structure
  3. Checks each file against hotspot data — flags files with score > 0.5
  4. Queries ownership for each changed file (CODEOWNERS + git-blame)
  5. Calculates risk based on: file count, lines changed, hotspots touched, module spread
  6. Returns structured JSON with everything a PR comment needs

Example response shape:

{
  "summary": {
    "totalFiles": 12,
    "totalAdditions": 450,
    "totalDeletions": 120,
    "totalModules": 3,
    "hotspotsTouched": 2
  },
  "riskAssessment": {
    "level": "medium",
    "score": 0.45,
    "factors": ["Touches 2 hotspot(s)", "Spans 3 modules"],
    "suggestions": ["Extra review recommended for hotspot files"]
  },
  "suggestedReviewers": [
    {"owner": "@api-team", "reason": "Owns 8 of 12 changed files", "coverage": 0.67}
  ],
  "modulesAffected": [
    {"moduleId": "internal/api", "filesChanged": 8, "riskLevel": "medium"}
  ]
}

How It Works in GitHub Actions

CKB runs as a local HTTP server inside the runner. The workflow:

  1. Checkout with full history (fetch-depth: 0)
  2. Install CKB
  3. Start server: ckb serve --port 8080 &
  4. Call endpoints with curl
  5. Parse JSON, post PR comment

Installation in CI

npm (Recommended):

- uses: actions/setup-node@v4
  with:
    node-version: '20'

- name: Install CKB
  run: npm install -g @tastehub/ckb

Or use npx (no install):

- uses: actions/setup-node@v4
  with:
    node-version: '20'

- name: Initialize CKB
  run: npx @tastehub/ckb init

Or download pre-built binary:

- run: |
    curl -sSL https://github.com/SimplyLiz/CodeMCP/releases/latest/download/ckb_linux_amd64.tar.gz | tar xz
    sudo mv ckb /usr/local/bin/

Or build from source:

- uses: actions/setup-go@v5
  with:
    go-version: '1.21'

- name: Install CKB
  run: |
    git clone https://github.com/SimplyLiz/CodeMCP.git /tmp/codemcp
    cd /tmp/codemcp
    go build -o /usr/local/bin/ckb ./cmd/ckb

Critical: Full Git History

CKB needs git history for ownership analysis. Without it, suggested reviewers will be empty.

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Required

PR Analysis Workflow

# .github/workflows/pr-analysis.yml
name: CKB PR Analysis

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

permissions:
  contents: read
  pull-requests: write

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install CKB
        run: npm install -g @tastehub/ckb

      - name: Initialize CKB
        run: |
          ckb init
          ckb doctor --fix

      - name: Start CKB Server
        run: |
          ckb serve --port 8080 &
          sleep 2  # Wait for server to start

      - name: Run PR Summary
        id: summary
        run: |
          RESULT=$(curl -s -X POST http://localhost:8080/pr/summary \
            -H "Content-Type: application/json" \
            -d '{"baseBranch": "${{ github.base_ref }}"}')

          RISK=$(echo "$RESULT" | jq -r '.riskAssessment.level // "unknown"')
          echo "risk=$RISK" >> $GITHUB_OUTPUT
          echo "result<<EOF" >> $GITHUB_OUTPUT
          echo "$RESULT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Post Comment
        uses: actions/github-script@v7
        with:
          script: |
            const data = JSON.parse(`${{ steps.summary.outputs.result }}`);
            let emoji = data.riskAssessment?.level === 'high' ? '🔴' :
                        data.riskAssessment?.level === 'medium' ? '🟡' : '🟢';

            let body = `## CKB Analysis ${emoji}\n\n`;
            body += `**Risk:** ${data.riskAssessment?.level || 'unknown'}\n`;
            body += `**Files:** ${data.summary?.totalFiles || 0}\n`;
            body += `**Hotspots touched:** ${data.summary?.hotspotsTouched || 0}\n\n`;

            if (data.suggestedReviewers?.length) {
              body += `### Suggested Reviewers\n`;
              data.suggestedReviewers.slice(0,3).forEach(r => {
                body += `- ${r.owner}\n`;
              });
            }

            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: body
            });

Scheduled Architecture Refresh

Run daily to keep architectural data fresh:

# .github/workflows/ckb-refresh.yml
name: CKB Architecture Refresh

on:
  schedule:
    - cron: '0 2 * * *'  # Daily at 2 AM UTC
  workflow_dispatch:
    inputs:
      force:
        description: 'Force full refresh'
        type: boolean
        default: false

permissions:
  contents: write

jobs:
  refresh:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install CKB
        run: npm install -g @tastehub/ckb

      - name: Initialize and Start Server
        run: |
          ckb init
          ckb serve --port 8080 &
          sleep 2

      - name: Refresh Architecture
        run: |
          curl -s -X POST http://localhost:8080/architecture/refresh \
            -H "Content-Type: application/json" \
            -d '{"scope": "all", "force": ${{ inputs.force || false }}}'

      - name: Generate Reports
        run: |
          curl -s "http://localhost:8080/hotspots?limit=20" > hotspots.json
          curl -s "http://localhost:8080/ownership/drift?threshold=0.3" > drift.json

      - name: Upload Reports
        uses: actions/upload-artifact@v4
        with:
          name: ckb-reports
          path: '*.json'
          retention-days: 30

      - name: Cache CKB Database
        run: |
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git config user.name "github-actions[bot]"
          git add .ckb/
          git diff --staged --quiet || git commit -m "chore: update CKB cache [skip ci]"
          git push

Using Async Refresh in CI

For large repositories, use async mode to avoid timeouts:

- name: Start CKB Server
  run: |
    ckb init
    ckb serve --port 8080 &
    sleep 2

- name: Start Async Refresh
  id: refresh
  run: |
    RESULT=$(curl -s -X POST http://localhost:8080/architecture/refresh \
      -H "Content-Type: application/json" \
      -d '{"scope": "all", "async": true}')
    JOB_ID=$(echo "$RESULT" | jq -r '.jobId')
    echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT

- name: Wait for Completion
  run: |
    JOB_ID="${{ steps.refresh.outputs.job_id }}"
    while true; do
      STATUS=$(curl -s "http://localhost:8080/jobs/$JOB_ID")
      STATE=$(echo "$STATUS" | jq -r '.status')
      PROGRESS=$(echo "$STATUS" | jq -r '.progress')

      echo "Job $JOB_ID: $STATE ($PROGRESS%)"

      if [ "$STATE" = "completed" ]; then
        echo "$STATUS" | jq .result
        break
      elif [ "$STATE" = "failed" ]; then
        echo "Job failed: $(echo $STATUS | jq -r '.error')"
        exit 1
      fi

      sleep 5
    done

Available Endpoints

POST /pr/summary — The main CI endpoint

Request:

{"baseBranch": "main", "headBranch": "HEAD"}

Returns: File counts, module impacts, hotspots touched, risk assessment (low/medium/high), suggested reviewers with coverage percentages.

Risk is calculated from:

  • File count (>20 files = +0.3 risk)
  • Lines changed (>1000 = +0.3 risk)
  • Hotspots touched (+0.1 per hotspot, max 0.3)
  • Module spread (>5 modules = +0.2 risk)

GET /ownership/drift — CODEOWNERS accuracy check

curl "http://localhost:8080/ownership/drift?threshold=0.3&limit=10"

Compares declared owners (CODEOWNERS) vs actual contributors (git-blame). Returns files where ownership has drifted, with drift scores.


GET /hotspots — High-churn files

curl "http://localhost:8080/hotspots?limit=20"

Returns files ranked by volatility (commit frequency, author count, complexity).


POST /architecture/refresh — Rebuild cached analysis

curl -X POST http://localhost:8080/architecture/refresh \
  -H "Content-Type: application/json" \
  -d '{"scope": "all", "async": true}'

With async: true, returns a jobId immediately. Poll /jobs/:id for completion.


GET /jobs/:id — Check async job status

curl "http://localhost:8080/jobs/job-abc123"

Returns: status (queued/running/completed/failed), progress (0-100), result (when done).


Contract-Aware CI (v6.3)

When changes touch API contracts (protobuf, OpenAPI), CKB can detect cross-boundary impact that simple file diffs miss.

Why This Matters

A "local" change to user.proto might break consumers in three other repositories. Without contract analysis, this surfaces as production incidents, not PR warnings.

Detecting Contract Changes

- name: Check Contract Impact
  run: |
    # Find changed contract files
    CONTRACTS=$(git diff --name-only ${{ github.base_ref }}...HEAD | grep -E '\.(proto|yaml|json)$' | grep -E '(proto/|openapi/|api/)' || true)

    if [ -n "$CONTRACTS" ]; then
      echo "⚠️ Contract files changed:"
      echo "$CONTRACTS"

      for CONTRACT in $CONTRACTS; do
        echo "Analyzing impact of $CONTRACT..."
        curl -s "http://localhost:8080/contracts/impact?path=$CONTRACT" | jq .
      done
    fi

Contract Impact Response

{
  "contract": "proto/api/v1/user.proto",
  "visibility": "public",
  "consumers": [
    {"repo": "frontend", "confidence": "high", "files": ["src/api/user.ts"]},
    {"repo": "mobile-app", "confidence": "medium", "files": ["lib/api/user.dart"]}
  ],
  "riskLevel": "high",
  "riskFactors": [
    "Public contract with 2 known consumers",
    "Breaking field removal detected"
  ],
  "suggestion": "Coordinate with frontend and mobile-app teams before merging"
}

Blocking on Contract Changes

- name: Contract Safety Check
  run: |
    CONTRACTS=$(git diff --name-only ${{ github.base_ref }}...HEAD | grep -E '\.proto$' || true)

    for CONTRACT in $CONTRACTS; do
      RESULT=$(curl -s "http://localhost:8080/contracts/impact?path=$CONTRACT")
      RISK=$(echo "$RESULT" | jq -r '.riskLevel')
      CONSUMERS=$(echo "$RESULT" | jq -r '.consumers | length')

      if [ "$RISK" = "high" ] && [ "$CONSUMERS" -gt 0 ]; then
        echo "::error::Contract $CONTRACT has $CONSUMERS consumers and is high risk"
        echo "$RESULT" | jq .
        exit 1
      fi
    done

Safe Refactor Checklist

When CKB detects a risky change, you can generate a checklist for the PR:

- name: Generate Refactor Checklist
  if: steps.summary.outputs.risk == 'high'
  uses: actions/github-script@v7
  with:
    script: |
      const data = JSON.parse(`${{ steps.summary.outputs.result }}`);

      let checklist = `## Safe Refactor Checklist\n\n`;
      checklist += `This PR was flagged as **high risk**. Please verify:\n\n`;

      // Add affected modules
      if (data.modulesAffected?.length) {
        checklist += `### Modules Affected\n`;
        data.modulesAffected.forEach(m => {
          checklist += `- [ ] Reviewed changes in \`${m.moduleId}\` (${m.filesChanged} files)\n`;
        });
        checklist += `\n`;
      }

      // Add reviewers to notify
      if (data.suggestedReviewers?.length) {
        checklist += `### Required Sign-offs\n`;
        data.suggestedReviewers.forEach(r => {
          checklist += `- [ ] ${r.owner} approved (owns ${Math.round(r.coverage * 100)}% of changes)\n`;
        });
        checklist += `\n`;
      }

      // Add hotspot warnings
      if (data.summary?.hotspotsTouched > 0) {
        checklist += `### Hotspot Review\n`;
        checklist += `- [ ] Verified ${data.summary.hotspotsTouched} hotspot file(s) have adequate test coverage\n`;
        checklist += `- [ ] Confirmed changes don't increase complexity\n\n`;
      }

      checklist += `### Before Merge\n`;
      checklist += `- [ ] All tests passing\n`;
      checklist += `- [ ] No unintended breaking changes\n`;
      checklist += `- [ ] Documentation updated if needed\n`;

      github.rest.issues.createComment({
        owner: context.repo.owner,
        repo: context.repo.repo,
        issue_number: context.issue.number,
        body: checklist
      });

What This Is (and Isn't)

This is: A way to get structured JSON about PR changes that you can parse and post as comments. It combines git diff data with CKB's hotspot/ownership/contract analysis to surface risk before merge.

This is not:

  • A CLI command you can run directly (ckb summarize-pr doesn't exist yet)
  • Enforcement — it posts warnings, doesn't block merges by default
  • Magic — if you don't have CODEOWNERS, reviewers come from git-blame heuristics
  • Real-time — hotspot data comes from the last refreshArchitecture run

The awkward part: You have to start a server (ckb serve &) and call it with curl. This is because v6.1 implemented the logic as HTTP handlers, not CLI commands. It works, but it's not as clean as ckb summarize-pr --base=main.

Recommended rollout:

  1. Start with just the PR comment (informational)
  2. Add hotspot warnings after you trust the data
  3. Add contract boundary detection for API changes (v6.3)
  4. Consider ownership drift on a schedule, not every PR
  5. Only add enforcement (failing builds) after the team trusts the signals

Environment Variables

Variable Description Default
CKB_REPO_ROOT Repository root path Current directory
CKB_LOG_LEVEL Log verbosity (debug/info/warn/error) info
CKB_CONFIG_PATH Config file location .ckb/config.json

Tips

  1. Cache the .ckb/ directory — Speeds up subsequent runs
  2. Use fetch-depth: 0 — Required for git-blame analysis
  3. Run refresh on schedule — Keep data fresh without blocking PRs
  4. Set appropriate timeouts — Large repos may need longer timeouts
  5. Use async for big repos — Avoid CI timeout issues

Example: Block High-Risk PRs

- name: Check Risk Level
  run: |
    RISK="${{ steps.summary.outputs.risk }}"
    if [ "$RISK" = "high" ]; then
      echo "::error::PR flagged as HIGH RISK. Manual review required."
      exit 1
    fi

Example: Auto-Request Reviewers

- name: Request Reviewers
  uses: actions/github-script@v7
  with:
    script: |
      const data = JSON.parse(`${{ steps.summary.outputs.result }}`);
      const reviewers = data.suggestedReviewers
        ?.filter(r => !r.owner.startsWith('@'))  // Filter out teams
        .map(r => r.owner)
        .slice(0, 2);

      if (reviewers?.length) {
        await github.rest.pulls.requestReviewers({
          owner: context.repo.owner,
          repo: context.repo.repo,
          pull_number: context.issue.number,
          reviewers: reviewers
        });
      }

Complete Example Workflows

Full example workflows are available in the repository:

  • examples/github-actions/pr-analysis.yml — PR analysis with comments
  • examples/github-actions/scheduled-refresh.yml — Daily architecture refresh

Clone this wiki locally