flutter_distribute_manager 2.1.1 copy "flutter_distribute_manager: ^2.1.1" to clipboard
flutter_distribute_manager: ^2.1.1 copied to clipboard

Ship Flutter apps to QA in one command: build the Android APK and iOS IPA, then upload to Google Drive (via rclone) and Diawi with shareable install links.

Changelog #

2.1.1 #

Changed #

  • Documentation and license text cleanup.

2.1.0 #

Added #

  • reset command — a clean way to remove saved configuration and start over. flutter_distribute_manager reset deletes the machine config (rclone remote, Drive folder, Diawi token, notify webhook) and the current project's .flutter_distribute_manager.json, while leaving Google Drive connected (the rclone remote and its OAuth token stay in place, so no re-sign-in). reset --all additionally deletes the rclone remote to fully disconnect Google Drive. The command prints exactly what it will remove and, in an interactive terminal, asks you to type reset to confirm; pass --yes (-y) to skip the prompt in CI, and --app-dir (-d) to target a project other than the current directory.

    Previously the only reset was the "Reset all configuration" option inside config, which cleared the machine config only — it never removed the per-project file or offered to disconnect Drive. That narrower option is still available inside config.

Changed #

  • Tightened the pub.dev description for clarity and length.

2.0.0 #

A full audit of the build, distribution and runtime layers. Several defects here meant the tool shipped the wrong artifact, or hung, rather than failing — the worst kind of bug for a release tool. Test suite grew from 100 to 361.

Why this is a major release. The behavioural changes are not backwards compatible, even though almost all of them are corrections:

  • The tool now refuses to ship artifacts it previously shipped. Stale IPAs left by a failed export, and debug APKs sitting in flutter-apk/, were being uploaded to QA; both are now rejected. A pipeline that (unknowingly) depended on one of those files being produced will now fail instead.
  • --export-method release-testing was the default and was never a value Flutter accepts, so every iOS build without a Team ID failed at the usage check. The default now works, which means iOS builds that used to fail immediately will now actually run.
  • Exit codes are more specific: a missing binary or bad --flutter-path is now 78 rather than 255, "built but no artifact found" is 70 rather than 1, and --version in a non-global position no longer exits 0.
  • A filesystem error while taking the build lock no longer reports "another build is already running" (exit 69); it warns and builds unlocked.
  • rclone is now invoked with --retries 1, since its own retries were multiplying with the tool's retry policy for up to nine full uploads.
  • ProcessRunner.capture/stream and DriveUploader gained members; anything implementing those interfaces must be updated.

Shipped the wrong artifact #

  • iOS: a stale IPA could be uploaded to QA. flutter build ipa exits 0 when the archive succeeds but xcodebuild -exportArchive fails, and it never cleans build/ios/ipa/. After a signing failure the tool picked up a previous IPA, stamped it with the current timestamp and shipped it. Builds now record a start time and reject anything older; export failure is detected in Flutter's output.
  • Android: debug APKs were shipped alongside release ones. The Flutter Gradle plugin copies every variant into one flat flutter-apk/ directory and never deletes anything, so any developer who had run flutter run had an app-debug.apk sitting there. Artifact discovery is now variant- and flavor-aware, and rejects stale files.
  • --flavor was passed to the build but ignored when locating artifacts, so a multi-flavor project could ship the wrong flavor.
  • Release builds silently signed with the debug keystore — the Flutter template's default — now warned about before the build starts.

iOS builds that could never succeed #

  • --export-method release-testing is not a value Flutter accepts (app-store, ad-hoc, development, enterprise), and it was the default. Every iOS build without a Team ID failed with a usage error.
  • Hardcoded signingStyle: automatic broke every manual-signing project.
  • ExportOptions.plist: XML is now escaped, destination is set, App Store exports set uploadSymbols and manageAppVersionAndBuildNumber: false, and the obsolete compileBitcode key is gone. enterprise was missing from the export-method model entirely.
  • Xcode presence was checked with which xcodebuild, which succeeds with Command Line Tools only; the build then died minutes later.

Hangs #

  • The child process's stdin was never closed, so anything that reads it — sudo, CocoaPods, a Gradle prompt, an SDK licence question — hung forever with no timeout.
  • A daemon inheriting the pipe blocked the run after a successful build. Gradle and adb keep stdout open, so waiting for end-of-stream meant waiting for the daemon to exit. Now a bounded post-exit drain.
  • A single non-UTF-8 byte killed the CLI mid-build with an unhandled FormatException and exit 255 — a café in a path was enough. Decoding is now lenient.

Distribution #

  • The Diawi API token leaked into logs and stack traces (the poll URI carries it, and ClientException.toString() includes the URI). Redacted. The notification webhook URL leaked the same way.
  • Discord notifications always failed — the payload used Slack's text field; Discord requires content. Slack, Discord and Teams are now detected and formatted per dialect.
  • A 401 or 413 re-uploaded the whole IPA three times. 4xx is no longer retried (except 408/425/429), and Retry-After is honoured. rclone's own --retries 3 multiplied with the retry policy for up to 9 full uploads; now pinned to 1.
  • The Drive progress bar never worked and destroyed error messages. It required a Transferred: prefix that --stats-one-line does not emit, so every progress line fell into the 20-line error tail and evicted the real error within ten seconds.
  • No HTTP timeouts on Diawi — a stalled connection hung the CLI after the build. Poll bound raised from 3 minutes, and a timeout now names the job id instead of stranding an uploaded build.
  • A failed rclone link was silent; QA got a summary with no link and no reason. Added DriveUploader.preflight() to validate the remote and its token before a build is paid for.

