# CI/CD Validation Contract # Defines automated checks that must pass before merging changes version: "1.0" description: "GitHub Actions CI validation requirements for nixos-matrix-platform-template" # CI workflow configuration workflow: name: "CI" file: ".github/workflows/ci.yml" triggers: - push - pull_request concurrency: group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true # Jobs and their contracts jobs: # Job 1: Nix validation and builds validate: name: "Nix Validation" runs-on: "ubuntu-latest" timeout-minutes: 10 failure_action: block-merge steps: - step: "checkout" uses: "actions/checkout@v4" contract: must_succeed: true failure_blocks_merge: true - step: "install-nix" uses: "cachix/install-nix-action@v25" with: nix_path: "nixpkgs=channel:nixos-24.05" contract: must_succeed: true failure_blocks_merge: true max_duration_seconds: 120 - step: "nix-flake-check" name: "Run nix flake check" command: "nix flake check --all-systems" contract: exit_code: 0 must_succeed: true failure_blocks_merge: true max_duration_seconds: 120 success_criteria: - "All flake outputs validate successfully" - "No syntax errors in Nix files" - "All module options type-check correctly" - step: "build-example-vps" name: "Build example VPS configuration" command: "nix build .#nixosConfigurations.example-vps.config.system.build.toplevel" contract: exit_code: 0 must_succeed: true failure_blocks_merge: true max_duration_seconds: 180 success_criteria: - "example-vps.nix builds without errors" - "All imported modules resolve correctly" - "result symlink created" - step: "build-example-dev" name: "Build example dev configuration" command: "nix build .#nixosConfigurations.example-dev.config.system.build.toplevel" contract: exit_code: 0 must_succeed: true failure_blocks_merge: true max_duration_seconds: 180 success_criteria: - "example-dev.nix builds without errors" - "All imported modules resolve correctly" - "result symlink created" # Job 2: Security scanning security: name: "Security Scanning" runs-on: "ubuntu-latest" timeout-minutes: 5 failure_action: block-merge steps: - step: "checkout" uses: "actions/checkout@v4" with: fetch-depth: 0 # Full history for gitleaks contract: must_succeed: true failure_blocks_merge: true - step: "gitleaks-scan" name: "Run gitleaks secret scanner" uses: "gitleaks/gitleaks-action@v2" env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" contract: exit_code: 0 must_succeed: true failure_blocks_merge: true max_duration_seconds: 60 success_criteria: - "Zero secrets detected" - "No Matrix tokens (syt_)" - "No Slack tokens (xox*)" - "No age keys (AGE-SECRET-KEY-*)" - "No personal emails or domains" failure_message: | Secret detected in commit! This is a CRITICAL security issue. DO NOT MERGE until secrets are removed. Steps to fix: 1. Identify the secret in the gitleaks report above 2. Remove or replace with placeholder (e.g., REPLACE_WITH_TOKEN) 3. If secret is in git history, consider force-push (if safe) 4. Re-run CI to verify fix # Job 3: Documentation validation docs: name: "Documentation Validation" runs-on: "ubuntu-latest" timeout-minutes: 5 failure_action: warn # Non-blocking for docs-only changes steps: - step: "checkout" uses: "actions/checkout@v4" contract: must_succeed: true - step: "markdown-lint" name: "Lint markdown files" uses: "DavidAnson/markdownlint-cli2-action@v14" with: globs: "**/*.md" contract: exit_code: 0 must_succeed: false # Warnings only max_duration_seconds: 30 success_criteria: - "Markdown files follow style guide" - "No broken relative links" - "Headers properly formatted" - step: "check-links" name: "Check for broken links" uses: "gaurav-nelson/github-action-markdown-link-check@v1" with: use-quiet-mode: "yes" config-file: ".github/markdown-link-check.json" contract: exit_code: 0 must_succeed: false # Warnings only max_duration_seconds: 60 success_criteria: - "All internal links resolve" - "External links return 200 OK" # Branch protection rules (configured via GitHub settings) branch_protection: branches: - name: "main" protections: require_pull_request: true require_approvals: 0 # v1.0 single maintainer require_status_checks: true required_checks: - "Nix Validation" - "Security Scanning" dismiss_stale_reviews: false require_code_owner_reviews: false restrict_pushes: false # Allow maintainer to push directly if needed allow_force_pushes: false allow_deletions: false # Status check requirements status_checks: strict: true # Require branches to be up-to-date before merging contexts: - "validate" # Job: Nix Validation - "security" # Job: Security Scanning # Validation matrix for different scenarios validation_scenarios: # Scenario 1: New module added new_module: required_checks: - "nix flake check passes" - "Module builds successfully" - "gitleaks finds no secrets" - "Module imported by at least one example configuration" - "Module documented in README or relevant doc" additional_manual_review: - "Module follows existing patterns" - "Options are well-documented" - "Security hardening applied (if applicable)" - "No personal configuration remains" # Scenario 2: Documentation change docs_only: required_checks: - "markdown-lint passes (or warnings explained)" - "link-check passes (or broken links justified)" - "gitleaks still passes (no secrets in examples)" additional_manual_review: - "Technical accuracy verified" - "No personal infrastructure references" - "Examples use generic domains/IPs" # Scenario 3: Example configuration change config_change: required_checks: - "nix flake check passes" - "Modified configuration builds successfully" - "gitleaks finds no secrets" - "No personal domains/IPs introduced" additional_manual_review: - "Configuration still demonstrates intended use case" - "Comments explain key options" - "Secrets properly templated" # Scenario 4: CI/CD workflow change ci_change: required_checks: - "Workflow syntax valid (GitHub validates)" - "Test run completes successfully" - "No secrets exposed in workflow logs" additional_manual_review: - "Change doesn't weaken security checks" - "Timeout values reasonable" - "Failure actions appropriate (block vs warn)" # Failure handling failure_policies: nix_build_failure: severity: critical action: block-merge notification: "PR author + maintainers" auto_comment: | ## Build Failure The Nix build failed for this PR. This prevents merging. **Common causes:** - Syntax error in .nix file - Missing import or dependency - Type error in module options - Invalid configuration value **To debug:** 1. Run `nix flake check` locally 2. Run `nix build .#nixosConfigurations.` locally 3. Check the error message for file and line number 4. Fix the issue and push updated commit CI will automatically re-run on push. secret_detected: severity: critical action: block-merge notification: "PR author + maintainers + security team" auto_comment: | ## ⚠️ SECRET DETECTED ⚠️ gitleaks has detected a potential secret in this PR. **CRITICAL: DO NOT MERGE until resolved.** **Steps to fix:** 1. Review the gitleaks report in the CI logs 2. Identify the secret (token, password, key, etc.) 3. Replace with placeholder or remove entirely 4. If secret is in git history: - Consider force-push to remove (if safe) - Or add to .gitignore and create new commit 5. Ensure secret is listed in secrets.yaml.example with generation instructions 6. Re-run CI to verify fix **If this is a false positive:** - Add pattern to .gitleaksignore (with justification in comment) - Document why this is safe in PR description docs_link_failure: severity: warning action: warn notification: "PR author" auto_comment: | ## Documentation Link Check Warning Some links in documentation may be broken. This is a **warning** - the PR can still be merged if links will be fixed soon. **Common causes:** - Link to external site that's temporarily down - Link to doc that hasn't been created yet - Typo in relative path **To fix:** 1. Check the link-check report in CI logs 2. Fix broken links or remove them 3. For external links, verify they're stable/permanent # Performance budgets performance: ci_total_time: target: "5 minutes" warning: "8 minutes" critical: "10 minutes" nix_flake_check: target: "1 minute" warning: "2 minutes" critical: "3 minutes" nix_build_per_config: target: "2 minutes" warning: "3 minutes" critical: "5 minutes" gitleaks_scan: target: "20 seconds" warning: "40 seconds" critical: "60 seconds" # Notification channels notifications: on_failure: - github-pr-comment - github-pr-status on_success: - github-pr-status on_critical_failure: - github-pr-comment - github-pr-status - email # If configured # Metrics to track (post-deployment) metrics: ci_success_rate: target: "> 95%" measurement: "successful CI runs / total CI runs" false_positive_rate: target: "< 5%" measurement: "false secret detections / total gitleaks runs" average_ci_duration: target: "< 5 minutes" measurement: "mean duration of all CI runs" time_to_fix_failures: target: "< 1 hour" measurement: "time from failure to green CI" # Integration with GitHub features github_integrations: status_checks: enabled: true required_for_merge: ["validate", "security"] auto_merge: enabled: false # Manual merge for v1.0 discussions: enabled: true categories: ["Q&A", "Ideas", "Show and Tell"] issues: templates: - bug_report.yml - feature_request.yml code_owners: file: ".github/CODEOWNERS" content: | # Code ownership for nixos-matrix-platform-template * @maintainer-username # Security-critical files require extra review .github/workflows/ @maintainer-username modules/matrix-secrets/ @maintainer-username *.sops.yaml @maintainer-username # Pre-commit hooks (optional for contributors) pre_commit: config_file: ".pre-commit-config.yaml" hooks: - id: "gitleaks" name: "gitleaks" entry: "gitleaks protect --staged" language: "system" pass_filenames: false - id: "nix-flake-check" name: "nix flake check" entry: "nix flake check" language: "system" pass_filenames: false stages: [commit] # Success criteria for CI system itself ci_success_criteria: - All jobs complete within performance budgets - Zero false negatives (secrets not detected) - < 5% false positives (valid code flagged as secret) - Status checks correctly block/warn as configured - Auto-comments provide helpful guidance - Notifications reach appropriate parties - Metrics tracked and visible in repository insights