Monorepo Toolkit
Affected Package Detection
What you'll learn
- How git-diff-based detection identifies changed packages
- Running affected detection locally and in CI
- Configuring exclusions and output formats
- Using affected results for selective builds and tests
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- The affected command generated in your monorepo toolkit (see Brick: Affected)
- A git history with at least one commit on your base branch
Step 1: How Affected Detection Works
The affected command compares your current branch against a base branch using git diff. It finds the changed files and maps them to their owning packages. Then it walks the dependency graph to find every transitively affected package.
git diff → changed files → owning packages → dependency graph → affected listFor example, if you change a file in packages/core_network/, any package that depends on core_network (directly or transitively) is also considered affected.
Step 2: Run Locally
Detect affected packages against your base branch:
# Compare against main (the default)
dart run monorepo_toolkit affected list
# Compare against develop
dart run monorepo_toolkit affected list --base develop
# Plain output, one package per line, for scripting
dart run monorepo_toolkit affected list --format plain
# JSON array of package names
dart run monorepo_toolkit affected list --jsonExample pretty output:
Comparing HEAD with main...
Found 3 affected package(s):
1. core_network
2. feature_auth
3. my_appThen run a command on just those packages:
dart run monorepo_toolkit affected analyze --base develop
dart run monorepo_toolkit affected test --coverage --base develop
dart run monorepo_toolkit affected run -- "dart format --set-exit-if-changed ."Step 3: Configure Exclusions
Create affected.yaml in your monorepo root to control what counts as a change:
affected:
base_branch: develop
# Packages always included regardless of changes
always_include:
- my_app
# Glob patterns to ignore when detecting changes
ignore:
- "devtools/**"
- ".github/**"
- ".claude/**"
- "**/*.md"
# Extra predefined commands (analyze, test, format are built in)
commands:
lint: "dart run custom_lints ."- base_branch: Default comparison branch when
--baseis not given - always_include: Packages added to the affected set on every run
- ignore: Glob patterns whose changes are disregarded
- commands: Named commands merged with the built-in
analyze,testandformat
Step 4: Use in CI for Selective Builds
The real power of affected detection is skipping unchanged packages in CI. This can reduce pipeline time from 20+ minutes to under 5 minutes in large monorepos.
jobs:
detect:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.affected.outputs.packages }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: dart-lang/setup-dart@v1
- name: Detect affected
id: affected
run: |
packages=$(dart run monorepo_toolkit affected list --json --base origin/main)
echo "packages=$packages" >> "$GITHUB_OUTPUT"
test:
needs: detect
if: ${{ needs.detect.outputs.packages != '[]' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package: ${{ fromJSON(needs.detect.outputs.packages) }}
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
- name: Test ${{ matrix.package }}
run: dart run monorepo_toolkit affected test --base origin/mainThe affected brick ships a fuller, annotated version as devtools/scripts/affected/ci-affected-matrix.yml.example. For simpler pipelines, replace the matrix with a single dart run monorepo_toolkit affected test --base origin/main step.
Step 5: Understand Transitive Detection
Affected detection follows the dependency graph. Given this structure:
app → feature_auth → core_network → core_commonIf you change core_network, the affected list includes:
core_network(directly changed)feature_auth(depends oncore_network)app(depends onfeature_auth)
But core_common is not affected because it does not depend on core_network.
Key Customization Points
| Customization | Where to Change |
|---|---|
| Base branch for comparison | --base flag (default main) or base_branch in affected.yaml |
| Output format | affected list --format pretty|plain, or --json |
| Always include packages | affected.yaml - always_include list |
| Ignore file patterns | affected.yaml - ignore list |
| Fail behavior | --no-fail-fast on analyze, test, run |
Next Steps
- Set up test coverage to measure coverage on affected packages only
- Visualize your dependency graph to understand package relationships