CI/CD Integration

CI/CD Integration

Integrate Capyseo into your CI/CD pipeline to catch SEO issues before deployment.

Quick Start

# Run analysis in CI mode capyseo analyze ./dist --ci --min-score 80

Exit code 1 if score is below threshold.

GitHub Actions

Basic Workflow

# .github/workflows/seo.yml name: SEO Analysis on: push: branches: [main] pull_request: jobs: seo: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - name: Build site run: bun run build - name: SEO Analysis run: bunx @capyseo/cli analyze ./dist --ci --min-score 80

With AI Analysis

- name: SEO Analysis with AI run: bunx @capyseo/cli analyze ./dist --ai --ci --min-score 80 env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}

SARIF Report

Upload results to GitHub Security tab:

- name: SEO Analysis run: bunx @capyseo/cli analyze ./dist --ci --format sarif -o seo-report.sarif continue-on-error: true - name: Upload SARIF uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: seo-report.sarif

Full Example

name: SEO Analysis on: push: branches: [main] pull_request: jobs: seo: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v2 - run: bun install - name: Build run: bun run build - name: SEO Analysis run: | bunx @capyseo/cli analyze ./dist \ --ci \ --min-score 80 \ --ai \ --format sarif \ -o seo-report.sarif env: GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - name: Upload SARIF uses: github/codeql-action/upload-sarif@v3 if: always() with: sarif_file: seo-report.sarif - name: Upload HTML Report uses: actions/upload-artifact@v4 if: always() with: name: seo-report path: seo-report.html

GitLab CI

# .gitlab-ci.yml seo: stage: test image: oven/bun:latest script: - bun install - bun run build - bunx @capyseo/cli analyze ./dist --ci --min-score 80 variables: GEMINI_API_KEY: $GEMINI_API_KEY artifacts: reports: sast: seo-report.sarif paths: - seo-report.html when: always

Jenkins

// Jenkinsfile pipeline { agent any environment { GEMINI_API_KEY = credentials('gemini-api-key') } stages { stage('Build') { steps { sh 'bun install' sh 'bun run build' } } stage('SEO Analysis') { steps { sh 'bunx @capyseo/cli analyze ./dist --ci --min-score 80' } } } post { always { archiveArtifacts artifacts: 'seo-report.html', allowEmptyArchive: true } } }

CircleCI

# .circleci/config.yml version: 2.1 jobs: seo: docker: - image: oven/bun:latest steps: - checkout - run: bun install - run: bun run build - run: name: SEO Analysis command: bunx @capyseo/cli analyze ./dist --ci --min-score 80 - store_artifacts: path: seo-report.html workflows: main: jobs: - seo

Exit Codes

Code Meaning
0 Success, score meets threshold
1 Score below threshold or errors found
2 Configuration or file error

CI Options

capyseo analyze ./dist \ --ci \ # Enable CI mode (required) --min-score 80 \ # Minimum score (default: 0) --format sarif \ # Output format -o report.sarif \ # Output file --fail-on error \ # Fail on severity --quiet # Minimal output

--ci

Enables CI mode:

  • Non-interactive output
  • Exit code based on score
  • Suitable for pipelines

--min-score

Minimum acceptable score (0-100):

--min-score 80 # Fail if below 80 --min-score 90 # Stricter threshold

--fail-on

Fail on specific severity:

--fail-on error # Fail only on errors --fail-on warning # Fail on warnings or errors --fail-on info # Fail on any issue

--quiet

Minimal output for logs:

--quiet # Only errors and final score

Caching

Cache AI responses to speed up CI:

- uses: actions/cache@v4 with: path: .capyseo-cache key: capyseo-${{ hashFiles('dist/**/*.html') }} restore-keys: capyseo-

Config:

// capyseo.config.js export default { ai: { cacheDir: '.capyseo-cache', cacheTTL: 86400000, // 24 hours }, };

Branch Protection

Require SEO checks to pass:

  1. Go to repository Settings → Branches
  2. Add branch protection rule for main
  3. Enable "Require status checks"
  4. Select "seo" job

PR Comments

Add SEO summary to pull requests:

- name: SEO Analysis id: seo run: | bunx @capyseo/cli analyze ./dist --ci --format json -o seo.json SCORE=$(jq '.score' seo.json) echo "score=$SCORE" >> $GITHUB_OUTPUT - name: Comment PR if: github.event_name == 'pull_request' uses: actions/github-script@v7 with: script: | const score = '${{ steps.seo.outputs.score }}'; github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number, body: `## SEO Score: ${score}/100` });

Secrets Setup

GitHub

  1. Go to Settings → Secrets → Actions
  2. Add GEMINI_API_KEY (or other provider key)

GitLab

  1. Go to Settings → CI/CD → Variables
  2. Add GEMINI_API_KEY (masked)

Best Practices

  1. Run on PRs - Catch issues before merge
  2. Set realistic thresholds - Start at 70, increase over time
  3. Cache AI responses - Reduce API calls and time
  4. Upload reports - Archive for debugging
  5. Use SARIF - Integrate with security dashboards
  6. Fail fast - Use --fail-on error for critical issues

Troubleshooting

"Score below threshold"

Error: SEO score 65 is below minimum 80
  • Review the report for issues
  • Fix critical errors first
  • Lower threshold temporarily if needed

"API key not found"

Error: No AI provider configured
  • Add secret to CI environment
  • Check secret name matches env var

Slow builds

  • Enable caching
  • Use --no-ai for faster checks
  • Analyze only changed pages

Example Config

// capyseo.config.js export default { rules: { 'meta-title': { severity: 'error' }, 'meta-description': { severity: 'error' }, 'image-alt': { severity: 'warning' }, }, ai: { enabled: true, provider: 'gemini', cacheDir: '.capyseo-cache', }, ci: { minScore: 80, failOn: ['error'], }, };