Skip to content

Architecture

Monorepo Toolkit ​

The monorepo toolkit is a collection of CLI scripts generated into your project at devtools/scripts/, wired together by the monorepo_toolkit CLI at devtools/monorepo_toolkit/. These tools help you manage a large Flutter monorepo — from dependency analysis to build preparation to asset optimization.

Every command runs from your project root. Most also have a melos shortcut:

bash
dart run monorepo_toolkit <command> [subcommand] [flags]
melos run <command>:<subcommand>      # shortcut, e.g. melos run graph:cycles

Extra arguments go after -- with melos, for example melos run env:switch -- production. Pass --verbose for noisy logging and --version to print the toolkit version.

Which scripts you get depends on the tier you generate with: Free projects include the core scripts, Asset Generator and the UI Kit Manager. Pro adds Coverage, Size Guard, Asset Optimizer, Size Analyzer, Permission Guard and Detect New Library. Enterprise adds Affected. The generated CI calls only the scripts your tier has: Pro and Enterprise projects get pr-checks.yml and update-baselines.yml (GitHub) or the guard, analysis and baseline jobs in .gitlab-ci.yml (GitLab). Every tier gets the test workflow (ci.yml on GitHub, the test job on GitLab) and the build workflows. A melos shortcut for a script you did not generate will fail until its brick is installed.

Upgrading a Free project to Pro ​

Run archipelago generate for each of size_guard, detect_new_library, size_analyzer and permission_guard. When the last of the four is installed, the generator adds the Pro CI: it writes .github/workflows/pr-checks.yml and update-baselines.yml if they are missing (GitHub), or appends the guard, analysis and baseline jobs to .gitlab-ci.yml between # archipelago:pro_ci:start and # archipelago:pro_ci:end (GitLab). The CI provider comes from ci.git_provider in archipelago.yaml. The workflows call all four tools, so nothing is added before the fourth is installed. Existing files are never overwritten, so running the commands again changes nothing.

CI test scope ​

The generated CI runs only what a change can break, and runs it once per change. The same rules drive Archipelago's own CI.

