feat(ci): add Codex AI review pipeline for Woodpecker

Adds automated code quality and security review pipeline that runs on
pull requests using OpenAI Codex with structured output schemas.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 22:04:34 -06:00
parent 281c7ab39b
commit af2e2b083d
4 changed files with 408 additions and 0 deletions

120
.woodpecker/README.md Normal file
View File

@@ -0,0 +1,120 @@
# Woodpecker CI Configuration for Mosaic Stack
## Codex AI Review Pipeline
This directory contains the Codex AI review pipeline configuration for automated code and security reviews on pull requests.
### Setup
1. **Add Codex API key to Woodpecker:**
- Go to mosaic-stack repo at `https://ci.mosaicstack.dev`
- Settings → Secrets
- Add secret: `codex_api_key` with your OpenAI API key
2. **Enable the pipeline:**
- The `codex-review.yml` pipeline will automatically run on all PRs
- The main `.woodpecker.yml` handles primary CI tasks
- This codex pipeline is independent and focused solely on reviews
### What Gets Reviewed
**Code Review (`code-review` step):**
- Correctness — logic errors, edge cases, error handling
- Code Quality — complexity, duplication, naming
- Testing — coverage, test quality
- Performance — N+1 queries, blocking ops
- Dependencies — deprecated packages
- Documentation — comments, API docs
**Security Review (`security-review` step):**
- OWASP Top 10 vulnerabilities
- Hardcoded secrets/credentials
- Injection flaws (SQL, NoSQL, OS command)
- XSS, CSRF, SSRF
- Auth/authz gaps
- Data exposure in logs
### Pipeline Behavior
- **Triggers:** Every pull request
- **Runs:** Code review + Security review in parallel
- **Fails if:**
- Code review finds **blockers**
- Security review finds **critical** or **high** severity issues
- **Outputs:** Structured JSON results in CI logs
### Local Testing
Test the review scripts locally before pushing:
```bash
# Code review of uncommitted changes
~/.claude/scripts/codex/codex-code-review.sh --uncommitted
# Security review of uncommitted changes
~/.claude/scripts/codex/codex-security-review.sh --uncommitted
# Code review against main branch
~/.claude/scripts/codex/codex-code-review.sh -b main
# Security review and save JSON
~/.claude/scripts/codex/codex-security-review.sh -b main -o security.json
```
### Schema Files
The `schemas/` directory contains JSON schemas that enforce structured output from Codex:
- `code-review-schema.json` — Defines output for code quality reviews
- `security-review-schema.json` — Defines output for security reviews
These schemas ensure consistent, machine-readable findings that the CI pipeline can parse and fail on.
### Integration with Main Pipeline
The main `.woodpecker.yml` in the repo root handles:
- Type checking (TypeScript)
- Linting (ESLint)
- Unit tests (Vitest)
- Integration tests (Playwright)
- Docker image builds
This `codex-review.yml` is independent and focuses solely on:
- AI-powered code quality review
- AI-powered security vulnerability scanning
Both pipelines run in parallel on PRs.
### Troubleshooting
**Pipeline fails with "codex: command not found"**
- Check that the node image in `codex-review.yml` matches a version with npm
- Current: `node:22-slim`
**Pipeline fails with auth errors**
- Verify `codex_api_key` secret is set in Woodpecker
- Test the key locally: `CODEX_API_KEY=<key> codex exec "test"`
**Pipeline passes but should fail**
- Check the failure conditions in `codex-review.yml`
- Current thresholds: blockers, critical, or high findings
## Files
| File | Purpose |
| ------------------------------------- | -------------------------------------- |
| `codex-review.yml` | Codex AI review pipeline configuration |
| `schemas/code-review-schema.json` | Code review output schema |
| `schemas/security-review-schema.json` | Security review output schema |
| `README.md` | This file |
## Parent CI Pipeline
The main `.woodpecker.yml` is located at the repository root and handles all build/test tasks.

View File

