Add OpenAPI spec validation hook (Redocly CLI)
PostToolUse hook triggers on openapi/*.yaml edits and runs redocly lint with the recommended ruleset (security-defined disabled since endpoints are intentionally public). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
22
.claude/hooks/openapi-validate.sh
Executable file
22
.claude/hooks/openapi-validate.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Read hook input from stdin (JSON with tool_input.file_path)
|
||||
INPUT=$(cat)
|
||||
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")
|
||||
|
||||
# Only run for OpenAPI spec files
|
||||
case "$FILE_PATH" in
|
||||
*/openapi/*.yaml|*/openapi/*.yml) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR/backend"
|
||||
|
||||
# Run validation (zero-config: structural validity only)
|
||||
if OUTPUT=$(npx @redocly/cli@latest lint src/main/resources/openapi/api.yaml --format=stylish 2>&1); then
|
||||
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"✓ OpenAPI spec validation passed."}}'
|
||||
else
|
||||
ESCAPED=$(echo "$OUTPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":$ESCAPED}}"
|
||||
fi
|
||||
@@ -13,6 +13,11 @@
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/frontend-check.sh\"",
|
||||
"timeout": 120
|
||||
},
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/openapi-validate.sh\"",
|
||||
"timeout": 120
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
10
README.md
10
README.md
@@ -150,6 +150,16 @@ ArchUnit enforces hexagonal boundaries: domain must not depend on adapters, appl
|
||||
|---------------------|------------------|-------------------|
|
||||
| Prettier | `npm run format` | Formatting issues |
|
||||
|
||||
### OpenAPI Spec (YAML)
|
||||
|
||||
**After editing an `openapi/*.yaml` file** (PostToolUse hook):
|
||||
|
||||
| What | Command | Fails on |
|
||||
|---------------------|--------------------------|-----------------------------------|
|
||||
| Redocly CLI | `redocly lint api.yaml` | Structural and ruleset violations |
|
||||
|
||||
Validates the OpenAPI 3.1 spec against the Redocly `recommended` ruleset (with `security-defined` disabled, since endpoints are intentionally public). Runs via `npx @redocly/cli@latest`.
|
||||
|
||||
## Deployment
|
||||
|
||||
### Docker Compose
|
||||
|
||||
5
backend/redocly.yaml
Normal file
5
backend/redocly.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
extends:
|
||||
- recommended
|
||||
|
||||
rules:
|
||||
security-defined: off
|
||||
215
docs/agents/research/2026-03-04-openapi-validation-pipeline.md
Normal file
215
docs/agents/research/2026-03-04-openapi-validation-pipeline.md
Normal file
@@ -0,0 +1,215 @@
|
||||
---
|
||||
date: "2026-03-04T22:27:37.933286+00:00"
|
||||
git_commit: 91e566efea0cbf53ba06a29b63317b7435609bd8
|
||||
branch: master
|
||||
topic: "Automatic OpenAPI Validation Pipelines for Backpressure Hooks"
|
||||
tags: [research, openapi, validation, hooks, backpressure, linting]
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Research: Automatic OpenAPI Validation Pipelines
|
||||
|
||||
## Research Question
|
||||
|
||||
What automatic validation pipelines exist for OpenAPI specs that can be integrated into the current Claude Code backpressure hook setup, running after the OpenAPI spec has been modified?
|
||||
|
||||
## Summary
|
||||
|
||||
The project already has a PostToolUse hook system that runs backend compile checks and frontend lint/type-checks after Edit/Write operations. Adding OpenAPI spec validation requires a new hook script that triggers specifically when `api.yaml` is modified. Several CLI tools support OpenAPI 3.1.0 validation — **Redocly CLI** is the strongest fit given the existing Node.js toolchain, MIT license, active maintenance, and zero-config baseline.
|
||||
|
||||
## Current Backpressure Setup
|
||||
|
||||
### Hook Architecture (`.claude/settings.json`)
|
||||
|
||||
The project uses Claude Code hooks for automated quality gates:
|
||||
|
||||
| Hook Event | Trigger | Scripts |
|
||||
|---|---|---|
|
||||
| `PostToolUse` | `Edit\|Write` tool calls | `backend-compile-check.sh`, `frontend-check.sh` |
|
||||
| `Stop` | Agent attempts to stop | `run-tests.sh` |
|
||||
|
||||
### How Hooks Work
|
||||
|
||||
Each hook script:
|
||||
1. Reads JSON from stdin containing `tool_input.file_path`
|
||||
2. Pattern-matches the file path to decide if it should run
|
||||
3. Executes validation (compile, lint, type-check, test)
|
||||
4. Returns JSON with either success message or failure details
|
||||
5. On failure: outputs `hookSpecificOutput` with error context (PostToolUse) or `{"decision":"block"}` (Stop)
|
||||
|
||||
### Existing Pattern for File Matching
|
||||
|
||||
```bash
|
||||
# backend-compile-check.sh — matches Java files
|
||||
case "$FILE_PATH" in
|
||||
*/backend/src/*.java|backend/src/*.java) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
# frontend-check.sh — matches TS/Vue files
|
||||
case "$FILE_PATH" in
|
||||
*/frontend/src/*.ts|*/frontend/src/*.vue|frontend/src/*.ts|frontend/src/*.vue) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
```
|
||||
|
||||
An OpenAPI validation hook would use the same pattern:
|
||||
```bash
|
||||
case "$FILE_PATH" in
|
||||
*/openapi/api.yaml|*/openapi/*.yaml) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
```
|
||||
|
||||
### Existing OpenAPI Tooling in the Project
|
||||
|
||||
- **Backend:** `openapi-generator-maven-plugin` v7.20.0 generates Spring interfaces from `api.yaml` (`pom.xml:149-178`)
|
||||
- **Frontend:** `openapi-typescript` v7.13.0 generates TypeScript types; `openapi-fetch` v0.17.0 provides type-safe client
|
||||
- **No validation/linting tools** currently installed — no Redocly, Spectral, or other linter config exists
|
||||
|
||||
## Tool Evaluation
|
||||
|
||||
### Redocly CLI (`@redocly/cli`)
|
||||
|
||||
| Attribute | Value |
|
||||
|---|---|
|
||||
| OpenAPI 3.1 | Full support |
|
||||
| Install | `npm install -g @redocly/cli` or `npx @redocly/cli@latest` |
|
||||
| CLI | `redocly lint api.yaml` |
|
||||
| License | MIT |
|
||||
| Maintenance | Very active — latest v2.20.3 (2026-03-03), daily/weekly releases |
|
||||
| GitHub | ~1.4k stars (Redocly ecosystem: 24k+ combined) |
|
||||
|
||||
**Checks:** Structural validity against OAS schema, configurable linting rules (naming, descriptions, operation IDs, security), style/consistency enforcement. Built-in rulesets: `minimal`, `recommended`, `recommended-strict`. Zero-config baseline works immediately. Custom rules via `redocly.yaml`.
|
||||
|
||||
**Fit for this project:** Node.js already in the toolchain (frontend). `npx` form requires no permanent install. MIT license compatible with GPL-3.0. The `@redocly/openapi-core` package is already present as a transitive dependency of `openapi-typescript` in `node_modules`.
|
||||
|
||||
### Spectral (`@stoplight/spectral-cli`)
|
||||
|
||||
| Attribute | Value |
|
||||
|---|---|
|
||||
| OpenAPI 3.1 | Full support (since v6.x) |
|
||||
| Install | `npm install -g @stoplight/spectral-cli` |
|
||||
| CLI | `spectral lint api.yaml` |
|
||||
| License | Apache 2.0 |
|
||||
| Maintenance | Active — latest v6.15.0 (2025-04-22), slower cadence |
|
||||
| GitHub | ~3k stars |
|
||||
|
||||
**Checks:** Schema compliance, missing descriptions/tags/operationIds, contact/license metadata. Highly extensible custom rulesets via YAML/JS. Configurable severity levels.
|
||||
|
||||
**Fit for this project:** Well-established industry standard. Apache 2.0 compatible with GPL. Less actively maintained than Redocly (10 months since last release). Heavier custom ruleset system may be over-engineered for current needs.
|
||||
|
||||
### Vacuum (`daveshanley/vacuum`)
|
||||
|
||||
| Attribute | Value |
|
||||
|---|---|
|
||||
| OpenAPI 3.1 | Full support (via libopenapi) |
|
||||
| Install | `brew install daveshanley/vacuum/vacuum` or Go binary |
|
||||
| CLI | `vacuum lint api.yaml` |
|
||||
| License | MIT |
|
||||
| Maintenance | Active — latest release 2025-12-22 |
|
||||
| GitHub | ~1k stars |
|
||||
|
||||
**Checks:** Structural validation, Spectral-compatible rulesets, OWASP security checks, naming conventions, descriptions/examples/tags. Single Go binary — no runtime dependencies.
|
||||
|
||||
**Fit for this project:** Zero-dependency binary is appealing for CI. However, adds a non-Node.js tool dependency when the project already has Node.js. Spectral ruleset compatibility is a plus for portability.
|
||||
|
||||
### oasdiff (`oasdiff/oasdiff`)
|
||||
|
||||
| Attribute | Value |
|
||||
|---|---|
|
||||
| OpenAPI 3.1 | Beta |
|
||||
| Install | `brew install oasdiff` or Go binary |
|
||||
| CLI | `oasdiff breaking base.yaml revision.yaml` |
|
||||
| License | Apache 2.0 |
|
||||
| Maintenance | Active — latest v1.11.10 (2026-02-05) |
|
||||
| GitHub | ~1.1k stars |
|
||||
|
||||
**Checks:** 300+ breaking change detection rules (paths, parameters, schemas, security, headers, enums). Requires two spec versions to compare — not a standalone validator.
|
||||
|
||||
**Fit for this project:** Different category — detects breaking changes between spec versions, not structural validity. Useful as a CI-only check comparing `HEAD~1` vs `HEAD`. OAS 3.1 support is still beta.
|
||||
|
||||
### Not Recommended
|
||||
|
||||
- **swagger-cli:** Abandoned, no OAS 3.1 support
|
||||
- **IBM OpenAPI Validator:** Active but opinionated IBM-specific rules add configuration overhead for no benefit
|
||||
|
||||
## Tool Comparison Matrix
|
||||
|
||||
| Tool | OAS 3.1 | License | Last Release | Stars | Runtime | Category |
|
||||
|---|---|---|---|---|---|---|
|
||||
| **Redocly CLI** | Full | MIT | 2026-03-03 | ~1.4k | Node.js | Lint + validate |
|
||||
| **Spectral** | Full | Apache 2.0 | 2025-04-22 | ~3k | Node.js | Lint |
|
||||
| **Vacuum** | Full | MIT | 2025-12-22 | ~1k | Go binary | Lint + validate |
|
||||
| **oasdiff** | Beta | Apache 2.0 | 2026-02-05 | ~1.1k | Go binary | Breaking changes |
|
||||
|
||||
## Integration Pattern
|
||||
|
||||
### Hook Script Structure
|
||||
|
||||
An OpenAPI validation hook would follow the existing pattern in `.claude/hooks/`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
INPUT=$(cat)
|
||||
FILE_PATH=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('file_path',''))" 2>/dev/null || echo "")
|
||||
|
||||
# Only run for OpenAPI spec files
|
||||
case "$FILE_PATH" in
|
||||
*/openapi/*.yaml|*/openapi/*.yml) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
|
||||
cd "$CLAUDE_PROJECT_DIR/backend"
|
||||
|
||||
# Run validation
|
||||
if OUTPUT=$(npx @redocly/cli@latest lint src/main/resources/openapi/api.yaml --format=stylish 2>&1); then
|
||||
echo '{"hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"✓ OpenAPI spec validation passed."}}'
|
||||
else
|
||||
ESCAPED=$(echo "$OUTPUT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
|
||||
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PostToolUse\",\"additionalContext\":$ESCAPED}}"
|
||||
fi
|
||||
```
|
||||
|
||||
### Registration in `.claude/settings.json`
|
||||
|
||||
The hook would be added to the existing `PostToolUse` array alongside the compile and lint hooks:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "command",
|
||||
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/openapi-validate.sh\"",
|
||||
"timeout": 120
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration (Optional)
|
||||
|
||||
A `redocly.yaml` in the project root or `backend/` directory can customize rules:
|
||||
|
||||
```yaml
|
||||
extends:
|
||||
- recommended
|
||||
|
||||
rules:
|
||||
operation-operationId: error
|
||||
tag-description: warn
|
||||
no-ambiguous-paths: error
|
||||
```
|
||||
|
||||
## Code References
|
||||
|
||||
- `.claude/settings.json:1-32` — Hook configuration (PostToolUse + Stop events)
|
||||
- `.claude/hooks/backend-compile-check.sh` — Java file detection pattern + compile check
|
||||
- `.claude/hooks/frontend-check.sh` — TS/Vue file detection pattern + type-check + lint
|
||||
- `.claude/hooks/run-tests.sh` — Stop hook with test execution and block/approve logic
|
||||
- `backend/pom.xml:149-178` — openapi-generator-maven-plugin configuration
|
||||
- `backend/src/main/resources/openapi/api.yaml` — The OpenAPI 3.1.0 spec to validate
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Should the validation use a pinned version (`npx @redocly/cli@1.x.x`) or latest? Pinned is more reproducible; latest gets rule updates automatically.
|
||||
- Should a `redocly.yaml` config be added immediately with the `recommended` ruleset, or start with zero-config (structural validation only) and add rules incrementally?
|
||||
- Is breaking change detection (oasdiff) desirable as a separate CI check, or is structural validation sufficient for now?
|
||||
Reference in New Issue
Block a user