273 lines
6.1 KiB
Markdown
273 lines
6.1 KiB
Markdown
# CI/CD Integration Examples
|
|
|
|
Ready-to-use configurations for automated CLAUDE.md validation.
|
|
|
|
## Pre-Commit Hook
|
|
|
|
### Basic Validation
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
# .git/hooks/pre-commit
|
|
|
|
if git diff --cached --name-only | grep -q "CLAUDE.md"; then
|
|
echo "Validating CLAUDE.md..."
|
|
|
|
FILE=$(git diff --cached --name-only | grep "CLAUDE.md" | head -1)
|
|
|
|
# Check file length
|
|
LINES=$(wc -l < "$FILE")
|
|
if [ "$LINES" -gt 500 ]; then
|
|
echo "ERROR: CLAUDE.md is $LINES lines (max 500)"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for secrets
|
|
if grep -qE "password|secret|token|api_key|-----BEGIN" "$FILE"; then
|
|
echo "ERROR: Potential secrets detected in CLAUDE.md"
|
|
echo "Run: grep -nE 'password|secret|token|api_key' $FILE"
|
|
exit 1
|
|
fi
|
|
|
|
# Check for broken imports
|
|
grep "^@" "$FILE" | while read -r import; do
|
|
path="${import#@}"
|
|
if [ ! -f "$path" ]; then
|
|
echo "ERROR: Broken import: $import"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo "CLAUDE.md validation passed"
|
|
fi
|
|
```
|
|
|
|
### Installation
|
|
```bash
|
|
chmod +x .git/hooks/pre-commit
|
|
```
|
|
|
|
## GitHub Actions
|
|
|
|
### Basic Workflow
|
|
|
|
```yaml
|
|
# .github/workflows/claude-md-audit.yml
|
|
name: CLAUDE.md Audit
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- '**/CLAUDE.md'
|
|
- '**/.claude/CLAUDE.md'
|
|
|
|
jobs:
|
|
audit:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Find CLAUDE.md files
|
|
id: find
|
|
run: |
|
|
files=$(find . -name "CLAUDE.md" -type f)
|
|
echo "files=$files" >> $GITHUB_OUTPUT
|
|
|
|
- name: Check file lengths
|
|
run: |
|
|
for file in ${{ steps.find.outputs.files }}; do
|
|
lines=$(wc -l < "$file")
|
|
if [ "$lines" -gt 500 ]; then
|
|
echo "::error file=$file::File is $lines lines (max 500)"
|
|
exit 1
|
|
fi
|
|
echo "OK: $file ($lines lines)"
|
|
done
|
|
|
|
- name: Check for secrets
|
|
run: |
|
|
for file in ${{ steps.find.outputs.files }}; do
|
|
if grep -qE "password|secret|token|api_key|-----BEGIN" "$file"; then
|
|
echo "::error file=$file::Potential secrets detected"
|
|
grep -nE "password|secret|token|api_key" "$file" || true
|
|
exit 1
|
|
fi
|
|
done
|
|
echo "No secrets detected"
|
|
|
|
- name: Check imports
|
|
run: |
|
|
for file in ${{ steps.find.outputs.files }}; do
|
|
dir=$(dirname "$file")
|
|
grep "^@" "$file" | while read -r import; do
|
|
path="${import#@}"
|
|
if [ ! -f "$dir/$path" ]; then
|
|
echo "::error file=$file::Broken import: $import"
|
|
exit 1
|
|
fi
|
|
done
|
|
done
|
|
echo "All imports valid"
|
|
```
|
|
|
|
### With PR Comment
|
|
|
|
```yaml
|
|
- name: Post audit summary
|
|
if: always()
|
|
uses: actions/github-script@v7
|
|
with:
|
|
script: |
|
|
const fs = require('fs');
|
|
|
|
// Collect results
|
|
const results = {
|
|
files: 0,
|
|
passed: 0,
|
|
failed: 0,
|
|
warnings: []
|
|
};
|
|
|
|
// Post comment
|
|
await github.rest.issues.createComment({
|
|
owner: context.repo.owner,
|
|
repo: context.repo.repo,
|
|
issue_number: context.issue.number,
|
|
body: `## CLAUDE.md Audit Results
|
|
|
|
- Files checked: ${results.files}
|
|
- Passed: ${results.passed}
|
|
- Failed: ${results.failed}
|
|
|
|
${results.warnings.length > 0 ? '### Warnings\n' + results.warnings.join('\n') : ''}
|
|
`
|
|
});
|
|
```
|
|
|
|
## VS Code Task
|
|
|
|
### tasks.json
|
|
|
|
```json
|
|
{
|
|
"version": "2.0.0",
|
|
"tasks": [
|
|
{
|
|
"label": "Audit CLAUDE.md",
|
|
"type": "shell",
|
|
"command": "bash",
|
|
"args": [
|
|
"-c",
|
|
"echo 'Auditing CLAUDE.md...' && wc -l CLAUDE.md && grep -c '^##' CLAUDE.md && echo 'Done'"
|
|
],
|
|
"group": "test",
|
|
"presentation": {
|
|
"reveal": "always",
|
|
"panel": "new"
|
|
},
|
|
"problemMatcher": []
|
|
},
|
|
{
|
|
"label": "Check CLAUDE.md secrets",
|
|
"type": "shell",
|
|
"command": "bash",
|
|
"args": [
|
|
"-c",
|
|
"if grep -qE 'password|secret|token|api_key' CLAUDE.md; then echo 'WARNING: Potential secrets found:' && grep -nE 'password|secret|token|api_key' CLAUDE.md; else echo 'No secrets detected'; fi"
|
|
],
|
|
"group": "test",
|
|
"problemMatcher": []
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Keyboard Shortcut
|
|
|
|
```json
|
|
// keybindings.json
|
|
{
|
|
"key": "ctrl+shift+a",
|
|
"command": "workbench.action.tasks.runTask",
|
|
"args": "Audit CLAUDE.md"
|
|
}
|
|
```
|
|
|
|
## Husky + lint-staged
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
npm install -D husky lint-staged
|
|
npx husky init
|
|
```
|
|
|
|
### package.json
|
|
|
|
```json
|
|
{
|
|
"lint-staged": {
|
|
"**/CLAUDE.md": [
|
|
"bash -c 'wc -l \"$0\" | awk \"{if (\\$1 > 500) {print \\\"ERROR: \\\" \\$1 \\\" lines\\\"; exit 1}}\"'",
|
|
"bash -c 'grep -qE \"password|secret|token\" \"$0\" && exit 1 || true'"
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
### .husky/pre-commit
|
|
|
|
```bash
|
|
npx lint-staged
|
|
```
|
|
|
|
## GitLab CI
|
|
|
|
```yaml
|
|
# .gitlab-ci.yml
|
|
claude-md-audit:
|
|
stage: test
|
|
rules:
|
|
- changes:
|
|
- "**/CLAUDE.md"
|
|
script:
|
|
- |
|
|
for file in $(find . -name "CLAUDE.md"); do
|
|
echo "Checking $file"
|
|
lines=$(wc -l < "$file")
|
|
if [ "$lines" -gt 500 ]; then
|
|
echo "ERROR: $file is $lines lines"
|
|
exit 1
|
|
fi
|
|
done
|
|
- echo "CLAUDE.md audit passed"
|
|
```
|
|
|
|
## Makefile Target
|
|
|
|
```makefile
|
|
.PHONY: audit-claude-md
|
|
|
|
audit-claude-md:
|
|
@echo "Auditing CLAUDE.md files..."
|
|
@find . -name "CLAUDE.md" -exec sh -c '\
|
|
lines=$$(wc -l < "{}"); \
|
|
if [ "$$lines" -gt 500 ]; then \
|
|
echo "ERROR: {} is $$lines lines"; \
|
|
exit 1; \
|
|
fi; \
|
|
echo "OK: {} ($$lines lines)"' \;
|
|
@echo "Checking for secrets..."
|
|
@! grep -rE "password|secret|token|api_key" --include="CLAUDE.md" . || \
|
|
(echo "ERROR: Secrets detected" && exit 1)
|
|
@echo "Audit complete"
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Fail fast**: Check secrets before anything else
|
|
2. **Clear errors**: Include file path and line numbers
|
|
3. **PR feedback**: Post results as comments
|
|
4. **Gradual adoption**: Start with warnings, then enforce
|
|
5. **Skip when needed**: Allow `[skip audit]` in commit message
|