Skip to content
Back to archipelago.banuacoder.com

DevTools

How to catch new app permissions from plugins in Flutter builds ​

Pro Reference for permission_guard: every variable, option and package it generates. Command: archipelago generate permission_guard

Generate Permission Guard, learn why scanning the built artifact finds permissions that source diffing misses, accept a baseline, and add it to CI.

What you'll learn ​

  • Generating the Permission Guard devtool
  • Seeing why a scan of the built artifact catches what source diffing misses
  • Accepting a permission baseline and running checks locally
  • Adding the check to CI

Prerequisites ​

  • An existing Archipelago monorepo (see Monorepo Scaffolding)
  • monorepo_toolkit already generated, because Permission Guard registers a command in it
  • A Pro or Enterprise subscription
  • apkanalyzer from the Android SDK command-line tools for Android checks

Step 1: Generate Permission Guard ​

New Pro projects already include this brick. Run it on an older project:

bash
archipelago generate permission_guard

The prompts are:

  • appName: your app name, for example MyApp
  • isForMonorepo: leave this as true

Or use a config file:

json
{
  "appName": "MyApp",
  "isForMonorepo": true
}
bash
archipelago generate permission_guard --config permission_guard_config.json

Step 2: Understand the Generated Structure ​

text
devtools/scripts/permission_guard/
└── lib/src/
    ├── commands/        # android, ios, update-baseline
    ├── models/
    └── services/

.permissionguard.yaml            # Config at the workspace root
permission_baseline/
├── android.txt                  # Empty on purpose
└── ios.txt                      # Empty on purpose

The hook registers the permission-guard command in the toolkit.

The baselines start empty. The first real build fails and prints the permissions it found. That forces you to review and accept the list.

Step 3: Know Why It Scans the Built Artifact ​

A pub package can add permissions without touching your AndroidManifest.xml or Info.plist. It can ship a library with its own manifest. Source diffing misses those. Permission Guard reads the permissions declared in the built APK or IPA. That is the merged list that stores and devices see.

Step 4: Configure ​

.permissionguard.yaml holds the policy:

yaml
fail_on_new: true
warn_on_removed: false
android_baseline_path: permission_baseline/android.txt
ios_baseline_path: permission_baseline/ios.txt

Step 5: Build, Then Accept the Baseline ​

Build an artifact first:

bash
flutter build apk --flavor production
flutter build ipa --flavor production   # macOS and Xcode only

Run the first check. It fails and lists the permissions. Review them, then accept:

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

dart run monorepo_toolkit permission-guard update-baseline \
  --platform ios \
  --artifact build/ios/ipa/MyApp.ipa

Commit the two baseline files.

Step 6: Run a Check Locally ​

bash
dart run monorepo_toolkit permission-guard android \
  --apk build/app/outputs/flutter-apk/app-production-release.apk

dart run monorepo_toolkit permission-guard ios \
  --ipa build/ios/ipa/MyApp.ipa

A match exits 0. A new permission prints a diff and exits with an error. Android App Bundles are not covered, because apkanalyzer reads APKs only.

Step 7: Approve New Permissions ​

When you add a dependency that needs a permission on purpose, run update-baseline again. Commit the baseline change with the dependency change. Each permission change becomes a reviewed commit.

Step 8: Add to CI ​

The hook does not edit your workflows. Add the steps yourself, after the build step:

yaml
- name: Check Android permissions
  run: |
    dart run monorepo_toolkit permission-guard android \
      --apk apps/<app>/build/app/outputs/flutter-apk/app-production-release.apk
yaml
- name: Check iOS permissions
  run: |
    dart run monorepo_toolkit permission-guard ios \
      --ipa apps/<app>/build/ios/ipa/<app>.ipa

Until you commit the baselines, the first CI run fails. That is expected.

Common Customizations ​

GoalWhat to do
Check several flavorsRun the guard once per artifact
Allow removals without a warningKeep warn_on_removed: false
Move the baselinesChange android_baseline_path and ios_baseline_path
Check iOS in CIUse a macOS job

Next Steps ​