@@ -0,0 +1,90 @@
# Codex AI Review Pipeline for Woodpecker CI
# Drop this into your repo's .woodpecker/ directory to enable automated
# code and security reviews on every pull request.
#
# Required secrets:
# - codex_api_key: OpenAI API key or Codex-compatible key
#
# Optional secrets:
# - gitea_token: Gitea API token for posting PR comments (if not using tea CLI auth)
when:
event: pull_request
variables:
- &node_image "node:22-slim"
- &install_codex "npm i -g @openai/codex"
steps:
# --- Code Quality Review ---
code-review:
image: *node_image
environment:
CODEX_API_KEY:
from_secret: codex_api_key
commands:
- *install_codex
- apt-get update -qq && apt-get install -y -qq jq git > /dev/null 2>&1
# Generate the diff
- git fetch origin ${CI_COMMIT_TARGET_BRANCH:-main}
- DIFF=$(git diff origin/${CI_COMMIT_TARGET_BRANCH:-main}...HEAD)
# Run code review with structured output
- |
codex exec \
--sandbox read-only \
--output-schema .woodpecker/schemas/code-review-schema.json \
-o /tmp/code-review.json \
"You are an expert code reviewer. Review the following code changes for correctness, code quality, testing, performance, and documentation issues. Only flag actionable, important issues. Categorize as blocker/should-fix/suggestion. If code looks good, say so.
Changes:
$DIFF"
# Output summary
- echo "=== Code Review Results ==="
- jq '.' /tmp/code-review.json
- |
BLOCKERS=$(jq '.stats.blockers // 0' /tmp/code-review.json)
if [ "$BLOCKERS" -gt 0 ]; then
echo "FAIL: $BLOCKERS blocker(s) found"
exit 1
fi
echo "PASS: No blockers found"
# --- Security Review ---
security-review:
image: *node_image
environment:
CODEX_API_KEY:
from_secret: codex_api_key
commands:
- *install_codex
- apt-get update -qq && apt-get install -y -qq jq git > /dev/null 2>&1
# Generate the diff
- git fetch origin ${CI_COMMIT_TARGET_BRANCH:-main}
- DIFF=$(git diff origin/${CI_COMMIT_TARGET_BRANCH:-main}...HEAD)
# Run security review with structured output
- |
codex exec \
--sandbox read-only \
--output-schema .woodpecker/schemas/security-review-schema.json \
-o /tmp/security-review.json \
"You are an expert application security engineer. Review the following code changes for security vulnerabilities including OWASP Top 10, hardcoded secrets, injection flaws, auth/authz gaps, XSS, CSRF, SSRF, path traversal, and supply chain risks. Include CWE IDs and remediation steps. Only flag real security issues, not code quality.
Changes:
$DIFF"
# Output summary
- echo "=== Security Review Results ==="
- jq '.' /tmp/security-review.json
- |
CRITICAL=$(jq '.stats.critical // 0' /tmp/security-review.json)
HIGH=$(jq '.stats.high // 0' /tmp/security-review.json)
if [ "$CRITICAL" -gt 0 ] || [ "$HIGH" -gt 0 ]; then
echo "FAIL: $CRITICAL critical, $HIGH high severity finding(s)"
exit 1
fi
echo "PASS: No critical or high severity findings"

View File

@@ -0,0 +1,92 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"summary": {
"type": "string",
"description": "Brief overall assessment of the code changes"
},
"verdict": {
"type": "string",
"enum": ["approve", "request-changes", "comment"],
"description": "Overall review verdict"
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Confidence score for the review (0-1)"
},
"findings": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"severity": {
"type": "string",
"enum": ["blocker", "should-fix", "suggestion"],
"description": "Finding severity: blocker (must fix), should-fix (important), suggestion (optional)"
},
"title": {
"type": "string",
"description": "Short title describing the issue"
},
"file": {
"type": "string",
"description": "File path where the issue was found"
},
"line_start": {
"type": "integer",
"description": "Starting line number"
},
"line_end": {
"type": "integer",
"description": "Ending line number"
},
"description": {
"type": "string",
"description": "Detailed explanation of the issue"
},
"suggestion": {
"type": "string",
"description": "Suggested fix or improvement"
}
},
"required": [
"severity",
"title",
"file",
"line_start",
"line_end",
"description",
"suggestion"
]
}
},
"stats": {
"type": "object",
"additionalProperties": false,
"properties": {
"files_reviewed": {
"type": "integer",
"description": "Number of files reviewed"
},
"blockers": {
"type": "integer",
"description": "Count of blocker findings"
},
"should_fix": {
"type": "integer",
"description": "Count of should-fix findings"
},
"suggestions": {
"type": "integer",
"description": "Count of suggestion findings"
}
},
"required": ["files_reviewed", "blockers", "should_fix", "suggestions"]
}
},
"required": ["summary", "verdict", "confidence", "findings", "stats"]
}

View File

@@ -0,0 +1,106 @@
{
"type": "object",
"additionalProperties": false,
"properties": {
"summary": {
"type": "string",
"description": "Brief overall security assessment of the code changes"
},
"risk_level": {
"type": "string",
"enum": ["critical", "high", "medium", "low", "none"],
"description": "Overall security risk level"
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Confidence score for the review (0-1)"
},
"findings": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"severity": {
"type": "string",
"enum": ["critical", "high", "medium", "low"],
"description": "Vulnerability severity level"
},
"title": {
"type": "string",
"description": "Short title describing the vulnerability"
},
"file": {
"type": "string",
"description": "File path where the vulnerability was found"
},
"line_start": {
"type": "integer",
"description": "Starting line number"
},
"line_end": {
"type": "integer",
"description": "Ending line number"
},
"description": {
"type": "string",
"description": "Detailed explanation of the vulnerability"
},
"cwe_id": {
"type": "string",
"description": "CWE identifier if applicable (e.g., CWE-79)"
},
"owasp_category": {
"type": "string",
"description": "OWASP Top 10 category if applicable (e.g., A03:2021-Injection)"
},
"remediation": {
"type": "string",
"description": "Specific remediation steps to fix the vulnerability"
}
},
"required": [
"severity",
"title",
"file",
"line_start",
"line_end",
"description",
"cwe_id",
"owasp_category",
"remediation"
]
}
},
"stats": {
"type": "object",
"additionalProperties": false,
"properties": {
"files_reviewed": {
"type": "integer",
"description": "Number of files reviewed"
},
"critical": {
"type": "integer",
"description": "Count of critical findings"
},
"high": {
"type": "integer",
"description": "Count of high findings"
},
"medium": {
"type": "integer",
"description": "Count of medium findings"
},
"low": {
"type": "integer",
"description": "Count of low findings"
}
},
"required": ["files_reviewed", "critical", "high", "medium", "low"]
}
},
"required": ["summary", "risk_level", "confidence", "findings", "stats"]
}