Runtime and config #

  • A filesystem error was reported as "another build is already running", which no amount of waiting fixes. Lock paths are now canonicalized, so -d app and -d app/ no longer take separate locks on one project.
  • One unknown config field discarded the entire config, silently losing the saved Diawi token and rclone remote.
  • ProjectConfig.save() crashed a finished run on a read-only checkout, after the uploads, so the user never saw their links.
  • Trailing YAML comments were parsed into the app name and version, landing in artifact filenames and Drive paths.
  • The project is now found by walking up from a subdirectory.
  • --version anywhere in the arguments exited 0, so a CI step with a mistyped flag passed silently.
  • The saved Diawi token was echoed to the terminal by config and init; the config file is now chmod 600.
  • FVM detection understood only the .fvm/flutter_sdk layout.

1.1.1 #

Bug fixes from an adversarial review of 1.1.0. No API or flag changes.

  • Build lock moved out of build/ — it lived in build/.fdm.lock, which flutter clean deletes, allowing a second concurrent build to slip in. The lock now lives in a stable OS-temp path keyed by the project, survives a clean, and no longer unlinks on release (closing an unlink race) or NUL-pads its pid stamp.
  • flutter clean failures are now surfaced instead of being swallowed (the auto-retry path no longer silently rebuilds against a dirty cache).
  • More accurate exit codes — "no artifacts" and artifact-staging I/O failures report build (70); invalid --retry reports usage (64).
  • fvm version reporting now resolves from the project directory, and a warning is shown when an fvm-pinned project falls back to a global Flutter.
  • gradleDeclaresAbiFilters no longer false-positives on abiFilters inside block/inline comments.
  • Build-failure signing hint no longer fires on benign debug-keystore log lines.
  • Bundled global flags (e.g. -vq doctor) are now routed correctly instead of being treated as a positional to build.

1.1.0 #

Real-world hardening from testing on a large, fvm-pinned production app. All existing flags remain backward-compatible.

Added #

  • fvm support — the tool now detects fvm-pinned projects (.fvmrc, .fvm/, .fvm/fvm_config.json) and invokes the project's pinned Flutter via fvm flutter …, so version-pinned apps build correctly with no manual PATH export. New --flutter-path overrides the executable explicitly.
  • --clean — run flutter clean before building. The tool also auto-cleans and retries once when it detects a corrupted Kotlin cache.
  • Concurrent-build lock — a per-project OS lock (build/.fdm.lock) refuses a second simultaneous build, preventing Gradle/Kotlin cache corruption.
  • --flavor — passed through to flutter build.
  • --quiet / -q — silence non-essential output (build chatter, detail lines); errors, warnings and the summary still show.
  • Distinct exit codes for CI: build failure (70), upload failure (69), missing configuration (78).

Improved #

  • --split-per-abindk.abiFilters conflict — when an app declares abiFilters, the tool builds a single APK by default (with a notice) instead of failing after a multi-minute build. An explicit --split-per-abi / --no-split-per-abi still wins.
  • Actionable failure hints — known Gradle/Kotlin/signing/SDK/v1-embedding failures now print a one-line fix instead of just raw output.
  • doctor — fvm-aware Flutter check (green when resolvable via fvm), verifies the configured rclone remote exists, and reports resolved Flutter/Java versions.
  • init — empty Diawi-token / webhook answers now skip with a clear notice.
  • Pre-build summary shows the resolved Flutter toolchain (and fvm), plus a reminder that --environment only labels artifacts and sets --dart-define=ENV=….

1.0.1 #

Documentation and metadata polish (no functional changes).

  • Reworked README and example walkthrough for clarity.
  • Tightened the package description.
  • Updated LICENSE copyright holder.

1.0.0 #

Initial release.

Build & distribute #

  • build (default command): build release Android APK (--split-per-abi) and iOS IPA for one or both platforms.
  • Environment tagging (DEV / UAT / PROD) with timestamped artifact names, also passed to the build as --dart-define=ENV=….
  • Google Drive uploads via rclone with an automatic App/Year/Month/Environment folder structure and case-insensitive folder matching to avoid duplicates.
  • Diawi integration for iOS one-tap install links.

Professional touches #

  • Resilient uploads — Drive and Diawi transfers retry with exponential backoff (--retry).
  • Live progress bar — rclone transfers render as a branded [████░░░] 64% 42 MiB / 66 MiB 4.2 MiB/s ETA 6s meter.
  • Per-step timing — build/upload durations reported in the summary.
  • --json — machine-readable result on stdout for CI pipelines.
  • --dry-run — preview the full plan without building or uploading.
  • Team notifications — post a build summary to a Slack/Discord/generic webhook (--notify-url).
  • Box-drawing UI and a summary table for a polished terminal experience.

Setup & diagnostics #

  • init: one-time setup wizard (install rclone, authenticate Drive, save Diawi token and notification webhook).
  • doctor: environment & connectivity health checks, including a macOS Gatekeeper section that lists quarantined binaries and the exact xattr command to clear them.
  • config: interactive configuration editor with full reset.

Everything else #

  • Interactive prompts plus a fully non-interactive mode (--yes) for CI/CD.
  • Per-machine and per-project configuration files.
  • A reusable library API (package:flutter_distribute_manager/…).
3
likes
160
points
42
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Ship Flutter apps to QA in one command: build the Android APK and iOS IPA, then upload to Google Drive (via rclone) and Diawi with shareable install links.

Repository (GitHub)
View/report issues

Topics

#cli #flutter #release #distribution #automation

License

MIT (license)

Dependencies

args, http, path

More

Packages that depend on flutter_distribute_manager