Deployment & CI/CD
Build Prepare for Production
What you'll learn
- Configuring
build_prepare.yamlto swap debug tooling for its noop twins - Why the build flavor, not the build mode, decides what ships
- Running build_prepare and its leak guard in CI
- Verifying that debug tools are excluded from production binaries
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- A monorepo_toolkit already generated at
devtools/ - At least one feature using the Impl + Noop pattern (e.g.,
app_debugger)
Step 1: Understand the Noop/Impl Pattern
Archipelago uses build-time dependency swapping to remove debug tools from production-flavor builds. Each feature that needs this has two implementations, for example:
features/app_debugger/
├── app_debugger/ # Impl — real debug UI, ships in internal flavors
└── app_debugger_noop/ # Noop — empty stubs, ships in the production flavorBoth packages expose the same class names (for example AppDebuggerSDKImpl). The noop package has stubs that do nothing. The impl package has the real tools. build-prepare swaps the dependency in pubspec.yaml and rewrites the matching package: imports before building.
Internal release builds (QA, alpha, beta, staging) keep the real tools, so testers can use the debugger and the Blackbox recorder. Only the production flavor gets the noop twins.
Step 2: Configure build_prepare.yaml
build_prepare.yaml lives in the monorepo root. A fresh project ships with no mappings. The launch tracker and monitoring ship in every flavor, gated by consent. Bricks that need stripping, such as app_debugger, add their own entries between the archipelago:build_prepare:mappings markers.
build_prepare:
internal_flavors: [development, staging, alpha, beta]
mappings:
- debug: app_debugger
release: app_debugger_noop
debug_only: []
target_paths:
- apps/my_app- internal_flavors - Flavors that keep the debug tooling. Every other flavor, including a new one you add later, gets the noop twins
- mappings[].debug - The package used in internal flavors (full implementation)
- mappings[].release - Its noop twin, used in the production flavor
- mappings[].flavors - Optional. Limits the mapping to the listed flavors
- debug_only - Packages with no noop twin that must never appear in a production build (the Blackbox recorder adds its capability plugins here)
target_paths lists the app directories whose pubspec.yaml and Dart imports are rewritten.
Step 3: Run Build Prepare Locally
Pass the same flavor the build uses:
# Production: swap debug tooling for the noop twins
dart run monorepo_toolkit build-prepare --flavor production
flutter build appbundle --flavor production
dart run monorepo_toolkit build-prepare --check-leaks --flavor production
dart run monorepo_toolkit build-prepare --restore
# Internal flavor: the tooling stays, even in release mode
dart run monorepo_toolkit build-prepare --flavor alphamelos run build-prepare:production runs the production swap (build-prepare:release is an alias), and melos run build-prepare:debug goes back. Do not run the production swap before an internal build.
After build-prepare --flavor production, the target pubspec.yaml files have their dependency references updated. For example, in apps/my_app/pubspec.yaml:
# Before (internal flavors)
dependencies:
app_debugger:
path: ../../features/app_debugger/app_debugger
# After (production flavor)
dependencies:
app_debugger_noop:
path: ../../features/app_debugger/app_debugger_noopStep 4: Integrate with CI
The generated Fastfile already does all of this. For a custom workflow, run build-prepare with the build's flavor before the build, the leak guard after it, and restore at the end:
- name: Prepare dependencies for the flavor
run: dart run monorepo_toolkit build-prepare --flavor "$FLAVOR"
- name: Build
run: flutter build appbundle --flavor "$FLAVOR"
- name: Check debug tooling leak
run: dart run monorepo_toolkit build-prepare --check-leaks --flavor "$FLAVOR"
- name: Restore dependencies
if: always()
run: dart run monorepo_toolkit build-prepare --restoreThe if: always() on the restore step resets dependencies even if the build fails, keeping your working tree clean.
Step 5: Verify Debug Stripping
After a production build, the leak guard reads the app's dependency graph, pubspec.lock and .flutter-plugins-dependencies:
dart run monorepo_toolkit build-prepare --check-leaks --flavor production
# Leak check passed: no debug-only package resolved.It exits 1 and lists every leaked package otherwise, for example when the app depends on app_blackbox_recording directly. To preview a swap, run it and look at git diff --stat, then --restore.
Key Customization Points
| Customization | Where to Change |
|---|---|
Add a new internal flavor (e.g. qa) | internal_flavors in build_prepare.yaml |
| Add new debug/noop pairs | build_prepare.yaml - add a new mapping entry |
| Guard a package that has no noop twin | debug_only list |
| Target additional apps | target_paths list |
| Use a different config file | --config flag |
| Undo the last swap | --restore flag |
Next Steps
- Set up GitHub Actions to automate the full build pipeline
- Configure Fastlane for store deployment after build_prepare