Monorepo Support
coverctl provides first-class support for monorepos through config inheritance, allowing you to define shared policies at the root and override them in individual packages.
Config Inheritance with extends
Section titled “Config Inheritance with extends”Use the extends field to inherit configuration from a parent file:
extends: ../../../.coverctl.yaml # Inherit from root
policy: domains: - name: auth-service match: ["./..."] min: 90 # Override parent's defaultHow Inheritance Works
Section titled “How Inheritance Works”- Relative paths: The
extendspath is resolved relative to the config file’s directory - Multi-level inheritance: Parent configs can also use
extends, creating a chain - Override behavior: Child domains with the same name override parent domains
- Merging: Child domains are merged with parent domains; exclusions are combined
Example Inheritance Chain
Section titled “Example Inheritance Chain”monorepo/├── .coverctl.yaml # Root config (default: 70%)├── libs/│ ├── .coverctl.yaml # Extends root (default: 75%)│ └── shared-utils/│ └── .coverctl.yaml # Extends libs (min: 80%)└── services/ ├── .coverctl.yaml # Extends root (default: 80%) └── payment/ └── .coverctl.yaml # Extends services (min: 95%)Monorepo Config Structure
Section titled “Monorepo Config Structure”Root Config
Section titled “Root Config”Define shared defaults and exclusions at the monorepo root:
# .coverctl.yaml (root)version: 1
policy: default: min: 70 domains: - name: core match: ["./internal/core/..."] min: 85
exclude: - "**/generated/**" - "**/mocks/**" - "**/*_mock.go" - "**/testdata/**"Package Config
Section titled “Package Config”Override or extend policies in individual packages:
extends: ../../.coverctl.yaml
policy: domains: - name: payment match: ["./..."] min: 95 # Higher threshold for critical code - name: payment-handlers match: ["./handlers/..."] min: 90
# Additional exclusions merged with parentexclude: - "./fixtures/**"Go Workspace Support
Section titled “Go Workspace Support”For Go workspaces (go.work), coverctl automatically detects the workspace structure.
Workspace Layout
Section titled “Workspace Layout”monorepo/├── go.work├── .coverctl.yaml├── libs/│ ├── go.mod│ └── shared/├── services/│ ├── api/│ │ ├── go.mod│ │ └── .coverctl.yaml│ └── worker/│ ├── go.mod│ └── .coverctl.yamlRunning from Package Directory
Section titled “Running from Package Directory”cd services/apicoverctl check # Uses services/api/.coverctl.yaml, inherits from rootRunning from Root
Section titled “Running from Root”coverctl check -c services/api/.coverctl.yamlCI Integration for Monorepos
Section titled “CI Integration for Monorepos”GitHub Actions Matrix Strategy
Section titled “GitHub Actions Matrix Strategy”Run coverage checks in parallel for each package:
name: Coverage
on: [push, pull_request]
jobs: coverage: runs-on: ubuntu-latest strategy: matrix: package: - services/api - services/worker - libs/shared
steps: - uses: actions/checkout@v4
- uses: actions/setup-go@v5 with: go-version: '1.22'
- name: Install coverctl run: go install github.com/felixgeelhaar/coverctl@latest
- name: Check coverage working-directory: ${{ matrix.package }} run: coverctl checkChanged Packages Only
Section titled “Changed Packages Only”For efficiency, only check packages with changed files:
- name: Get changed packages id: changed uses: dorny/paths-filter@v3 with: filters: | api: - 'services/api/**' worker: - 'services/worker/**' shared: - 'libs/shared/**'
- name: Check API coverage if: steps.changed.outputs.api == 'true' working-directory: services/api run: coverctl checkBest Practices
Section titled “Best Practices”1. Establish Sensible Defaults
Section titled “1. Establish Sensible Defaults”Set reasonable defaults at the root that all packages inherit:
# Root .coverctl.yamlversion: 1policy: default: min: 70 # Reasonable baseline2. Progressive Thresholds
Section titled “2. Progressive Thresholds”Use higher thresholds for critical packages:
# Critical service configextends: ../../.coverctl.yamlpolicy: domains: - name: payments match: ["./..."] min: 95 # Critical code needs high coverage3. Shared Exclusions
Section titled “3. Shared Exclusions”Define common exclusions at the root:
exclude: - "**/generated/**" - "**/mocks/**" - "**/*_test.go" - "**/testdata/**" - "**/vendor/**"4. Domain-Based Organization
Section titled “4. Domain-Based Organization”Mirror your package structure in domains:
policy: domains: - name: libs match: ["./libs/..."] min: 80 - name: services match: ["./services/..."] min: 75 - name: critical match: ["./services/payment/...", "./services/auth/..."] min: 90Troubleshooting
Section titled “Troubleshooting””module root not found” Error
Section titled “”module root not found” Error”This error occurs when coverctl cannot find a go.mod file. Solutions:
- Run from package directory:
cd packages/mypackage && coverctl check - Specify config path:
coverctl check -c packages/mypackage/.coverctl.yaml - Ensure go.mod exists: Each package should have its own
go.mod
Circular Inheritance Detected
Section titled “Circular Inheritance Detected”coverctl prevents circular config inheritance. Check your extends chains:
# BAD: Creates a cycle# a.yaml extends b.yaml# b.yaml extends a.yaml
# GOOD: Linear inheritance# child.yaml extends parent.yaml extends root.yamlConfig Not Found
Section titled “Config Not Found”Ensure paths are relative to the config file, not the current directory:
# In packages/api/.coverctl.yamlextends: ../../.coverctl.yaml # Goes up two levels to rootComplete Monorepo Example
Section titled “Complete Monorepo Example”mycompany/├── .coverctl.yaml # Root config├── go.work├── libs/│ ├── .coverctl.yaml # Extends root, 75% default│ ├── go.mod│ └── utils/│ └── utils.go└── services/ ├── .coverctl.yaml # Extends root, 80% default ├── api/ │ ├── .coverctl.yaml # Extends services, 85% for API │ ├── go.mod │ └── main.go └── payment/ ├── .coverctl.yaml # Extends services, 95% for payment ├── go.mod └── main.goRoot config:
version: 1policy: default: min: 70exclude: - "**/generated/**" - "**/mocks/**"Libs config:
extends: ../.coverctl.yamlpolicy: default: min: 75 domains: - name: libs match: ["./..."]Services config:
extends: ../.coverctl.yamlpolicy: default: min: 80Payment service config:
extends: ../.coverctl.yamlpolicy: domains: - name: payment-core match: ["./internal/core/..."] min: 95 - name: payment-handlers match: ["./handlers/..."] min: 90See Also
Section titled “See Also”- Advanced Configuration - Diff mode, merging
- CI Integration - GitHub Actions setup
- Domains - Domain configuration