Compliance Automation
Strategies and tools for automating ADR compliance checking.
CI/CD Integration
Section titled “CI/CD Integration”GitHub Actions Workflow
Section titled “GitHub Actions Workflow”name: ADR Compliance
on: pull_request: branches: [main, develop] push: branches: [main]
jobs: adr-compliance: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Load ADR Configuration id: adr-config run: | if [ -f ".claude/adr.local.md" ]; then echo "config_exists=true" >> $GITHUB_OUTPUT fi
- name: Check Technology Compliance run: | echo "Checking database technology..." if grep -rq "mysql\|mongodb" src/; then echo "::error::Non-approved database technology found" exit 1 fi
- name: Check Architecture Patterns run: | echo "Checking for direct service calls..." # Add pattern checks based on your ADRs
- name: Generate Compliance Report if: always() run: | echo "## ADR Compliance Report" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY echo "|-------|--------|" >> $GITHUB_STEP_SUMMARYGitLab CI Pipeline
Section titled “GitLab CI Pipeline”stages: - lint - test - compliance
adr-compliance: stage: compliance script: - ./scripts/adr-compliance-check.sh rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH artifacts: reports: junit: compliance-report.xml paths: - compliance-report.md when: alwaysAzure DevOps Pipeline
Section titled “Azure DevOps Pipeline”trigger: branches: include: - main - develop
stages: - stage: Compliance jobs: - job: ADRCompliance steps: - script: | ./scripts/adr-compliance-check.sh displayName: 'Check ADR Compliance' - task: PublishTestResults@2 inputs: testResultsFormat: 'JUnit' testResultsFiles: '**/compliance-*.xml'Architecture Testing Tools
Section titled “Architecture Testing Tools”ArchUnit (Java/Kotlin)
Section titled “ArchUnit (Java/Kotlin)”import com.tngtech.archunit.core.domain.JavaClasses;import com.tngtech.archunit.core.importer.ClassFileImporter;import com.tngtech.archunit.lang.ArchRule;import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
public class ADRComplianceTest {
private final JavaClasses classes = new ClassFileImporter() .importPackages("com.example");
// ADR-001: Controllers must use services, not repositories @Test void controllersShouldNotAccessRepositories() { ArchRule rule = noClasses() .that().resideInAPackage("..controller..") .should().accessClassesThat() .resideInAPackage("..repository..");
rule.check(classes); }
// ADR-002: Domain layer should not depend on infrastructure @Test void domainShouldNotDependOnInfrastructure() { ArchRule rule = noClasses() .that().resideInAPackage("..domain..") .should().dependOnClassesThat() .resideInAPackage("..infrastructure..");
rule.check(classes); }
// ADR-003: All REST endpoints must have security annotations @Test void endpointsMustBeSecured() { ArchRule rule = methods() .that().areAnnotatedWith(GetMapping.class) .or().areAnnotatedWith(PostMapping.class) .should().beAnnotatedWith(PreAuthorize.class);
rule.check(classes); }}Dependency-Cruiser (JavaScript/TypeScript)
Section titled “Dependency-Cruiser (JavaScript/TypeScript)”module.exports = { forbidden: [ // ADR-001: UI should not import from data layer { name: 'no-ui-to-data', severity: 'error', from: { path: '^src/ui' }, to: { path: '^src/data' } }, // ADR-002: Services should not import from controllers { name: 'no-service-to-controller', severity: 'error', from: { path: '^src/services' }, to: { path: '^src/controllers' } }, // ADR-003: No direct database imports outside data layer { name: 'database-only-in-data', severity: 'error', from: { pathNot: '^src/data' }, to: { path: 'node_modules/(pg|mysql|mongodb)' } } ], options: { doNotFollow: { path: 'node_modules' } }};ESLint Custom Rules
Section titled “ESLint Custom Rules”module.exports = { meta: { type: 'problem', docs: { description: 'ADR-005: Disallow direct service-to-service HTTP calls', }, }, create(context) { return { ImportDeclaration(node) { if (node.source.value.includes('axios') || node.source.value.includes('node-fetch')) { if (context.getFilename().includes('/services/')) { context.report({ node, message: 'ADR-005 Violation: Services should use event bus, not direct HTTP calls' }); } } } }; }};Pre-commit Hooks
Section titled “Pre-commit Hooks”Husky + lint-staged
Section titled “Husky + lint-staged”{ "husky": { "hooks": { "pre-commit": "lint-staged && npm run adr:check" } }, "lint-staged": { "src/**/*.ts": [ "npm run adr:check:staged" ] }, "scripts": { "adr:check": "./scripts/adr-compliance.sh", "adr:check:staged": "./scripts/adr-compliance-staged.sh" }}Pre-commit Framework (Python)
Section titled “Pre-commit Framework (Python)”repos: - repo: local hooks: - id: adr-compliance name: ADR Compliance Check entry: ./scripts/adr-compliance.sh language: script files: \.(py|ts|js|java)$ stages: [commit]Scheduled Audits
Section titled “Scheduled Audits”Cron-based Full Audit
Section titled “Cron-based Full Audit”# GitHub Actions scheduled workflowname: Weekly ADR Audit
on: schedule: - cron: '0 9 * * 1' # Every Monday at 9 AM workflow_dispatch:
jobs: full-audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Run Full ADR Audit run: ./scripts/full-adr-audit.sh
- name: Create Issue on Violations if: failure() uses: actions/github-script@v6 with: script: | github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: 'ADR Compliance Violations Detected', body: 'Weekly audit found compliance issues. See workflow run for details.', labels: ['adr', 'compliance', 'automated'] })Monitoring and Alerting
Section titled “Monitoring and Alerting”Compliance Metrics
Section titled “Compliance Metrics”#!/bin/bash# Count violations by ADRecho "compliance_violations{adr=\"001\"} $(grep -rc 'mysql' src/ | grep -v ':0$' | wc -l)"echo "compliance_violations{adr=\"002\"} $(grep -rc 'direct.*call' src/services/ | wc -l)"
# Track over timeDATE=$(date +%Y-%m-%d)echo "$DATE,ADR-001,$(grep -rc 'mysql' src/ | wc -l)" >> metrics/compliance-history.csvSlack/Teams Notifications
Section titled “Slack/Teams Notifications”#!/bin/bashVIOLATIONS=$(./scripts/adr-compliance.sh 2>&1)VIOLATION_COUNT=$(echo "$VIOLATIONS" | grep -c "VIOLATION")
if [ $VIOLATION_COUNT -gt 0 ]; then curl -X POST "$SLACK_WEBHOOK_URL" \ -H 'Content-Type: application/json' \ -d "{ \"text\": \"ADR Compliance Alert: $VIOLATION_COUNT violations found\", \"blocks\": [{ \"type\": \"section\", \"text\": { \"type\": \"mrkdwn\", \"text\": \"*ADR Compliance Report*\n$VIOLATIONS\" } }] }"fiCompliance Dashboard
Section titled “Compliance Dashboard”Generate HTML Report
Section titled “Generate HTML Report”#!/bin/bashcat > compliance-dashboard.html << 'EOF'<!DOCTYPE html><html><head> <title>ADR Compliance Dashboard</title> <style> .compliant { color: green; } .violation { color: red; } .warning { color: orange; } </style></head><body> <h1>ADR Compliance Dashboard</h1> <table> <tr><th>ADR</th><th>Status</th><th>Violations</th></tr>EOF
# Add rows for each ADRfor adr in docs/adr/[0-9]*.md; do ID=$(basename "$adr" | cut -d'-' -f1) # Check compliance and add rowdone
echo "</table></body></html>" >> compliance-dashboard.htmlBest Practices
Section titled “Best Practices”Gradual Enforcement
Section titled “Gradual Enforcement”- Phase 1: Warning only (collect data)
- Phase 2: Block new violations
- Phase 3: Require remediation of existing violations
Exemption Process
Section titled “Exemption Process”# Format: file_pattern:ADR_ID:reason:expiry_datesrc/legacy/*:ADR-001:Legacy code pending migration:2024-06-01src/migrations/*:ADR-002:Migration scripts exempt:permanentDocumentation Integration
Section titled “Documentation Integration”Link compliance checks back to ADRs:
## ADR-001: Use PostgreSQL
### Compliance
**Automated Checks**:- CI pipeline: `adr-compliance.yml`- Pre-commit hook: `check-database-imports`
**Manual Review Required For**:- New database dependencies in package.json- Changes to database configuration