EventGitHub ActionsGitLab CIWhat runs
PR/MR into developpull_requestmerge_request_eventAnalyze and test the changed packages plus their dependents. Pro: size guard and new-library check
Direct push to developpush (develop only)branch pipeline (develop only)Same, diffed against the last commit ci.yml passed on develop (GitLab: the push's before commit)
Push of a merged PR/MR to developpushbranch pipelineNothing: the PR/MR run tested it. GitHub asks the API whether the head commit belongs to a merged PR; GitLab looks for the "See merge request" line of its default merge commit message, so squash and fast-forward merges are tested again
PR/MR into main, or from release/* / hotfix/*pull_requestmerge_request_eventEvery package. Pro: also the APK/IPA builds with size analysis and permission guard
Manual runworkflow_dispatchweb pipelineEverything. On develop, GitLab's manual pipeline also refreshes the baselines
Push to main, to a PR/MR branch, to release/* / hotfix/*noneno pipelineNothing: the PR/MR run already covered the change
Tag mobile/v*release-mobile.yml (release builds, no tests)no pipelineGitLab has no release job: run Fastlane from your release process
  • devtools/ci/scope.sh decides the scope (full, base, version_only, code). It needs the full history (fetch-depth: 0, GIT_DEPTH: 0), because a shallow clone has no merge base. Run it locally with CI_SCOPE_BASE=<ref> bash devtools/ci/scope.sh. On GitHub it prints a warning when it cannot find the last passing run (first run, or an API error) and falls back to the push's before commit.
  • A change to a workspace-wide file runs everything: root pubspec.yaml/pubspec.lock, analysis_options.yaml, build_prepare.yaml, flavors.yaml, devtools/ci/, .github/workflows/, .gitlab-ci.yml. No package owns these files, so an affected-only run would test nothing.
  • devtools/ci/test.sh bootstraps, generates code, then runs melos run analyze and melos run test on a full run. Otherwise it uses monorepo_toolkit affected analyze --no-fatal-infos --no-fatal-warnings and affected test with --base <base> when the project has the Enterprise affected tool, and melos exec --diff=<base> --include-dependents when it does not. A package without lib/ or test/ is skipped inside the command; melos's --dir-exists filter is not used, because melos applies it before --include-dependents and would drop the dependents of a changed package that has no tests.
  • A change that only bumps version: in pubspec.yaml files and edits CHANGELOG.md, an empty back-merge, and a docs-only change (*.md, docs/) skip the suite.
  • Jobs are gated with job-level if: (GitHub) and rules: (GitLab), not workflow paths: filters. GitHub reads paths: from the first 300 changed files only, and a workflow skipped by paths: leaves a required check pending forever.
  • Require ci-ok in branch protection (GitHub). A job skipped by if: reports success, so if scope failed, the skipped test job would count as passing; ci-ok fails unless scope passed and test passed or was skipped on purpose. On GitLab the test job runs the scope itself and fails when it fails.
  • Test workflows cancel the older run of the same PR or branch: concurrency with cancel-in-progress: true on GitHub; workflow:auto_cancel:on_new_commit: interruptible on GitLab, where only MR test jobs are interruptible (rules:interruptible). A GitLab develop push pipeline is never cancelled, as it diffs only from the commit before its own push. The GitLab file needs GitLab 16.10 or later. Release, deploy and baseline jobs never cancel.
  • Heavy jobs are release-only or manual: the Pro APK/IPA size and permission checks run on release PRs/MRs and manual runs, and update-baselines.yml runs only by hand (run it on develop after a release). Goldens stay out of CI (melos run test:golden).

The workflows are self-contained and do not call the shared banua-coder/banua-coder-workflow reusable workflows, which offer the same scope options for other repos (2026-10-15 amendment).

Projects generated before flutter_modular_monorepo 5.1.0 have no ci.yml/test job and run the Pro checks on every PR. To adopt the scope, copy devtools/ci/, .github/workflows/ci.yml, pr-checks.yml and update-baselines.yml (or .gitlab-ci.yml) from a fresh project (archipelago create --output <scratch dir>).

TIP

The toolkit is generated into your project so it works in CI without requiring the Archipelago CLI at build time. You own the code and can customize it.

Detailed Guides ​

Each script has a dedicated guide with config formats, examples, and CI integration:

Core Scripts (Free):

Extension Scripts (Pro/Enterprise):


affected ​

Detect which packages changed since the last release and run targeted operations only on affected packages.

affected list ​

List packages affected by changes since the base branch (--base, default main).

bash
melos run affected:list
dart run monorepo_toolkit affected list --base develop --json

affected analyze ​

Run static analysis only on affected packages.

bash
melos run affected:analyze

affected test ​

Run tests only on affected packages.

bash
melos run affected:test

affected run ​

Run an arbitrary shell command only on affected packages.

bash
melos run affected:run -- "dart format --set-exit-if-changed ."

Other affected subcommands ​

affected format, affected fix-check, affected golden, affected build (app packages only, --platform apk|aab|ios|ipa), affected license and affected devtool (size-guard, dependency-graph, coverage) have no melos shortcut. Call them with dart run monorepo_toolkit affected <subcommand>. See Affected Packages for flags.

TIP

Use affected in CI to skip unchanged packages and reduce pipeline time. It compares against the merge base of your PR branch.


build-prepare ​

Swap debug tooling for its no-op twins in production-flavor builds, or restore it. The build flavor decides: --flavor <f> keeps the tooling when <f> is in internal_flavors and swaps it otherwise. Flags: --flavor/-f, --check-leaks (after a build, fail if a debug-only package resolved), --restore/-r (revert the last swap from .build_prepare.state.json) and --config/-c (default build_prepare.yaml). release and debug stay as explicit-mode aliases.

build-prepare --flavor <flavor> ​

Prepare dependencies for a build of that flavor. Internal flavors keep the debug tooling; every other flavor gets the noop twins.

bash
dart run monorepo_toolkit build-prepare --flavor production
melos run build-prepare:production   # same; build-prepare:release is an alias

build-prepare --check-leaks --flavor <flavor> ​

Run after the build. Fails a production-flavor build that still resolves a mapped debug package or a debug_only package.

build-prepare --restore / build-prepare debug ​

Restore the debug/development dependencies.

bash
melos run build-prepare:debug
How it works

Reads build_prepare.yaml at the workspace root, swaps the package names in the pubspec.yaml files under target_paths, and rewrites the matching package: imports in their Dart files. Debug and noop packages export the same class names, so the app compiles with either.

yaml
# build_prepare.yaml
build_prepare:
  internal_flavors: [development, staging, alpha, beta]
  mappings:
    - debug: app_debugger
      release: app_debugger_noop
  target_paths:
    - apps/your_app

See Build Prepare for the full format.

WARNING

Always restore (build-prepare --restore) after a production build, and never run the production swap before an internal build. The Fastlane integration handles both automatically.


coverage ​

Manage test coverage across the monorepo.

coverage init ​

Initialize coverage configuration for all packages.

bash
melos run coverage:init

coverage run ​

Run tests with coverage collection.

bash
melos run coverage:run

coverage combine ​

Combine coverage reports from all packages into a single report.

bash
melos run coverage:combine

coverage check ​

Check that coverage meets the minimum threshold.

bash
melos run coverage:check

Thresholds come from coverage.yaml at the workspace root (min_line_threshold, min_branch_threshold, min_function_threshold, with per-package overrides). coverage check takes no flags. See Test Coverage.

coverage full ​

Run the full coverage pipeline: test, combine, and check. Flags: --concurrency/-j (default 1) and --[no-]fail-fast.

bash
melos run coverage:full
dart run monorepo_toolkit coverage full -j 4

There is also melos run coverage:run:parallel, which runs flutter test --coverage in 4 packages at once.

TIP

Use coverage full in CI as a single command that runs tests, combines reports, and fails the build if coverage drops below the threshold.


deps ​

Manage dependencies across the monorepo.

deps add ​

Add a pub.dev dependency to the centralized shared/dependencies package, optionally re-exporting it from a barrel file.

bash
melos run deps:add -- freezed_annotation -v "^2.4.0" -c models

Flags: --version/-v, --category/-c (barrel file).

deps remove ​

Remove a dependency from shared/dependencies and its barrel exports.

bash
melos run deps:remove -- freezed_annotation --force

Flag: --force/-f skips the confirmation prompt.

deps internal ​

Add one workspace package as a dependency of another (the relative path is computed for you).

bash
melos run deps:internal -- <source-package> <target-package>

deps external ​

Add a pub.dev dependency directly to one package.

bash
melos run deps:external -- <package-name> <dependency> -v "^5.4.0" --dev

Flags: --version/-v, --dev/-d. See Dependency Manager.

deps check, deps align, deps diagnose ​

Find direct pins that drift between packages or duplicate the barrels (check), set one constraint everywhere (align <package> [constraint]), and explain a pub get version-solving failure (diagnose, --write-overrides is opt-in).

bash
melos run deps:check
melos run deps:align -- dio
melos run deps:diagnose

env ​

Manage environment variables and flavor configurations.

env init ​

Initialize environment configuration for the project. Flags: --var/-v (repeatable) and --force.

bash
melos run env:init

env add ​

Add a new environment variable to every flavor's .env file and to env.dart.

bash
melos run env:add -- API_SECRET

env sync ​

Sync the flavor enum with the current flavors.yaml configuration.

bash
melos run env:sync

env switch ​

Copy .env.<flavor> to .env so build_runner picks up the correct secrets for a flavor. Run this before every build. --if-missing only writes .env when it does not exist yet (this is what melos run env:ensure runs after every melos bootstrap).

bash
melos run env:switch -- development
melos run env:switch -- production
# Or invoke the toolkit directly:
dart run monorepo_toolkit env switch development
Build workflow
bash
melos run env:switch -- development
dart run build_runner build --delete-conflicting-outputs
flutter build apk --flavor development

See the Environment Sync guide for CI integration and the full command reference.

Flavor workflow
  1. Define flavors in flavors.yaml at the workspace root
  2. Run env:sync to generate the Dart enum
  3. Each flavor has its own .env.<flavor> file (.env.development, .env.staging, .env.production)
  4. Run env switch <flavor> to copy the active flavor to .env
  5. Run build_runner build to generate obfuscated Dart code from .env

flavor ​

Manage app flavors (variants like dev, staging, production).

flavor list-apps ​

List all apps in the workspace that support flavors.

bash
melos run flavor:list-apps

flavor list ​

List all defined flavors for an app.

bash
melos run flavor:list -- --app my_app

flavor add ​

Add a new flavor to an app.

bash
melos run flavor:add -- --app my_app --name qa

Flags: --app/-a and --name/-n (required), --app-name, --suffix/-s, --generate/-g.

flavor generate ​

Generate native flavor files (flutter_flavorizr), launcher icons, splash screens, IDE run configurations and main_<flavor>.dart entry points.

bash
melos run flavor:generate -- --app my_app

Flags: --app/-a (required), --force/-f, --check, and --no-splash, --no-icons, --no-ide-configs, --no-main-files.

flavor sync ​

Sync flavor definitions between the global flavors.yaml and the apps.

bash
melos run flavor:sync

Flags: --app/-a, --from-apps, --init.


graph ​

Analyze and visualize the dependency graph of the monorepo.

graph visualize ​

Generate a visual dependency graph. --format/-f is text (default), dot, mermaid or json; --output/-o writes to a file.

bash
melos run graph:visualize -- --format mermaid -o graph.md

graph stats ​

Show dependency statistics (counts, depth, fan-in/fan-out).

bash
melos run graph:stats

graph cycles ​

Detect circular dependencies in the workspace. Exits non-zero when cycles exist, unless --no-fail is passed.

bash
melos run graph:cycles

WARNING

Circular dependencies break the layered architecture. Run graph:cycles in CI to prevent them from being introduced.

graph validate ​

Validate the dependency graph against architecture rules (layer violations, forbidden dependencies).

bash
melos run graph:validate

size-guard ​

Monitor and enforce app binary size budgets.

size-guard check ​

Scan the workspace for files above a size threshold (default 500KB), such as unoptimized images, fonts or videos.

bash
melos run size-guard:check
dart run monorepo_toolkit size-guard check --changed-only --base develop

Flags: --threshold/-t, --extension/-e, --ignore/-i, --config/-c (default size_guard.yaml), --stats/-s, --changed-only, --base/-b.

TIP

Use --changed-only as a PR gate so it only flags files the branch introduced. See Size Guard.


size-analyzer ​

Parse the JSON from flutter build --analyze-size, list the top size contributors and diff against a committed baseline. No melos shortcut.

bash
dart run monorepo_toolkit size-analyzer analyze --json build/app-size.json --baseline size_baseline/android.json
dart run monorepo_toolkit size-analyzer update-baseline --json build/app-size.json --output size_baseline/android.json

See Size Analyzer.


permission-guard ​

Extract permissions from a compiled APK or IPA and diff them against a committed baseline. No melos shortcut.

bash
dart run monorepo_toolkit permission-guard android --apk build/app/outputs/flutter-apk/app-release.apk
dart run monorepo_toolkit permission-guard ios --ipa build/ios/ipa/MyApp.ipa
dart run monorepo_toolkit permission-guard update-baseline --platform android --artifact app-release.apk

See Permission Guard.


detect-new-library ​

Report direct dependencies added to pubspec.yaml files in a PR (warn-only unless --fail or fail_on_new: true). No melos shortcut.

bash
dart run monorepo_toolkit detect-new-library check --base develop

See Detect New Library.


assets ​

Manage and optimize image and media assets.

assets optimize ​

Optimize SVG, PNG and JPEG assets using svgo, oxipng and jpegoptim (install them first). Flags: --dry-run/-d, --config/-c.

bash
melos run assets:optimize

changelog ​

Write a CHANGELOG.md section from the commits since the last matching tag, grouped by Conventional Commit type. Flags: --package, --tag-pattern, --version, --all, --dry-run/-d. See Changelog.

bash
melos run changelog -- --version 1.4.0

asset-gen ​

Generate Dart constants for assets.

asset-gen generate ​

Generate type-safe asset enums from files in the asset package's assets/ directory. Flags: --dry-run/-d, --package/-p, --prefix.

bash
melos run asset-gen:generate
Example output
dart
// Generated — do not modify
class AppAssets {
  static const String logo = 'assets/images/logo.png';
  static const String onboarding1 = 'assets/images/onboarding_1.svg';
}

ui-kit ​

Manage UI kit components in the design system package.

ui-kit list ​

List all registered UI kit components.

bash
melos run ui-kit:list

ui-kit add ​

Add a new component to the UI kit.

bash
melos run ui-kit:add

ui-kit remove ​

Remove a component from the UI kit.

bash
melos run ui-kit:remove

ui-kit status ​

Show which components are installed and how many are available.

bash
melos run ui-kit:status

ui-kit create ​

Scaffold a new custom component (--name, --layer, --dependency) and register it.

bash
melos run ui-kit:create

theme-variant ​

Generate theme variant files from a YAML config (--config/-c required, --prefix, --ui-kit-path).

bash
melos run theme-variant -- --config theme_variants.yaml

workspace ​

Workspace-level setup and configuration.

workspace setup ​

One-time migration of the root pubspec.yaml workspace list from explicit paths to Dart 3.11+ glob patterns. Does nothing if globs are already in use. --dry-run/-n previews.

bash
melos run workspace:setup

workspace fix-resolution ​

Add the missing resolution: workspace field to every workspace package. --dry-run/-n previews.

bash
dart run monorepo_toolkit workspace fix-resolution

workspace validate ​

Check resolution: workspace on all packages and detect duplicate dependency_overrides. Exits 1 on problems, so it is CI-safe.

bash
dart run monorepo_toolkit workspace validate

TIP

To set up a fresh clone, run melos bootstrap. It also seeds .env through the env:ensure hook.


app ​

Manage app targets in the monorepo.

app create ​

Scaffold a new app target in the apps/ directory.

bash
melos run app:create -- --name my_app --org com.example

Flags: --name/-n (required, snake_case), --org/-o (default com.example), --domain/-d.


run ​

Run an app with interactive app, flavor and device selection. Flags: --app/-a, --flavor/-f, --device/-d, --no-prepare.

bash
melos run run:app

See Run App.


prepare ​

Prepare the workspace for a flavor in one idempotent step: env switch, envied regeneration and build-prepare. run calls it for you.

bash
dart run monorepo_toolkit prepare --flavor production

Flags: --flavor/-f (required), --mode/-m (auto default: the flavor decides, internal flavors keep the debug tooling and every other flavor gets the noop twins; debug or release force a direction), --app-config-path.


generate host-registry ​

Regenerate the marker regions of an app's injector.dart from each package's archipelago: di_modules manifest.

bash
melos run di:check       # same as: generate host-registry --check
dart run monorepo_toolkit generate host-registry --app my_app

Flags: --app/-a, --target/-t, --root, --prune, --check (report drift without writing, exit 1 if it would change).

host registryThe injector follows the manifests, and CI fails when it does not

Step 1 of 5, before: The app injector lists every package module between archipelago: markers. This package has no DI module declared yet, so the injector does not mention it.

features/biometric_auth/biometric_auth_impl/pubspec.yaml
# …
apps/template_app/lib/di/injector.dart
// archipelago:feature_imports:start// …import 'package:autoscroll_sdk_impl/autoscroll_sdk_impl.dart';import 'package:connectivity_impl/connectivity_impl.dart';// …// archipelago:feature_imports:end@InjectableInit(  externalPackageModulesBefore: [    // archipelago:feature_modules_before:start    // …    ExternalModule(AuthImplGlobalModule),    ExternalModule(FeedbackImplPackageModule),    // …    // archipelago:feature_modules_before:end  ],

generate test-harness ​

Write the app's test/support/composition_root_harness.dart from each package's archipelago: test_prerequisites declaration. The file gives tests one call, installCompositionRootPrerequisites(), that installs every platform fake the installed packages need before initializeInjector() can run under flutter test. See Testing.

bash
dart run monorepo_toolkit generate test-harness
dart run monorepo_toolkit generate test-harness --check

If the file is missing, the command creates it. If the file exists, the command rewrites only the archipelago:test_prerequisite_imports and archipelago:test_prerequisites marker regions. The command fails when no app has a lib/di/injector.dart, or when the harness exists but has lost a marker pair.

Commit the harness. It is not build_runner output, so nothing regenerates it on a fresh checkout. Run the command again after you add or remove a package that declares a prerequisite.

Flags: --app/-a, --root, --check (report drift without writing, exit 1 if it would change).


miniapp_signer (separate executable) Pro ​

Signs mini-app manifest v2 files. The webview_sdk_advanced brick generates it at devtools/scripts/miniapp_signer/. It is not a monorepo_toolkit subcommand, so projects without that brick do not get it or its cryptography dependency.

bash
dart run miniapp_signer keygen --kid 2026-a --out ~/.miniapp-keys/2026-a.jwk.json
dart run miniapp_signer sign --key ~/.miniapp-keys/2026-a.jwk.json --kid 2026-a rewards.json -o rewards.jws
dart run miniapp_signer verify --keys apps/my_app/assets/config/mini_app_registry_keys.json rewards.jws

keygen refuses an --out path inside a git work tree unless you pass --force. Exit codes: 0 OK, 1 refused or invalid, 64 usage error. See Where to put signing keys.


Quick Reference ​

CommandDescription
affected:listList changed packages
affected:testTest changed packages only
build-prepare:productionSwap debug tooling to noop for the production flavor (build-prepare:release is an alias)
build-prepare:debugRestore debug deps
coverage:fullFull coverage pipeline
graph:cyclesDetect circular deps
graph:validateValidate architecture rules
workspace:setupMigrate workspace list to globs
env:syncSync flavors
env:switch -- <flavor>Select active env flavor before build
run:appRun an app with flavor and device selection
di:checkFail if injector.dart is out of date
size-guard:checkFlag oversized files
assets:optimizeOptimize images
asset-gen:generateGenerate asset enums
changelogWrite a CHANGELOG.md entry from commits since the last tag