Skip to content
Back to archipelago.banuacoder.com

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 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 (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 --json

Example pretty output:

Comparing HEAD with main...
Found 3 affected package(s):
  1. core_network
  2. feature_auth
  3. my_app

Then run a command on just those packages:

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

yaml
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 --base is 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, test and format

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:
      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/main

The 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_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) or base_branch in affected.yaml
Output formataffected list --format pretty|plain, or --json
Always include packagesaffected.yaml - always_include list
Ignore file patternsaffected.yaml - ignore list
Fail behavior--no-fail-fast on analyze, test, run

Next Steps ​