udara_cli 1.3.1
udara_cli: ^1.3.1 copied to clipboard
A CLI tool for managing whitelabel Flutter projects with multi-client support
1.3.1 #
Everything below is opt-in or backwards compatible: existing projects keep their current behaviour until they run udara_cli migrate-config.
- NEW:
udara_cli generate-configregenerates the generated config class (lib/udara_config.g.dart) from the client.envfiles, e.g. after adding a key, without branding or cleaning anything. It reports keys thatapp_config.includeleaves out. The IDE extensions (0.3.0) add a Regenerate App Config action for it.- A
**/*.g.dartrule in.gitignore(common for build_runner output) kept the generated config out of git, so fresh clones didn't compile.generate-config,migrate-config --applyandsetupnow add!lib/udara_config.g.dartto.gitignorewhen needed, anddoctorfails when the file is ignored.
- SECURITY:
- New generated app config (
app_config: mode: generatedinudara.yaml): instead of bundling the client's.envas a Flutter asset (readable by anyone who unzips the APK/IPA, together with the default client's.env), each build generateslib/udara_config.g.dartwith the client's values as typed constants and restores it afterwards. No config file ships, build-only keys (DEVELOPMENT_TEAM,ASSETS_PATH, configurable) are left out, and no other client's values are included. The class mirrors flutter_dotenv's API (env,get,maybeGet,getInt,getDouble,getBool,isEveryDefined,isInitialized) and has the same fields for every client and environment. - Allow-list (
app_config.include): only the listed keys are compiled in.migrate-configfills it with the keys the code reads (literal reads, plus keys detected from string literals when code reads keys by computed name, opt-in with--include-detected);doctorwarns when code reads a key that isn't listed. - Native builds keep their root
.env, only when needed: in generated mode,buildwrites a root.env(the client's.envplus its.secrets, marked with a header) only when a native build file reads it (e.g. Gradle release signing), never as an app asset, and removes it after the build.whitelabelnever writes it;cleanremoves a leftover one.migrate-configreports native build files that read it. list-clients --jsonreports the project'sappConfigmode, so the IDE extensions only pass--dart-define=CLIENT_ENV=.envin dotenv mode..secretsfiles:clients/<client>/.secrets(and.secrets_test) hold build-time secrets that are never compiled in, bundled, copied into branding assets or listed; hooks get their path asUDARA_SECRETS_FILE.setupgit-ignores them.doctorwarns about secret-looking values that would ship with the app (in either mode), fails when a.secretsfile isn't git-ignored, and in generated mode checks the generated file, that no.envis still an asset and that nodotenv.load()is left.
- MIGRATION (nothing changes until you opt in):
- Projects without an
app_configsection keep the existing dotenv behaviour exactly;buildanddoctornow point out that.envis readable in the shipped app. - New
udara_cli migrate-configpreviews, then with--applymigrates a project: sets generated mode, generates the class from the default client, rewrites dotenv usages to the generated class, removesdotenv.load()calls, the unusedCLIENT_ENVconstant and.envassets, and git-ignores.secrets..envfiles are never modified. It refuses to run on a dirty git tree (sogit checkout .undoes it) and lists anything it can't rewrite safely; items that would break the app block--applyunless--force. setupon a new project (noclients/yet) starts in generated mode and doesn't install flutter_dotenv.
- OTHER:
buildnow leaves the project exactly as it found it: besides its config files it snapshots and restores everything branding and hooks change (applicationId,AndroidManifest.xml,Info.plist, the Xcode project, Androidres/,Assets.xcassets,Base.lproj, and Firebase outputs). Previously the client's bundle id, app name, icons and splash stayed in the project after a build.whitelabelstill keeps branding applied. Hooks that skip work when their output already matches (like the example Firebase hook) now run on every build.- Cleanup restores every file a run backed up, instead of a fixed list.
cleanregenerates the default client's config afterwhitelabel --keepin generated mode.
- FIXES:
whitelabel --keep(the editors' Run Client) now backs up everything it changes (pubspec, launcher icon config, fonts, generated config, native branding) and leaves those backups in.udara/instead of restoring them. The next run restores the original project before applying its client, andcleanrestores it too. Previously, running a client without fonts after one with fonts kept the previous client's fonts and pubspecfonts:entry, two font clients in a row mixed their fonts, andcleanrestored the font folder but not the pubspec entry.- Client fonts replace
assets/fontsfrom a snapshot instead of parking the originals inassets/fonts.bak, so a project that had noassets/fontsgets none back. A leftoverassets/fonts.bakfrom older versions is still restored. cleanon a project branded by an older version (no backups) repairs the sections udara_cli manages (pubspecfonts:, theassets/branding/entry, the splash image and the launcher icon path) from the last commit.assets/brandingis snapshotted and restored exactly, so a branding folder a run created (including the default client's) no longer lingers after cleanup, and switching clients leaves no other client's folder behind.buildchecks each Android artifact's signing certificate. Gradle signs a release build with the debug key when it finds no release keystore (for example a misnamedkey.propertiesentry), and the build still succeeds; Google Play then rejects the upload. A debug-signed AAB now fails the build and is kept as*.debug-signed.aabso it can't be uploaded by mistake, before anyafter_buildhook runs. A debug-signed APK only warns, since those are common for QA.doctorreports a project branded with--keepas such instead of as an interrupted run.
- BATCH & PARALLEL BUILDS:
- Batch builds:
--client,--platformand--typeaccept several values (comma-separated or repeated), and--all-clientsbuilds every client. - Parallel builds: batches are split into jobs of one client on one platform and run
--parallel Nat a time (autoby default: 1-4 based on RAM and CPU). Each parallel job runs in its own synced copy of the project under~/.udara_cli/workspaces/, so builds never touch each other's files or your working copy; the copies keep their build, Gradle and CocoaPods caches.udara_cli clean --workspacesdeletes them. - Per-client versions:
--build-version 1.4.0+12or--build-version acme=1.4.0+12,beta=2.0.0sets the version for a build without editing pubspec.yaml (passed to Flutter as--build-name/--build-number; without+nthe pubspec build number is kept). - Live progress: a redrawing terminal view with overall percent, ETA and each running job's step (plain status lines in CI and IDE consoles). ETAs are estimated from the project's build history.
--progress-file <path>writes a JSON snapshot for tools. - Per-job logs in
build/udara/logs/; the summary shows the lines around each failure. - Cancel with Ctrl+C or a termination/hang-up signal (IDE stop buttons): every worker and every Flutter/Gradle/Xcode process it started is stopped, finished artifacts are kept, and a single build restores the project immediately. Exit code 130.
--fail-faststops the whole batch at the first failure.- The IDE extensions (0.3.0) add Build Multiple Clients… with per-client versions, parallelism and progress bars on top of this.
- BUILD OUTPUT:
- Artifacts are collected in
build/udara/<client>/<client>_v<version>.<type>instead of being renamed inside Flutter's output folders, so a later build can't overwrite or clean them up. - In
after_brandinghooks of a multi-target job,UDARA_PLATFORMandUDARA_BUILD_TYPElist all targets;after_buildhooks get the single target.
1.3.0 #
- NEW:
- Hooks: declare commands in
udara.yamlthat run atafter_branding(after the client's native branding is applied, beforeflutter build; also duringwhitelabel, so IDE "Run Client" flows get it) andafter_build(after a successful build, withUDARA_ARTIFACT). Hooks receiveUDARA_CLIENT,UDARA_CLIENT_DIR,UDARA_BUNDLE_ID,UDARA_ENV,UDARA_VERSIONand more; a failing hook stops the run and the project is still restored. Unknown hook names fail fast. doctorvalidatesudara.yamland checks hook scripts exist and are executable.example/hooks/with ready-made Firebase (FCM) and OneSignal push notification hooks.
- FIXES:
buildandwhitelabelnow restore the project first when a previous run was interrupted. Previously the stale backups were restored by the new run's cleanup, silently reverting part of the new client's branding (e.g. the iOS bundle id).
1.2.0 #
- FIXES:
.envparsing now handles inline# comments, single quotes andexport KEY=VALUE. Previously a value such asDEVELOPMENT_TEAM="ABCD1234" #comment(the templatesetupgenerated) was read asABCD1234" #commentand patched into the Xcode project verbatim.- An existing root
.envis backed up before a build stages the client env there, and restored afterwards; when no root.envexisted the staged copy is removed on cleanup instead of leaving client secrets behind. - Built-in ignore patterns such as
*.jksand*.pemnow also match files in sub-folders of the client assets directory, so nested keystores and keys are no longer copied into the app bundle. clients/default/.env(the runtime fallback registered bysetup) is no longer stripped fromflutter.assetswhen a client is applied.whitelabelnow stages the env the same way asbuild(root.env, registered as an asset), honours--testandDEVELOPMENT_TEAM, and restores the staged project files afterwards (the iOS project file is left as patched). New--keepflag leaves them in place so the app can be run as the client withflutter run --dart-define=CLIENT_ENV=.env.- Unknown options (e.g.
udara_cli build --bogus) are reported as usage errors with the command's help instead of an "Unexpected Error" asking to file a bug. - iOS builds record
ipaas the artifact type (instead of the Android defaultaab) and the built.ipais renamed to<client>_v<version>.ipalike Android artifacts. - The Slack build summary is now actually posted for every build; previously only APK uploads happened and AAB/iOS/failed builds produced no summary message.
- The splash screen image now uses
APP_LOGO_PATH(falling back toAPP_ICON_PATH) as documented, instead of always using the app icon. - Client fonts are looked up in
clients/<client>/fontsfirst (whatdoctorvalidates) and then<ASSETS_PATH>/fonts. - The default client's branding folder is preserved consistently: the pre-sync cleanup no longer deletes it while the post-build cleanup kept it.
- Removed the stray
.udara_build_history.jsonfrom the repository and ignored it.
- IMPROVEMENTS:
- Global
--verboseflag: prints stack traces on failures and enables Slack debug output. doctorvalidates icon/logo paths again by resolving them to the client folder, flags images that are still the generated placeholder, checksfonts.yamlreferences against the font files present, validatesBUNDLE_IDformat, warns about leftover.udarastate from interrupted builds and about.udara_build_history.jsonmissing from.gitignore.setup --clientsgenerates valid solid-colour placeholder PNGs (so icon/splash tooling runs before real artwork exists), a per-clientREADME.md, a cleaner.envtemplate, addsflutter_dotenvwhen missing, and appends.udara/,.udara_build_history.jsonand/.envto.gitignore.buildfails fast with a pointed message when the client folder, its env file, orflutter_launcher_icons.yamlis missing (listing the available clients), and prints a build summary with duration and artifact path.list-clientsshows each client's app name, bundle id and whether a.env_testexists.historyprints the artifact path of successful builds and rejects a non-numeric--limit.cleanrestores any files left backed up by an interrupted build before runningflutter clean.- Shared step/validation/Slack helpers moved into the base command; dead code removed.
- Added a unit test suite (
dart test) covering env parsing, pubspec asset editing, backup/restore, asset sync ignore rules and artifact renaming. list-clients --jsonandhistory --jsonemit machine-readable output (human log lines move to stderr) for editor integrations and scripts.- New IDE extensions under
extensions/: a VS Code extension and an Android Studio / IntelliJ plugin that list clients and env files and trigger run, build, whitelabel, doctor, clean and diff through the CLI.
1.1.3 #
- FIXES (published to pub.flutter-io.cn on 2026-08-06; notes copied from that release):
- Improved
cleancommand logging by reporting the project cleanup phase before cleanup completes. - Added temporary
.envremoval during cleanup so stale environment files do not persist between builds. - Refined cleanup behavior to ensure project state is restored cleanly after asset refreshes.
1.1.2 #
- FIXES:
- Cleaned up the
doctorcommand diagnostics and kept the asset-path validation comments aligned with the actual client asset layout. - Ensured local project metadata is excluded from version control by ignoring
.udara/workspace artifacts. - Prepared the package for a clean publish by keeping the repo state release-ready.
1.1.1 #
- FIXES:
- Hardened backup/restore behavior by storing backups in a project-local
.udara/backupsdirectory instead of side-by-side.bakfiles, making restore operations more reliable and less likely to clobber unrelated files. - Protected managed branding folders from accidental deletion by refusing to remove non-managed asset directories and cleaning up only inactive client branding folders marked by
udara_cli. - Improved asset sync validation so builds fail early when no branding assets are copied for the selected client, with clearer remediation guidance.
- Corrected the client
.envasset path wiring during white-label setup to ensure the proper env file is tracked for each client.
1.1.0 #
- NEW:
doctorcommand: Validates project & client setup before building — checks required project files, pubspec dependencies (includingflutter_dotenv), and per-client.env/.env_testpresence, required keys, referenced asset paths, and font configuration. Run withudara_cli doctororudara_cli doctor --client <name>.historycommand: Every build (success or failure) is now recorded to a project-local.udara_build_history.json. View recent builds withudara_cli history, filter with--client/--limit, or clear with--clear.diffcommand: Compare environment configuration between two clients withudara_cli diff --client-a <NAME> --client-b <NAME>. Add--testto compare.env_testfiles, or--allto show every key instead of only the ones that differ.- Added an
example/folder demonstrating a full setup → build workflow.
- IMPROVEMENTS:
- Colored terminal output: Success, warning, and error messages are now color-coded (green/yellow/red) instead of emoji-only, with automatic fallback to plain text when the terminal doesn't support ANSI escapes (e.g. output piped to a file or CI log).
- Added
topicsand refined metadata inpubspec.yamlfor improved pub.flutter-io.cn discoverability. - Expanded dartdoc comments across the public API.
1.0.6 #
- FIXES:
- Dynamic iOS Development Team: Added support for reading client-specific DEVELOPMENT_TEAM IDs from environment variables during iOS builds.
- Centralized Structured Logging: Replaced ad-hoc print() statements with a dedicated Logger class (phase, info, success, warning, error) to produce clean, scannable terminal output.
- Enhanced Exception Tracking: Replaced generic throws with contextual BuildException instances that include actionable remediation suggestions (fix) to easily pinpoint and resolve failure root causes.
- Built-in default ignore rules to prevent accidental copying of sensitive files such as:
service_account.json*.pem,*.key,*.p12,*.jks,*.keystore
- Resilient Pipeline Execution: Improved file I/O checks, pre-flight configuration validation, and build step notifications across all commands.
- Support for user-defined ignore patterns via
.udaraignorefile.