Skip to content

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 identifies which files changed, maps those files back to their owning packages, and then resolves the full dependency graph to find all transitively affected packages.

git diff → changed files → owning packages → dependency graph → affected list

For 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:

bash
# Compare against main
dart run devtools affected --base main

# Compare against develop
dart run devtools affected --base develop

# Output as JSON for scripting
dart run devtools affected --base main --format json

Example text output:

Affected packages (3):
  - packages/core_network
  - packages/feature_auth
  - app

Step 3: Configure Exclusions

Create affected_config.yaml in your monorepo root to exclude packages that should never trigger downstream rebuilds:

yaml
exclude:
  - packages/app_debugger_impl
  - packages/internal_tools
  - devtools

ignore_files:
  - "*.md"
  - "*.txt"
  - ".gitignore"
  • exclude — Packages to skip entirely (changes in these won't trigger dependents)
  • ignore_files — File patterns to ignore when detecting changes

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.

yaml
jobs:
  detect:
    runs-on: ubuntu-latest
    outputs:
      affected: ${{ steps.affected.outputs.packages }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: subosito/flutter-action@v2
      - name: Detect affected
        id: affected
        run: |
          packages=$(dart run devtools affected --base origin/main --format json)
          echo "packages=$packages" >> $GITHUB_OUTPUT

  test:
    needs: detect
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: subosito/flutter-action@v2
      - name: Test affected packages
        run: |
          for pkg in $(echo '${{ needs.detect.outputs.affected }}' | jq -r '.[]'); do
            echo "Testing $pkg..."
            (cd "$pkg" && flutter test)
          done

Step 5: Understand Transitive Detection

Affected detection follows the dependency graph. Given this structure:

app → feature_auth → core_network → core_common

If you change core_network, the affected list includes:

  • core_network (directly changed)
  • feature_auth (depends on core_network)
  • app (depends on feature_auth)

But core_common is not affected because it does not depend on core_network.

Key Customization Points

CustomizationWhere to Change
Base branch for comparison--base flag (default: main)
Output format--format flag (text, json)
Exclude packagesaffected_config.yamlexclude list
Ignore file patternsaffected_config.yamlignore_files list
Dependency resolution depthdependency_graph.dart — adjust traversal

Next Steps

Built by Banua Coder