From 6fc9be531741b69445bd905cd35aa34c0b0afb96 Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Thu, 11 Jun 2026 23:19:29 -0400 Subject: [PATCH 1/7] Stamp release version into Android code push config at release build fcp codepush release --build now writes the resolved release version into android/app/src/main/assets/codepush.yaml before flutter build and restores the file afterwards, mirroring the iOS Info.plist baseline stamp. The shipped APK's config asset therefore always records the version it was released as, which the engine compares against an installed patch's recorded version to discard stale patches after a store update. --- .github/workflows/claude-review.yml | 40 +++++++++++++++++++ .../codepush_commands/_codepush_release.dart | 29 ++++++++++++++ lib/src/shared/android_baseline_yaml.dart | 40 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 .github/workflows/claude-review.yml create mode 100644 lib/src/shared/android_baseline_yaml.dart diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml new file mode 100644 index 0000000..c07258e --- /dev/null +++ b/.github/workflows/claude-review.yml @@ -0,0 +1,40 @@ +name: Claude Code Review + +on: + pull_request: + types: [opened, synchronize, ready_for_review, reopened] + +jobs: + claude-review: + # Repo secrets are not available to PRs from forks; skip them. + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + id-token: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.pull_request.number }} + + Review this pull request as a senior Dart/Flutter engineer. Focus on: + - Correctness bugs and edge cases in the changed code + - API misuse and error-handling gaps + - Security issues (credential handling, injection, unsafe file I/O) + - Backwards compatibility for existing users of this package + + Use `gh pr comment` for overall feedback and + `mcp__github_inline_comment__create_inline_comment` (with confirmed: true) + for line-specific issues. Only post GitHub comments — do not submit + review text as plain messages. Be concise; skip pure style nits. + claude_args: | + --model claude-sonnet-4-6 + --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" diff --git a/lib/src/commands/codepush_commands/_codepush_release.dart b/lib/src/commands/codepush_commands/_codepush_release.dart index 572efc7..6c943b7 100644 --- a/lib/src/commands/codepush_commands/_codepush_release.dart +++ b/lib/src/commands/codepush_commands/_codepush_release.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:args/command_runner.dart'; +import 'package:flutter_compile/src/shared/android_baseline_yaml.dart'; import 'package:flutter_compile/src/shared/codepush_archive_service.dart'; import 'package:flutter_compile/src/shared/codepush_artifact_manager.dart'; import 'package:flutter_compile/src/shared/codepush_build_service.dart'; @@ -99,6 +100,7 @@ class CodePushReleaseSubCommand extends Command { final buildService = CodePushBuildService(logger: _logger); String? baselineId; String? originalIosInfoPlist; + String? originalAndroidYaml; String? builtPlatform; if (shouldBuild) { @@ -171,6 +173,29 @@ class CodePushReleaseSubCommand extends Command { } } + // Stamp the release version into the Android code push config + // asset BEFORE `flutter build`, so the shipped APK carries the + // version it is released as. Restored afterwards so the app repo + // stays clean (same pattern as the iOS Info.plist stamp above). + if (platform == 'apk' || platform == 'appbundle') { + final releaseVersion = version; + if (releaseVersion.isNotEmpty) { + originalAndroidYaml = + writeReleaseVersionToAndroidYaml(releaseVersion); + if (originalAndroidYaml == null) { + _logger.warn( + 'Warning: $kDefaultAndroidCodePushYamlPath not found. ' + 'This build will not embed a release version. ' + 'Run "fcp codepush init" to set up Android.', + ); + } else { + _logger.detail( + 'Stamped release_version=$releaseVersion into codepush.yaml', + ); + } + } + } + final buildProgress = _logger.progress('Building release ($platform)'); final buildOk = await buildService.buildRelease( platform: platform, @@ -205,6 +230,10 @@ class CodePushReleaseSubCommand extends Command { restoreIosInfoPlist(originalIosInfoPlist); _logger.detail('Restored ios/Runner/Info.plist'); } + if (originalAndroidYaml != null) { + restoreAndroidYaml(originalAndroidYaml); + _logger.detail('Restored android assets/codepush.yaml'); + } } } diff --git a/lib/src/shared/android_baseline_yaml.dart b/lib/src/shared/android_baseline_yaml.dart new file mode 100644 index 0000000..cbb00dc --- /dev/null +++ b/lib/src/shared/android_baseline_yaml.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +const String kDefaultAndroidCodePushYamlPath = + 'android/app/src/main/assets/codepush.yaml'; + +/// Stamps [releaseVersion] into the Android code push config asset so the +/// built APK carries the version it is released as. Returns the original +/// file content for restoring afterwards, or `null` when the file does not +/// exist (code push has not been initialized for Android). +String? writeReleaseVersionToAndroidYaml( + String releaseVersion, { + String yamlPath = kDefaultAndroidCodePushYamlPath, +}) { + final yamlFile = File(yamlPath); + if (!yamlFile.existsSync()) return null; + + final originalContent = yamlFile.readAsStringSync(); + var content = originalContent; + final versionLine = 'release_version: "$releaseVersion"'; + + if (RegExp(r'^release_version:', multiLine: true).hasMatch(content)) { + content = content.replaceFirst( + RegExp(r'^release_version:.*$', multiLine: true), + versionLine, + ); + } else { + if (content.isNotEmpty && !content.endsWith('\n')) content += '\n'; + content += '$versionLine\n'; + } + + yamlFile.writeAsStringSync(content); + return originalContent; +} + +void restoreAndroidYaml( + String originalContent, { + String yamlPath = kDefaultAndroidCodePushYamlPath, +}) { + File(yamlPath).writeAsStringSync(originalContent); +} From 982af50469395d984132251266a0a4ff79c93972 Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Thu, 11 Jun 2026 23:48:17 -0400 Subject: [PATCH 2/7] Address review: restore on write failure, surfaced restore errors, version validation - writeReleaseVersionToAndroidYaml restores the original content and rethrows if the stamped write fails, so the caller is never left without a backup of a corrupted file. - A restore failure in the finally block no longer masks an in-flight build error; it logs the dirty state and manual recovery step. - Version strings containing quotes or newlines are rejected before they can produce malformed YAML. - Dropped the redundant releaseVersion alias. --- .../codepush_commands/_codepush_release.dart | 44 +++++++++++-------- lib/src/shared/android_baseline_yaml.dart | 17 ++++++- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/lib/src/commands/codepush_commands/_codepush_release.dart b/lib/src/commands/codepush_commands/_codepush_release.dart index 6c943b7..3257c48 100644 --- a/lib/src/commands/codepush_commands/_codepush_release.dart +++ b/lib/src/commands/codepush_commands/_codepush_release.dart @@ -177,22 +177,19 @@ class CodePushReleaseSubCommand extends Command { // asset BEFORE `flutter build`, so the shipped APK carries the // version it is released as. Restored afterwards so the app repo // stays clean (same pattern as the iOS Info.plist stamp above). - if (platform == 'apk' || platform == 'appbundle') { - final releaseVersion = version; - if (releaseVersion.isNotEmpty) { - originalAndroidYaml = - writeReleaseVersionToAndroidYaml(releaseVersion); - if (originalAndroidYaml == null) { - _logger.warn( - 'Warning: $kDefaultAndroidCodePushYamlPath not found. ' - 'This build will not embed a release version. ' - 'Run "fcp codepush init" to set up Android.', - ); - } else { - _logger.detail( - 'Stamped release_version=$releaseVersion into codepush.yaml', - ); - } + if ((platform == 'apk' || platform == 'appbundle') && + version.isNotEmpty) { + originalAndroidYaml = writeReleaseVersionToAndroidYaml(version); + if (originalAndroidYaml == null) { + _logger.warn( + 'Warning: $kDefaultAndroidCodePushYamlPath not found. ' + 'This build will not embed a release version. ' + 'Run "fcp codepush init" to set up Android.', + ); + } else { + _logger.detail( + 'Stamped release_version=$version into codepush.yaml', + ); } } @@ -231,8 +228,19 @@ class CodePushReleaseSubCommand extends Command { _logger.detail('Restored ios/Runner/Info.plist'); } if (originalAndroidYaml != null) { - restoreAndroidYaml(originalAndroidYaml); - _logger.detail('Restored android assets/codepush.yaml'); + try { + restoreAndroidYaml(originalAndroidYaml); + _logger.detail('Restored android assets/codepush.yaml'); + } on FileSystemException catch (e) { + // Don't mask an in-flight build error with a restore failure; + // tell the user the repo is dirty and how to fix it. + _logger.err( + 'Failed to restore $kDefaultAndroidCodePushYamlPath after the ' + 'build: $e\nThe file still contains the stamped release ' + 'version — restore it manually (e.g. git checkout -- ' + '$kDefaultAndroidCodePushYamlPath).', + ); + } } } } diff --git a/lib/src/shared/android_baseline_yaml.dart b/lib/src/shared/android_baseline_yaml.dart index cbb00dc..845ef2c 100644 --- a/lib/src/shared/android_baseline_yaml.dart +++ b/lib/src/shared/android_baseline_yaml.dart @@ -11,6 +11,14 @@ String? writeReleaseVersionToAndroidYaml( String releaseVersion, { String yamlPath = kDefaultAndroidCodePushYamlPath, }) { + if (releaseVersion.contains('"') || releaseVersion.contains('\n')) { + throw ArgumentError.value( + releaseVersion, + 'releaseVersion', + 'contains characters that would break the YAML config', + ); + } + final yamlFile = File(yamlPath); if (!yamlFile.existsSync()) return null; @@ -28,7 +36,14 @@ String? writeReleaseVersionToAndroidYaml( content += '$versionLine\n'; } - yamlFile.writeAsStringSync(content); + try { + yamlFile.writeAsStringSync(content); + } on FileSystemException { + // Restore the original before propagating so the caller is never left + // with a stamped file it has no backup of. + yamlFile.writeAsStringSync(originalContent); + rethrow; + } return originalContent; } From 24bdd4652f04ad40b7d2afda6d509e46752d8bcd Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Fri, 12 Jun 2026 00:09:35 -0400 Subject: [PATCH 3/7] Address review round 2: atomic stamp write, allow-list version validation - The stamped write now goes to a temp file renamed over the original, so a failed write can never corrupt the config and never needs an in-place rescue that would fail for the same reason and mask the original error. - Version validation switched from a quote/newline block-list to an allow-list ([A-Za-z0-9._+-]), closing the YAML escape-sequence gap. --- lib/src/shared/android_baseline_yaml.dart | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/src/shared/android_baseline_yaml.dart b/lib/src/shared/android_baseline_yaml.dart index 845ef2c..6099cea 100644 --- a/lib/src/shared/android_baseline_yaml.dart +++ b/lib/src/shared/android_baseline_yaml.dart @@ -11,7 +11,7 @@ String? writeReleaseVersionToAndroidYaml( String releaseVersion, { String yamlPath = kDefaultAndroidCodePushYamlPath, }) { - if (releaseVersion.contains('"') || releaseVersion.contains('\n')) { + if (!RegExp(r'^[A-Za-z0-9._+\-]+$').hasMatch(releaseVersion)) { throw ArgumentError.value( releaseVersion, 'releaseVersion', @@ -36,12 +36,19 @@ String? writeReleaseVersionToAndroidYaml( content += '$versionLine\n'; } + // Write to a temp file and rename over the original: a failed write + // (disk full, permissions) can then never corrupt the config, and the + // original error propagates without a doomed in-place rescue attempt. + final tempFile = File('$yamlPath.tmp'); try { - yamlFile.writeAsStringSync(content); + tempFile.writeAsStringSync(content); + tempFile.renameSync(yamlPath); } on FileSystemException { - // Restore the original before propagating so the caller is never left - // with a stamped file it has no backup of. - yamlFile.writeAsStringSync(originalContent); + try { + tempFile.deleteSync(); + } on FileSystemException { + // Best-effort cleanup; the original config is untouched either way. + } rethrow; } return originalContent; From 0cfaab620826b304f6d1efcd43c96ed8271c163d Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Fri, 12 Jun 2026 00:23:11 -0400 Subject: [PATCH 4/7] style: format six checkpoint-lineage files to satisfy CI These files predate the formatter check running against this lineage; no functional changes. --- .../codepush_commands/_codepush_login.dart | 23 ++-- .../codepush_commands/_codepush_patch.dart | 46 ++++--- lib/src/shared/codepush_artifact_manager.dart | 102 +++++++------- lib/src/shared/codepush_build_service.dart | 50 +++---- lib/src/shared/ios_patch_entry_target.dart | 9 +- .../shared/codepush_archive_service_test.dart | 127 +++++++++--------- 6 files changed, 185 insertions(+), 172 deletions(-) diff --git a/lib/src/commands/codepush_commands/_codepush_login.dart b/lib/src/commands/codepush_commands/_codepush_login.dart index 2534ea7..cf8be60 100644 --- a/lib/src/commands/codepush_commands/_codepush_login.dart +++ b/lib/src/commands/codepush_commands/_codepush_login.dart @@ -15,7 +15,8 @@ class CodePushLoginSubCommand extends Command { ) ..addOption( 'server', - help: 'Code push server URL. Defaults to the value stored in ' + help: + 'Code push server URL. Defaults to the value stored in ' '~/.flutter_compilerc (set by previous login or ' '`fcp codepush config --server`), or ' '${Constants.codePushDefaultServer} if unset.', @@ -31,7 +32,8 @@ class CodePushLoginSubCommand extends Command { @override Future run() async { - final serverUrl = (argResults?['server'] as String?) ?? + final serverUrl = + (argResults?['server'] as String?) ?? await CodePushClient.getServerUrl(); // If --api-key provided, use the old direct login flow. @@ -69,9 +71,7 @@ class CodePushLoginSubCommand extends Command { final data = json.decode(body) as Map; if (response.statusCode != 201) { - _logger.err( - 'Failed to start login: ${data['error'] ?? 'Unknown'}', - ); + _logger.err('Failed to start login: ${data['error'] ?? 'Unknown'}'); return ExitCode.software.code; } @@ -83,7 +83,9 @@ class CodePushLoginSubCommand extends Command { // Step 2: Open browser. _logger.info('Your authorization code: $userCode'); _logger.info(''); - _logger.info('Authorization URL (click or copy to use a different browser):'); + _logger.info( + 'Authorization URL (click or copy to use a different browser):', + ); _logger.info(' $authorizeUrl'); _logger.info(''); @@ -91,7 +93,9 @@ class CodePushLoginSubCommand extends Command { await _openBrowser(authorizeUrl); _logger.info('Browser opened. Authorize the CLI there.'); } catch (_) { - _logger.info('Could not open browser automatically — use the URL above.'); + _logger.info( + 'Could not open browser automatically — use the URL above.', + ); } _logger.info(''); @@ -126,8 +130,9 @@ class CodePushLoginSubCommand extends Command { } if (status == 'expired') { - progress - .fail('Authorization expired. Run "fcp codepush login" again.'); + progress.fail( + 'Authorization expired. Run "fcp codepush login" again.', + ); return ExitCode.software.code; } diff --git a/lib/src/commands/codepush_commands/_codepush_patch.dart b/lib/src/commands/codepush_commands/_codepush_patch.dart index ad3558c..e922881 100644 --- a/lib/src/commands/codepush_commands/_codepush_patch.dart +++ b/lib/src/commands/codepush_commands/_codepush_patch.dart @@ -13,10 +13,7 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushPatchSubCommand extends Command { CodePushPatchSubCommand(this._logger) { argParser - ..addOption( - 'release-id', - help: 'The release ID to patch against.', - ) + ..addOption('release-id', help: 'The release ID to patch against.') ..addOption( 'patch-file', help: 'Path to an existing patch file to upload.', @@ -33,7 +30,8 @@ class CodePushPatchSubCommand extends Command { ) ..addMultiOption( 'dart-define', - help: 'Additional --dart-define values to forward to flutter build ' + help: + 'Additional --dart-define values to forward to flutter build ' 'when --build is used. Repeat for multiple values.', ) ..addOption( @@ -61,7 +59,8 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'flutter-version', - help: 'Flutter SDK version this patch was built with (e.g., 3.41.2). ' + help: + 'Flutter SDK version this patch was built with (e.g., 3.41.2). ' 'Auto-detected from "flutter --version" if not specified, ' 'then falls back to codepush_engine_flutter_version in ' '~/.flutter_compilerc. Required by the finalize step to locate ' @@ -69,7 +68,8 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'package-prefix', - help: 'iOS only. Package URI prefix identifying the user\'s ' + help: + 'iOS only. Package URI prefix identifying the user\'s ' 'app code (e.g. `package:fcptest/`). Only libraries ' 'whose URIs start with this prefix get compiled into the ' 'patch; everything else (Flutter framework, pub deps) ' @@ -78,19 +78,22 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'patch-entry-file', - help: 'iOS only. Path to the Dart file under `lib/` that defines ' + help: + 'iOS only. Path to the Dart file under `lib/` that defines ' '`codePushPatch()`. If omitted, flutter_compile scans `lib/` ' 'and requires exactly one matching source file.', ) ..addFlag( 'swap-mode', - help: 'iOS only. Alternative patch generation mode that ' + help: + 'iOS only. Alternative patch generation mode that ' 'enables runtime function replacement. Default: off.', negatable: false, ) ..addMultiOption( 'include-uri', - help: 'iOS only. Additional library URI to include in the ' + help: + 'iOS only. Additional library URI to include in the ' 'bytecode module (repeatable). For patch-side helper ' 'libraries not discovered automatically.', ); @@ -202,8 +205,10 @@ class CodePushPatchSubCommand extends Command { iosPatchSourceImport = importPathForPatchSource(patchSource); iosHelperImports = _discoverDirectHelpers(patchSource); if (iosHelperImports.isNotEmpty) { - _logger.detail('Discovered ${iosHelperImports.length} ' - 'helper import(s) from patch source'); + _logger.detail( + 'Discovered ${iosHelperImports.length} ' + 'helper import(s) from patch source', + ); } _logger.detail('Using iOS patch source: $patchSource'); _logger.detail('Generated iOS patch target: $generated'); @@ -311,8 +316,7 @@ class CodePushPatchSubCommand extends Command { final includeUris = { if (iosSwapMode && iosPatchSourceImport != null) '$packagePrefix$iosPatchSourceImport', - for (final helper in iosHelperImports) - '$packagePrefix$helper', + for (final helper in iosHelperImports) '$packagePrefix$helper', ...explicitIncludes.where((u) => u.isNotEmpty), }.toList(); @@ -626,8 +630,9 @@ class CodePushPatchSubCommand extends Command { if (!file.existsSync()) return null; try { for (final line in file.readAsLinesSync()) { - final match = - RegExp(r'^name:\s*([A-Za-z_][A-Za-z0-9_]*)\s*$').firstMatch(line); + final match = RegExp( + r'^name:\s*([A-Za-z_][A-Za-z0-9_]*)\s*$', + ).firstMatch(line); if (match != null) { return 'package:${match.group(1)}/'; } @@ -638,9 +643,7 @@ class CodePushPatchSubCommand extends Command { return null; } - String? _resolveIosPatchSource({ - String? explicitPath, - }) { + String? _resolveIosPatchSource({String? explicitPath}) { if (explicitPath != null && explicitPath.isNotEmpty) { if (!File(explicitPath).existsSync()) { _logger.err('Patch entry source not found: $explicitPath'); @@ -717,8 +720,9 @@ class CodePushPatchSubCommand extends Command { // Match: import 'relative/path.dart'; or import "../path.dart"; // Exclude: dart: and package: imports. - final importPattern = - RegExp(r'''import\s+['"](?!dart:|package:)([^'"]+)['"]'''); + final importPattern = RegExp( + r'''import\s+['"](?!dart:|package:)([^'"]+)['"]''', + ); for (final match in importPattern.allMatches(source)) { final relativePath = match.group(1); diff --git a/lib/src/shared/codepush_artifact_manager.dart b/lib/src/shared/codepush_artifact_manager.dart index cca91dd..8f3eb2c 100644 --- a/lib/src/shared/codepush_artifact_manager.dart +++ b/lib/src/shared/codepush_artifact_manager.dart @@ -16,11 +16,12 @@ class CodePushArtifactManager { String? baseUrl, String? cacheRoot, HttpClient Function()? httpClientFactory, - }) : _logger = logger, - _baseUrl = baseUrl ?? _defaultArtifactBucketBase, - _cacheRoot = cacheRoot ?? - '${Platform.environment['HOME'] ?? '/tmp'}/.flutter_compile/cache/${Constants.codePushCacheDir}', - _httpClientFactory = httpClientFactory ?? HttpClient.new; + }) : _logger = logger, + _baseUrl = baseUrl ?? _defaultArtifactBucketBase, + _cacheRoot = + cacheRoot ?? + '${Platform.environment['HOME'] ?? '/tmp'}/.flutter_compile/cache/${Constants.codePushCacheDir}', + _httpClientFactory = httpClientFactory ?? HttpClient.new; /// Artifact server base URL for engine binaries. static const String _defaultArtifactBucketBase = @@ -53,8 +54,9 @@ class CodePushArtifactManager { final request = await client.getUrl(Uri.parse(url)); final response = await request.close(); if (response.statusCode != 200) { - progress - .fail('Build tool download failed (HTTP ${response.statusCode})'); + progress.fail( + 'Build tool download failed (HTTP ${response.statusCode})', + ); return null; } final sink = cached.openWrite(); @@ -77,10 +79,10 @@ class CodePushArtifactManager { final os = Platform.isMacOS ? 'darwin' : Platform.isLinux - ? 'linux' - : Platform.isWindows - ? 'windows' - : 'unknown'; + ? 'linux' + : Platform.isWindows + ? 'windows' + : 'unknown'; final arch = Platform.version.contains('arm64') ? 'arm64' : 'x64'; return '$os-$arch'; } @@ -207,8 +209,8 @@ class CodePushArtifactManager { } const iosTarget = 'ios-arm64'; - final overrideDirPath = - Platform.environment['FCP_CODEPUSH_IOS_ENGINE_DIR']?.trim(); + final overrideDirPath = Platform.environment['FCP_CODEPUSH_IOS_ENGINE_DIR'] + ?.trim(); final usingLocalOverride = overrideDirPath != null && overrideDirPath.isNotEmpty; final iosDir = Directory(platformDir(flutterVersion, iosTarget)); @@ -285,9 +287,7 @@ class CodePushArtifactManager { ); return false; } - _logger.detail( - 'Using local iOS engine override from $overrideDirPath', - ); + _logger.detail('Using local iOS engine override from $overrideDirPath'); } else { // Always re-download iOS target files from GCS. A previous // `--force` run may have cached an older version and the @@ -323,8 +323,9 @@ class CodePushArtifactManager { } final engineCache = '$flutterRoot/bin/cache/artifacts/engine'; - final productSdkDir = - Directory('$engineCache/common/flutter_patched_sdk_product'); + final productSdkDir = Directory( + '$engineCache/common/flutter_patched_sdk_product', + ); final iosReleaseDir = Directory('$engineCache/ios-release'); final frameworkDir = Directory( '$engineCache/ios-release/Flutter.xcframework/ios-arm64/Flutter.framework', @@ -346,7 +347,8 @@ class CodePushArtifactManager { _logger.err('Missing one or more iOS overlay files.'); return false; } - if (!usingLocalOverride && (xcframeworkTar == null || !xcframeworkTar.existsSync())) { + if (!usingLocalOverride && + (xcframeworkTar == null || !xcframeworkTar.existsSync())) { _logger.err('Missing overlay file: ${xcframeworkTar?.path ?? 'unknown'}'); return false; } @@ -357,13 +359,14 @@ class CodePushArtifactManager { .toIso8601String() .replaceAll(RegExp('[^0-9]'), '') .substring(0, 14); - final backupDir = - Directory('$engineCache/.fcp-stock-backup-$stamp'); + final backupDir = Directory('$engineCache/.fcp-stock-backup-$stamp'); backupDir.createSync(recursive: true); - Directory('${backupDir.path}/flutter_patched_sdk_product') - .createSync(recursive: true); - Directory('${backupDir.path}/ios-release/Flutter.framework') - .createSync(recursive: true); + Directory( + '${backupDir.path}/flutter_patched_sdk_product', + ).createSync(recursive: true); + Directory( + '${backupDir.path}/ios-release/Flutter.framework', + ).createSync(recursive: true); void backup(File src, String destRel) { if (!src.existsSync()) return; @@ -372,16 +375,16 @@ class CodePushArtifactManager { dest.writeAsBytesSync(src.readAsBytesSync()); } - final stockPlatform = - File('${productSdkDir.path}/platform_strong.dill'); - final stockVmOutline = - File('${productSdkDir.path}/vm_outline_strong.dill'); + final stockPlatform = File('${productSdkDir.path}/platform_strong.dill'); + final stockVmOutline = File('${productSdkDir.path}/vm_outline_strong.dill'); final stockFlutter = File('${frameworkDir.path}/Flutter'); final stockGenSnap = File('${iosReleaseDir.path}/gen_snapshot_arm64'); backup(stockPlatform, 'flutter_patched_sdk_product/platform_strong.dill'); - backup(stockVmOutline, - 'flutter_patched_sdk_product/vm_outline_strong.dill'); + backup( + stockVmOutline, + 'flutter_patched_sdk_product/vm_outline_strong.dill', + ); backup(stockFlutter, 'ios-release/Flutter.framework/Flutter'); backup(stockGenSnap, 'ios-release/gen_snapshot_arm64'); _logger.detail('Stock artifacts backed up to ${backupDir.path}'); @@ -403,10 +406,12 @@ class CodePushArtifactManager { // Extract xcframework to a temp dir and locate the ios-arm64 binary. final tempXcf = Directory.systemTemp.createTempSync('fcp-xcf-'); try { - final tarResult = Process.runSync( - 'tar', - ['-xzf', xcframeworkTar!.path, '-C', tempXcf.path], - ); + final tarResult = Process.runSync('tar', [ + '-xzf', + xcframeworkTar!.path, + '-C', + tempXcf.path, + ]); if (tarResult.exitCode != 0) { _logger.err( 'Failed to extract Flutter.xcframework.tar.gz: ${tarResult.stderr}', @@ -491,9 +496,7 @@ class CodePushArtifactManager { _logger.detail('Detected Flutter version: $flutterVersion'); if (isVersionCached(flutterVersion)) { - _logger.info( - 'Artifacts already cached for Flutter $flutterVersion.', - ); + _logger.info('Artifacts already cached for Flutter $flutterVersion.'); return flutterVersion; } @@ -515,11 +518,12 @@ class CodePushArtifactManager { .whereType() .where((d) => File('${d.path}/.stamp').existsSync()) .map((d) { - final name = d.uri.pathSegments.where((s) => s.isNotEmpty).last; - return name.startsWith('flutter-') - ? name.substring('flutter-'.length) - : name; - }).toList() + final name = d.uri.pathSegments.where((s) => s.isNotEmpty).last; + return name.startsWith('flutter-') + ? name.substring('flutter-'.length) + : name; + }) + .toList() ..sort(); } @@ -583,8 +587,11 @@ class CodePushArtifactManager { targetSdk.deleteSync(recursive: true); } - final result = - Process.runSync('cp', ['-R', sourceSdk.path, targetSdk.path]); + final result = Process.runSync('cp', [ + '-R', + sourceSdk.path, + targetSdk.path, + ]); if (result.exitCode != 0) { _logger.err('Failed to copy Dart SDK: ${result.stderr}'); return false; @@ -593,8 +600,9 @@ class CodePushArtifactManager { final engineStamp = File('$targetFlutterRoot/bin/cache/engine.stamp'); if (engineStamp.existsSync()) { final hash = engineStamp.readAsStringSync().trim(); - File('$targetFlutterRoot/bin/cache/engine-dart-sdk.stamp') - .writeAsStringSync(hash); + File( + '$targetFlutterRoot/bin/cache/engine-dart-sdk.stamp', + ).writeAsStringSync(hash); } return true; diff --git a/lib/src/shared/codepush_build_service.dart b/lib/src/shared/codepush_build_service.dart index d308a1a..00c0638 100644 --- a/lib/src/shared/codepush_build_service.dart +++ b/lib/src/shared/codepush_build_service.dart @@ -179,9 +179,8 @@ class CodePushBuildService { return false; } - final isIos = platform == 'ios' && - artifactManager != null && - flutterVersion != null; + final isIos = + platform == 'ios' && artifactManager != null && flutterVersion != null; String? expectedGenSnapshotSha; String? expectedFrameworkSha; @@ -207,7 +206,8 @@ class CodePushBuildService { if (flutterRoot != null) { final engineCache = '$flutterRoot/bin/cache/artifacts/engine'; sdkGenSnapshotPath = '$engineCache/ios-release/gen_snapshot_arm64'; - sdkFrameworkPath = '$engineCache/ios-release/' + sdkFrameworkPath = + '$engineCache/ios-release/' 'Flutter.xcframework/ios-arm64/Flutter.framework/Flutter'; expectedGenSnapshotSha = _sha256OfFile(File(sdkGenSnapshotPath)); @@ -248,8 +248,7 @@ class CodePushBuildService { expectedFrameworkSha != null && sdkGenSnapshotPath != null && sdkFrameworkPath != null) { - final postBuildGenSnapshotSha = - _sha256OfFile(File(sdkGenSnapshotPath)); + final postBuildGenSnapshotSha = _sha256OfFile(File(sdkGenSnapshotPath)); final genSnapshotDrifted = postBuildGenSnapshotSha != expectedGenSnapshotSha; @@ -262,8 +261,7 @@ class CodePushBuildService { 'flutter build reset gen_snapshot to stock. ' 'Re-overlaying and rebuilding...', ); - final reOverlayOk = - await artifactManager.installOverlaysIntoFlutterSdk( + final reOverlayOk = await artifactManager.installOverlaysIntoFlutterSdk( flutterVersion: flutterVersion, platform: artifactManager.currentPlatform, ); @@ -401,9 +399,7 @@ class CodePushBuildService { 'build/app/intermediates/flutter/release/armeabi-v7a/app.so', 'build/app/outputs/flutter-apk/app-release.apk', ], - 'appbundle': [ - 'build/app/intermediates/flutter/release/app.so', - ], + 'appbundle': ['build/app/intermediates/flutter/release/app.so'], 'linux': [ 'build/linux/x64/release/bundle/lib/libapp.so', 'build/linux/arm64/release/bundle/lib/libapp.so', @@ -411,9 +407,7 @@ class CodePushBuildService { 'macos': [ 'build/macos/Build/Products/Release/Runner.app/Contents/Frameworks/App.framework/App', ], - 'windows': [ - 'build/windows/x64/runner/Release/app.so', - ], + 'windows': ['build/windows/x64/runner/Release/app.so'], }; final candidates = platformPaths[platform] ?? []; @@ -434,10 +428,7 @@ class CodePushBuildService { /// Verify the first bytes of a patch payload match the expected /// format for [platform]. Returns `null` on pass; a short error /// string on fail. - static String? validatePayloadMagic( - List payload, - String platform, - ) { + static String? validatePayloadMagic(List payload, String platform) { if (payload.length < 4) { return 'Patch payload is too small to be valid ' '(got ${payload.length} bytes).'; @@ -567,16 +558,14 @@ class CodePushBuildService { }) async { if (buildPlatform == 'ios') { if (flutterVersion == null || flutterVersion.isEmpty) { - _logger.err( - 'Flutter version is required to prepare an iOS fcp build.', - ); + _logger.err('Flutter version is required to prepare an iOS fcp build.'); return false; } - final overlaysInstalled = - await artifactManager.installOverlaysIntoFlutterSdk( - flutterVersion: flutterVersion, - platform: buildPlatform, - ); + final overlaysInstalled = await artifactManager + .installOverlaysIntoFlutterSdk( + flutterVersion: flutterVersion, + platform: buildPlatform, + ); if (!overlaysInstalled) { _logger.err( 'Failed to install the iOS fcp engine overlays into the active Flutter SDK.', @@ -620,7 +609,8 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: 'Build tool not available. ' + message: + 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } @@ -670,7 +660,8 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: 'Build tool not available. ' + message: + 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } @@ -725,7 +716,8 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: 'Build tool not available. ' + message: + 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } diff --git a/lib/src/shared/ios_patch_entry_target.dart b/lib/src/shared/ios_patch_entry_target.dart index d7fa101..da59555 100644 --- a/lib/src/shared/ios_patch_entry_target.dart +++ b/lib/src/shared/ios_patch_entry_target.dart @@ -7,9 +7,7 @@ final RegExp _codePushPatchPattern = RegExp( multiLine: true, ); -List findCodePushPatchSourceCandidates({ - String libDirPath = 'lib', -}) { +List findCodePushPatchSourceCandidates({String libDirPath = 'lib'}) { final libDir = Directory(libDirPath); if (!libDir.existsSync()) return const []; @@ -61,7 +59,7 @@ String buildGeneratedIosPatchEntrypoint({ // This avoids cross-library DirectCall entirely but produces a // new library (no overlap, no function swap). if (patchSourcePath != null) { - final sourceFile = File(patchSourcePath); + final sourceFile = File(patchSourcePath); if (sourceFile.existsSync()) { var source = sourceFile.readAsStringSync(); @@ -70,7 +68,8 @@ String buildGeneratedIosPatchEntrypoint({ // imports like `import 'foo.dart'` need to become // `import 'screens/foo.dart'` so they resolve from lib/. final sourceDir = _normalizePath( - sourceFile.parent.path.replaceFirst(RegExp(r'^lib/?'), '')); + sourceFile.parent.path.replaceFirst(RegExp(r'^lib/?'), ''), + ); if (sourceDir.isNotEmpty) { source = source.replaceAllMapped( RegExp(r'''(import\s+['"])(?!dart:|package:)([^'"]+['"])'''), diff --git a/test/src/shared/codepush_archive_service_test.dart b/test/src/shared/codepush_archive_service_test.dart index 9c08b35..5599261 100644 --- a/test/src/shared/codepush_archive_service_test.dart +++ b/test/src/shared/codepush_archive_service_test.dart @@ -14,10 +14,7 @@ void main() { setUp(() { projectDir = Directory.systemTemp.createTempSync('fcp-archive-test-'); logger = Logger(level: Level.quiet); - service = CodePushArchiveService( - logger: logger, - projectDir: projectDir, - ); + service = CodePushArchiveService(logger: logger, projectDir: projectDir); }); tearDown(() { @@ -34,15 +31,21 @@ void main() { final baselineApp = Directory( '${projectDir.path}/build/codepush/baseline/Runner.app', ); - Directory('${baselineApp.path}/Frameworks/Flutter.framework') - .createSync(recursive: true); - Directory('${baselineApp.path}/Frameworks/App.framework') - .createSync(recursive: true); - File('${baselineApp.path}/Frameworks/Flutter.framework/Flutter') - .writeAsStringSync(flutterFrameworkContents); - File('${baselineApp.path}/Frameworks/App.framework/App') - .writeAsStringSync(appFrameworkContents); - File('${baselineApp.path}/Runner').writeAsStringSync(runnerBinaryContents); + Directory( + '${baselineApp.path}/Frameworks/Flutter.framework', + ).createSync(recursive: true); + Directory( + '${baselineApp.path}/Frameworks/App.framework', + ).createSync(recursive: true); + File( + '${baselineApp.path}/Frameworks/Flutter.framework/Flutter', + ).writeAsStringSync(flutterFrameworkContents); + File( + '${baselineApp.path}/Frameworks/App.framework/App', + ).writeAsStringSync(appFrameworkContents); + File( + '${baselineApp.path}/Runner', + ).writeAsStringSync(runnerBinaryContents); File('${baselineApp.path}/Info.plist').writeAsStringSync('plist'); } @@ -55,10 +58,12 @@ void main() { } void initGitRepo() { - final result = Process.runSync( - 'git', - ['-C', projectDir.path, 'init', '-q'], - ); + final result = Process.runSync('git', [ + '-C', + projectDir.path, + 'init', + '-q', + ]); if (result.exitCode != 0) { fail('git init failed: ${result.stderr}'); } @@ -87,8 +92,7 @@ void main() { ); expect(ok, isTrue); - final releaseDir = - Directory('${projectDir.path}/.fcp-archive/rel-2'); + final releaseDir = Directory('${projectDir.path}/.fcp-archive/rel-2'); expect(releaseDir.existsSync(), isTrue); expect( File('${releaseDir.path}/Runner.app/Info.plist').existsSync(), @@ -101,9 +105,11 @@ void main() { isTrue, ); - final manifest = jsonDecode( - File('${releaseDir.path}/manifest.json').readAsStringSync(), - ) as Map; + final manifest = + jsonDecode( + File('${releaseDir.path}/manifest.json').readAsStringSync(), + ) + as Map; expect(manifest['archive_format_version'], 1); expect(manifest['release_id'], 'rel-2'); expect(manifest['baseline_id'], 'base-2'); @@ -127,15 +133,16 @@ void main() { ); expect(ok, isTrue); - final releaseDir = - Directory('${projectDir.path}/.fcp-archive/rel-3'); + final releaseDir = Directory('${projectDir.path}/.fcp-archive/rel-3'); expect( Directory('${releaseDir.path}/Runner.app.dSYM').existsSync(), isTrue, ); - final manifest = jsonDecode( - File('${releaseDir.path}/manifest.json').readAsStringSync(), - ) as Map; + final manifest = + jsonDecode( + File('${releaseDir.path}/manifest.json').readAsStringSync(), + ) + as Map; expect(manifest['has_dsym'], isTrue); }); @@ -154,28 +161,25 @@ void main() { ); }); - test( - 'appends .fcp-archive/ to .git/info/exclude in a git repo', - () { - initGitRepo(); - writeBaselineApp(); - - service.archiveIosRelease( - releaseId: 'rel-5', - baselineId: 'base-5', - fcpVersion: '0.0.0', - ); - - final excludeFile = File('${projectDir.path}/.git/info/exclude'); - expect(excludeFile.existsSync(), isTrue); - final lines = excludeFile - .readAsStringSync() - .split('\n') - .map((l) => l.trim()) - .toList(); - expect(lines.contains('.fcp-archive/'), isTrue); - }, - ); + test('appends .fcp-archive/ to .git/info/exclude in a git repo', () { + initGitRepo(); + writeBaselineApp(); + + service.archiveIosRelease( + releaseId: 'rel-5', + baselineId: 'base-5', + fcpVersion: '0.0.0', + ); + + final excludeFile = File('${projectDir.path}/.git/info/exclude'); + expect(excludeFile.existsSync(), isTrue); + final lines = excludeFile + .readAsStringSync() + .split('\n') + .map((l) => l.trim()) + .toList(); + expect(lines.contains('.fcp-archive/'), isTrue); + }); test('does not duplicate the exclude entry on re-runs', () { initGitRepo(); @@ -192,9 +196,11 @@ void main() { fcpVersion: '0.0.0', ); - final excludeContents = - File('${projectDir.path}/.git/info/exclude').readAsStringSync(); - final occurrences = '\n$excludeContents\n'.split('\n.fcp-archive/').length - 1; + final excludeContents = File( + '${projectDir.path}/.git/info/exclude', + ).readAsStringSync(); + final occurrences = + '\n$excludeContents\n'.split('\n.fcp-archive/').length - 1; expect(occurrences, 1); }); @@ -209,10 +215,7 @@ void main() { expect(ok, isTrue); // No .git directory was created. - expect( - Directory('${projectDir.path}/.git').existsSync(), - isFalse, - ); + expect(Directory('${projectDir.path}/.git').existsSync(), isFalse); }); test('overwrites an existing release archive on re-run', () { @@ -250,11 +253,13 @@ void main() { fcpVersion: '0.0.0', ); - final manifest = jsonDecode( - File( - '${projectDir.path}/.fcp-archive/rel-9/manifest.json', - ).readAsStringSync(), - ) as Map; + final manifest = + jsonDecode( + File( + '${projectDir.path}/.fcp-archive/rel-9/manifest.json', + ).readAsStringSync(), + ) + as Map; // Pre-computed sha256 of the literal bytes. expect( manifest['framework_sha256'], From 6a486d181f4dfd74725a5bfeee1577ef9af05812 Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Fri, 12 Jun 2026 00:38:57 -0400 Subject: [PATCH 5/7] style: format with Dart 3.12.2 to match CI --- bin/flutter_compile.dart | 6 +- example/lib/main.dart | 5 +- lib/src/command_runner.dart | 24 +-- .../build_commands/_engine_build.dart | 11 +- lib/src/commands/clean_command.dart | 15 +- .../codepush_commands/_codepush_account.dart | 35 ++-- .../codepush_commands/_codepush_apps.dart | 15 +- .../codepush_commands/_codepush_init.dart | 71 +++++--- .../codepush_commands/_codepush_keys.dart | 18 +- .../codepush_commands/_codepush_release.dart | 50 ++--- .../codepush_commands/_codepush_rollback.dart | 5 +- .../codepush_commands/_codepush_setup.dart | 24 +-- .../codepush_commands/_codepush_status.dart | 97 +++++----- .../codepush_commands/_codepush_versions.dart | 34 ++-- lib/src/commands/config_command.dart | 12 +- lib/src/commands/doctor_command.dart | 36 ++-- lib/src/commands/flutter_switch_command.dart | 5 +- .../commands/install_commands/_devtools.dart | 68 ++++--- .../commands/install_commands/_engine.dart | 29 +-- .../commands/install_commands/_flutter.dart | 49 ++--- lib/src/commands/run_command.dart | 18 +- lib/src/commands/sdk_commands/_sdk_exec.dart | 3 +- .../commands/sdk_commands/_sdk_global.dart | 4 +- .../commands/sdk_commands/_sdk_install.dart | 18 +- lib/src/commands/sdk_commands/_sdk_list.dart | 32 ++-- .../commands/sdk_commands/_sdk_remove.dart | 3 +- lib/src/commands/sdk_commands/_sdk_use.dart | 9 +- lib/src/commands/status_command.dart | 10 +- .../sync_commands/_sync_devtools.dart | 18 +- .../commands/sync_commands/_sync_engine.dart | 18 +- .../commands/sync_commands/_sync_flutter.dart | 26 ++- .../commands/sync_commands/sync_command.dart | 12 +- lib/src/commands/test_command.dart | 18 +- lib/src/commands/uninstall_command.dart | 39 ++-- lib/src/commands/update_command.dart | 8 +- lib/src/daemon/daemon_peer.dart | 2 +- lib/src/shared/codepush_archive_service.dart | 35 ++-- lib/src/shared/codepush_client.dart | 46 ++--- lib/src/shared/constants.dart | 12 +- lib/src/shared/functions.dart | 60 +++--- lib/src/tui/tui_app.dart | 6 +- lib/src/tui/tui_renderer.dart | 18 +- test/ensure_build_test.dart | 9 +- test/helpers/test_helpers.dart | 14 +- test/src/command_runner_test.dart | 40 ++-- test/src/commands/config_command_test.dart | 13 +- test/src/commands/sdk_command_test.dart | 4 +- test/src/commands/sdk_exec_command_test.dart | 4 +- .../src/commands/sdk_global_command_test.dart | 7 +- test/src/commands/sdk_install_test.dart | 78 ++++---- test/src/commands/sdk_use_command_test.dart | 7 +- test/src/commands/status_command_test.dart | 4 +- test/src/commands/update_command_test.dart | 172 ++++++++---------- test/src/daemon/daemon_peer_test.dart | 107 ++--------- test/src/daemon/file_watcher_test.dart | 5 +- .../shared/codepush_build_service_test.dart | 72 ++++---- test/src/shared/constants_test.dart | 11 +- test/src/shared/functions_test.dart | 5 +- test/src/shared/ios_baseline_plist_test.dart | 46 ++--- .../shared/ios_patch_entry_target_test.dart | 20 +- 60 files changed, 796 insertions(+), 846 deletions(-) diff --git a/bin/flutter_compile.dart b/bin/flutter_compile.dart index d87733a..c91be7f 100644 --- a/bin/flutter_compile.dart +++ b/bin/flutter_compile.dart @@ -13,6 +13,8 @@ Future main(List args) async { /// exited already. This is useful to prevent Future chains from proceeding /// after you've decided to exit. Future _flushThenExit(int status) { - return Future.wait([stdout.close(), stderr.close()]) - .then((_) => exit(status)); + return Future.wait([ + stdout.close(), + stderr.close(), + ]).then((_) => exit(status)); } diff --git a/example/lib/main.dart b/example/lib/main.dart index fb6d46b..5623453 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -5,9 +5,6 @@ void main() async { final result = await Process.run('flutter_compile', ['--help']); print(result.stdout); - await Future.wait([ - stdout.close(), - stderr.close(), - ]); + await Future.wait([stdout.close(), stderr.close()]); exit(0); } diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart index 4cdb75a..a5c085a 100644 --- a/lib/src/command_runner.dart +++ b/lib/src/command_runner.dart @@ -22,16 +22,14 @@ const description = /// {@endtemplate} class FlutterCompileCommandRunner extends CompletionCommandRunner { /// {@macro flutter_compile_command_runner} - FlutterCompileCommandRunner({ - Logger? logger, - PubUpdater? pubUpdater, - }) : _logger = logger ?? Logger(), - // Wrap PubUpdater's HTTP client with a cache-busting shim so that - // back-to-back version checks (e.g. `update` then `--version`) - // can't disagree because pub.dev's CDN served one a stale edge - // node. See pub_cache_busting_client.dart and issue #17. - _pubUpdater = pubUpdater ?? PubUpdater(PubCacheBustingClient()), - super(executableName, description) { + FlutterCompileCommandRunner({Logger? logger, PubUpdater? pubUpdater}) + : _logger = logger ?? Logger(), + // Wrap PubUpdater's HTTP client with a cache-busting shim so that + // back-to-back version checks (e.g. `update` then `--version`) + // can't disagree because pub.dev's CDN served one a stale edge + // node. See pub_cache_busting_client.dart and issue #17. + _pubUpdater = pubUpdater ?? PubUpdater(PubCacheBustingClient()), + super(executableName, description) { // Add root options and flags argParser ..addFlag( @@ -167,11 +165,9 @@ class FlutterCompileCommandRunner extends CompletionCommandRunner { } _logger ..info('') - ..info( - ''' + ..info(''' ${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} -Run ${lightCyan.wrap('$executableName update')} to update''', - ); +Run ${lightCyan.wrap('$executableName update')} to update'''); } catch (_) {} } diff --git a/lib/src/commands/build_commands/_engine_build.dart b/lib/src/commands/build_commands/_engine_build.dart index 0a085bb..997d808 100644 --- a/lib/src/commands/build_commands/_engine_build.dart +++ b/lib/src/commands/build_commands/_engine_build.dart @@ -35,11 +35,7 @@ class EngineBuildSubCommand extends Command { help: 'Build with --unoptimized (faster dev builds)', defaultsTo: true, ) - ..addFlag( - 'simulator', - help: 'Build for iOS simulator', - defaultsTo: false, - ) + ..addFlag('simulator', help: 'Build for iOS simulator', defaultsTo: false) ..addFlag( 'clean', help: 'Clean output directory before building', @@ -178,8 +174,9 @@ Future buildEngine( } // Decide whether to run GN - final buildNinjaExists = - await File('$srcDir/out/$outputDir/build.ninja').exists(); + final buildNinjaExists = await File( + '$srcDir/out/$outputDir/build.ninja', + ).exists(); final runGn = shouldRunGn( forceGn: forceGn, skipGn: skipGn, diff --git a/lib/src/commands/clean_command.dart b/lib/src/commands/clean_command.dart index 9c495d2..852f961 100644 --- a/lib/src/commands/clean_command.dart +++ b/lib/src/commands/clean_command.dart @@ -106,15 +106,12 @@ class CleanCommand extends Command { Future _dirSize(String path) async { if (Platform.isWindows) { - final result = await Process.run( - 'powershell', - [ - '-Command', - '(Get-ChildItem -Recurse -File "$path" ' - '| Measure-Object -Property Length -Sum).Sum / 1MB ' - '| ForEach-Object { "{0:N1}M" -f \$_ }', - ], - ); + final result = await Process.run('powershell', [ + '-Command', + '(Get-ChildItem -Recurse -File "$path" ' + '| Measure-Object -Property Length -Sum).Sum / 1MB ' + '| ForEach-Object { "{0:N1}M" -f \$_ }', + ]); return (result.stdout as String).trim(); } final result = await Process.run('du', ['-sh', path]); diff --git a/lib/src/commands/codepush_commands/_codepush_account.dart b/lib/src/commands/codepush_commands/_codepush_account.dart index 739c313..b61fcc0 100644 --- a/lib/src/commands/codepush_commands/_codepush_account.dart +++ b/lib/src/commands/codepush_commands/_codepush_account.dart @@ -6,11 +6,7 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushAccountSubCommand extends Command { CodePushAccountSubCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -55,10 +51,12 @@ class CodePushAccountSubCommand extends Command { if (asJson) { // --json contract: structured output exits 0, callers inspect the // `error` field instead of the process status. - _logger.info(json.encode({ - 'logged_in': false, - 'error': result['error'] ?? 'Unknown', - })); + _logger.info( + json.encode({ + 'logged_in': false, + 'error': result['error'] ?? 'Unknown', + }), + ); return ExitCode.success.code; } progress?.fail('Error: ${result['error'] ?? 'Unknown'}'); @@ -69,14 +67,17 @@ class CodePushAccountSubCommand extends Command { final apps = result['apps'] as List?; if (asJson) { - _logger.info(json.encode({ - 'logged_in': true, - 'email': user?['email'], - 'name': user?['name'], - 'tier': user?['tier'], - 'has_active_subscription': user?['has_active_subscription'] ?? false, - 'apps': apps ?? [], - })); + _logger.info( + json.encode({ + 'logged_in': true, + 'email': user?['email'], + 'name': user?['name'], + 'tier': user?['tier'], + 'has_active_subscription': + user?['has_active_subscription'] ?? false, + 'apps': apps ?? [], + }), + ); return ExitCode.success.code; } diff --git a/lib/src/commands/codepush_commands/_codepush_apps.dart b/lib/src/commands/codepush_commands/_codepush_apps.dart index 8603ce4..8dc2c01 100644 --- a/lib/src/commands/codepush_commands/_codepush_apps.dart +++ b/lib/src/commands/codepush_commands/_codepush_apps.dart @@ -135,18 +135,21 @@ class _AppsCreateCommand extends Command { final request = await httpClient.postUrl(uri); request.headers.set('Authorization', 'Bearer $token'); request.headers.set('Content-Type', 'application/json'); - request.write(json.encode({ - 'name': appName, - if (platform != null) 'platform': platform, - })); + request.write( + json.encode({ + 'name': appName, + if (platform != null) 'platform': platform, + }), + ); final response = await request.close(); final body = await response.transform(utf8.decoder).join(); final data = json.decode(body) as Map; if (response.statusCode == 403) { - progress - .fail(data['message'] ?? 'App limit reached. Upgrade your plan.'); + progress.fail( + data['message'] ?? 'App limit reached. Upgrade your plan.', + ); return ExitCode.software.code; } diff --git a/lib/src/commands/codepush_commands/_codepush_init.dart b/lib/src/commands/codepush_commands/_codepush_init.dart index 0f2a027..99ffcde 100644 --- a/lib/src/commands/codepush_commands/_codepush_init.dart +++ b/lib/src/commands/codepush_commands/_codepush_init.dart @@ -13,10 +13,7 @@ class CodePushInitSubCommand extends Command { 'name', help: 'App name (defaults to pubspec name or directory name).', ) - ..addOption( - 'platform', - help: 'Target platform (android, ios).', - ); + ..addOption('platform', help: 'Target platform (android, ios).'); } final Logger _logger; @@ -41,8 +38,10 @@ class CodePushInitSubCommand extends Command { final pubspec = File('pubspec.yaml'); if (pubspec.existsSync()) { final content = pubspec.readAsStringSync(); - final match = - RegExp(r'^name:\s*(.+)$', multiLine: true).firstMatch(content); + final match = RegExp( + r'^name:\s*(.+)$', + multiLine: true, + ).firstMatch(content); if (match != null) appName = match.group(1)?.trim(); } appName ??= Directory.current.path.split('/').last; @@ -68,16 +67,20 @@ class CodePushInitSubCommand extends Command { final httpClient = HttpClient(); try { - final request = - await httpClient.postUrl(Uri.parse('$serverUrl/api/v1/apps')); + final request = await httpClient.postUrl( + Uri.parse('$serverUrl/api/v1/apps'), + ); request.headers.set('Authorization', 'Bearer $token'); request.headers.set('Content-Type', 'application/json'); request.headers.set('Accept', 'application/json'); - request.write(json.encode({ - 'name': appName, - if (platform != null) 'platform': platform, - if (publicKeyPemForCreate != null) 'public_key': publicKeyPemForCreate, - })); + request.write( + json.encode({ + 'name': appName, + if (platform != null) 'platform': platform, + if (publicKeyPemForCreate != null) + 'public_key': publicKeyPemForCreate, + }), + ); final response = await request.close(); final body = await response.transform(utf8.decoder).join(); @@ -120,8 +123,9 @@ class CodePushInitSubCommand extends Command { 'verification.', ); } else { - keyProgress - .fail('Could not generate signing keys (openssl missing?)'); + keyProgress.fail( + 'Could not generate signing keys (openssl missing?)', + ); _logger.warn( 'Patches will not be signed. Install openssl and re-run ' '`fcp codepush keys generate`.', @@ -150,7 +154,8 @@ class CodePushInitSubCommand extends Command { _logger.info(' 1. Wrap your app with CodePushOverlay in main.dart:'); _logger.info(''); _logger.info( - ' import \'package:flutterplaza_code_push/flutterplaza_code_push.dart\';'); + ' import \'package:flutterplaza_code_push/flutterplaza_code_push.dart\';', + ); _logger.info(''); _logger.info(' runApp('); _logger.info(' CodePushOverlay('); @@ -223,8 +228,10 @@ class CodePushInitSubCommand extends Command { String? _readPubspecVersion() { final pubspec = File('pubspec.yaml'); if (!pubspec.existsSync()) return null; - final match = RegExp(r'^version:\s*(.+)$', multiLine: true) - .firstMatch(pubspec.readAsStringSync()); + final match = RegExp( + r'^version:\s*(.+)$', + multiLine: true, + ).firstMatch(pubspec.readAsStringSync()); return match?.group(1)?.trim(); } @@ -256,8 +263,9 @@ class CodePushInitSubCommand extends Command { final manifestContent = manifest.readAsStringSync(); // Find package name from manifest, build.gradle.kts, or build.gradle - var packageName = - RegExp(r'package="([^"]+)"').firstMatch(manifestContent)?.group(1); + var packageName = RegExp( + r'package="([^"]+)"', + ).firstMatch(manifestContent)?.group(1); if (packageName == null) { // Try build.gradle.kts for (final gradleFile in [ @@ -266,16 +274,16 @@ class CodePushInitSubCommand extends Command { ]) { if (gradleFile.existsSync()) { final gradleContent = gradleFile.readAsStringSync(); - final nsMatch = - RegExp(r'namespace\s*[=:]\s*["\x27]([^"\x27]+)["\x27]') - .firstMatch(gradleContent); + final nsMatch = RegExp( + r'namespace\s*[=:]\s*["\x27]([^"\x27]+)["\x27]', + ).firstMatch(gradleContent); if (nsMatch != null) { packageName = nsMatch.group(1); break; } - final appIdMatch = - RegExp(r'applicationId\s*[=:]\s*["\x27]([^"\x27]+)["\x27]') - .firstMatch(gradleContent); + final appIdMatch = RegExp( + r'applicationId\s*[=:]\s*["\x27]([^"\x27]+)["\x27]', + ).firstMatch(gradleContent); if (appIdMatch != null) { packageName = appIdMatch.group(1); break; @@ -331,8 +339,9 @@ class CodePushApp : FlutterApplication() { ); } else if (!manifestContent.contains('CodePushApp')) { // App has a custom Application class (possibly per-flavor). - final existingMatch = - RegExp(r'android:name="([^"]+)"').firstMatch(manifestContent); + final existingMatch = RegExp( + r'android:name="([^"]+)"', + ).firstMatch(manifestContent); final existingClass = existingMatch?.group(1); _logger.warn( 'AndroidManifest.xml already has a custom Application class: ' @@ -375,7 +384,8 @@ class CodePushApp : FlutterApplication() { final publicKeyFile = File('$home/.flutter_codepush/codepush_public.pem'); if (publicKeyFile.existsSync()) { final pem = publicKeyFile.readAsStringSync().trim(); - publicKeyBlock = '\tFLTCodePushPublicKey\n' + publicKeyBlock = + '\tFLTCodePushPublicKey\n' '\t$pem\n'; } @@ -403,7 +413,8 @@ class CodePushApp : FlutterApplication() { progress.complete('iOS configured'); _logger.info( - ' Updated: Info.plist (FLTCodePushEnabled, release version, public key)'); + ' Updated: Info.plist (FLTCodePushEnabled, release version, public key)', + ); } // ── pubspec.yaml setup ──────────────────────────────────────── diff --git a/lib/src/commands/codepush_commands/_codepush_keys.dart b/lib/src/commands/codepush_commands/_codepush_keys.dart index 738ccef..7a57ee7 100644 --- a/lib/src/commands/codepush_commands/_codepush_keys.dart +++ b/lib/src/commands/codepush_commands/_codepush_keys.dart @@ -49,9 +49,7 @@ class CodePushKeysSubCommand extends Command { ' register Upload the public key to the server for the current app', ); _logger.info(''); - _logger.info( - 'Typical migration flow for pre-0.15.0 users:', - ); + _logger.info('Typical migration flow for pre-0.15.0 users:'); _logger.info(' 1. fcp codepush keys generate'); _logger.info(' 2. fcp codepush keys register'); _logger.info( @@ -109,9 +107,7 @@ class _KeysGenerateCommand extends Command { final progress = _logger.progress('Generating RSA-2048 keypair'); final result = await buildService.generateSigningKey(outputDir); if (result == null) { - progress.fail( - 'Key generation failed. Is openssl installed and on PATH?', - ); + progress.fail('Key generation failed. Is openssl installed and on PATH?'); return ExitCode.software.code; } progress.complete('Keypair generated'); @@ -137,12 +133,14 @@ class _KeysRegisterCommand extends Command { argParser ..addOption( 'app-id', - help: 'App ID to register the public key against. Defaults to the ' + help: + 'App ID to register the public key against. Defaults to the ' 'stored codepush_app_id from ~/.flutter_compilerc.', ) ..addOption( 'public-key', - help: 'Path to the PEM-encoded public key file. Defaults to ' + help: + 'Path to the PEM-encoded public key file. Defaults to ' '~/.flutter_codepush/codepush_public.pem, which is written ' 'by `fcp codepush keys generate`.', ); @@ -202,9 +200,7 @@ class _KeysRegisterCommand extends Command { final serverUrl = await CodePushClient.getServerUrl(); final client = CodePushClient(serverUrl: serverUrl); - final progress = _logger.progress( - 'Registering public key with $serverUrl', - ); + final progress = _logger.progress('Registering public key with $serverUrl'); try { final result = await client.registerAppPublicKey( diff --git a/lib/src/commands/codepush_commands/_codepush_release.dart b/lib/src/commands/codepush_commands/_codepush_release.dart index 3257c48..2863e08 100644 --- a/lib/src/commands/codepush_commands/_codepush_release.dart +++ b/lib/src/commands/codepush_commands/_codepush_release.dart @@ -13,19 +13,13 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushReleaseSubCommand extends Command { CodePushReleaseSubCommand(this._logger) { argParser - ..addOption( - 'app-id', - help: 'The app ID to create a release for.', - ) + ..addOption('app-id', help: 'The app ID to create a release for.') ..addOption( 'version', abbr: 'v', help: 'The version string for this release (e.g., 1.0.0+1).', ) - ..addOption( - 'snapshot', - help: 'Path to a pre-built release artifact.', - ) + ..addOption('snapshot', help: 'Path to a pre-built release artifact.') ..addOption( 'platform', abbr: 'p', @@ -38,12 +32,14 @@ class CodePushReleaseSubCommand extends Command { ) ..addMultiOption( 'dart-define', - help: 'Additional --dart-define values to forward to flutter build ' + help: + 'Additional --dart-define values to forward to flutter build ' 'when --build is used. Repeat for multiple values.', ) ..addOption( 'flutter-version', - help: 'Flutter SDK version this release was built with (e.g., 3.41.2). ' + help: + 'Flutter SDK version this release was built with (e.g., 3.41.2). ' 'Auto-detected from "flutter --version" if not specified. ' 'Required for server-side patch compilation.', ); @@ -81,15 +77,18 @@ class CodePushReleaseSubCommand extends Command { final pubspec = File('pubspec.yaml'); if (pubspec.existsSync()) { final content = pubspec.readAsStringSync(); - final match = - RegExp(r'^version:\s*(.+)$', multiLine: true).firstMatch(content); + final match = RegExp( + r'^version:\s*(.+)$', + multiLine: true, + ).firstMatch(content); if (match != null) { version = match.group(1)?.trim(); } } if (version == null || version.isEmpty) { _logger.err( - 'No version specified. Use --version or add one to pubspec.yaml.'); + 'No version specified. Use --version or add one to pubspec.yaml.', + ); return ExitCode.usage.code; } _logger.detail('Using version from pubspec.yaml: $version'); @@ -160,8 +159,9 @@ class CodePushReleaseSubCommand extends Command { // not leave the app repo dirty. if (platform == 'ios') { final generatedBaselineId = generateBaselineId(); - originalIosInfoPlist = - writeBaselineIdToIosInfoPlist(generatedBaselineId); + originalIosInfoPlist = writeBaselineIdToIosInfoPlist( + generatedBaselineId, + ); if (originalIosInfoPlist == null) { _logger.warn( 'Warning: ios/Runner/Info.plist not found. ' @@ -279,8 +279,9 @@ class CodePushReleaseSubCommand extends Command { if (vResult.exitCode == 0) { try { final vJson = (vResult.stdout as String).trim(); - final match = - RegExp(r'"frameworkVersion"\s*:\s*"([^"]+)"').firstMatch(vJson); + final match = RegExp( + r'"frameworkVersion"\s*:\s*"([^"]+)"', + ).firstMatch(vJson); flutterVersion = match?.group(1); } catch (_) {} } @@ -288,8 +289,9 @@ class CodePushReleaseSubCommand extends Command { if (flutterVersion == null || flutterVersion.isEmpty) { final plainResult = Process.runSync(flutter, ['--version']); if (plainResult.exitCode == 0) { - final match = RegExp(r'Flutter (\d+\.\d+\.\d+)') - .firstMatch(plainResult.stdout as String); + final match = RegExp( + r'Flutter (\d+\.\d+\.\d+)', + ).firstMatch(plainResult.stdout as String); flutterVersion = match?.group(1); } } @@ -306,10 +308,12 @@ class CodePushReleaseSubCommand extends Command { final serverUrl = await CodePushClient.getServerUrl(); final client = CodePushClient(serverUrl: serverUrl); - final versionSuffix = - flutterVersion != null ? ' (Flutter $flutterVersion)' : ''; - final progress = - _logger.progress('Creating release v$version for $appId$versionSuffix'); + final versionSuffix = flutterVersion != null + ? ' (Flutter $flutterVersion)' + : ''; + final progress = _logger.progress( + 'Creating release v$version for $appId$versionSuffix', + ); try { final result = await client.createRelease( diff --git a/lib/src/commands/codepush_commands/_codepush_rollback.dart b/lib/src/commands/codepush_commands/_codepush_rollback.dart index 75bf17d..113dfe9 100644 --- a/lib/src/commands/codepush_commands/_codepush_rollback.dart +++ b/lib/src/commands/codepush_commands/_codepush_rollback.dart @@ -37,10 +37,7 @@ class CodePushRollbackSubCommand extends Command { final progress = _logger.progress('Rolling back patch $patchId'); try { - final result = await client.rollbackPatch( - token: token, - patchId: patchId, - ); + final result = await client.rollbackPatch(token: token, patchId: patchId); final statusCode = result['status_code'] as int; diff --git a/lib/src/commands/codepush_commands/_codepush_setup.dart b/lib/src/commands/codepush_commands/_codepush_setup.dart index bfde82c..8ec94a8 100644 --- a/lib/src/commands/codepush_commands/_codepush_setup.dart +++ b/lib/src/commands/codepush_commands/_codepush_setup.dart @@ -9,12 +9,14 @@ class CodePushSetupSubCommand extends Command { argParser ..addOption( 'flutter-version', - help: 'Flutter SDK version to download artifacts for ' + help: + 'Flutter SDK version to download artifacts for ' '(e.g., 3.24.0). Auto-detected if omitted.', ) ..addOption( 'platform', - help: 'Target platform (e.g., darwin-arm64, linux-x64). ' + help: + 'Target platform (e.g., darwin-arm64, linux-x64). ' 'Defaults to current platform.', ) ..addFlag( @@ -119,8 +121,10 @@ class CodePushSetupSubCommand extends Command { 'Flutter $flutterVersion is not yet supported for code push.', ); _logger.info(''); - _logger.info('Run `fcp codepush setup --list-versions` to see ' - 'available Flutter versions.'); + _logger.info( + 'Run `fcp codepush setup --list-versions` to see ' + 'available Flutter versions.', + ); return ExitCode.software.code; } checkProgress.complete('Flutter $flutterVersion is supported'); @@ -157,9 +161,7 @@ class CodePushSetupSubCommand extends Command { platform: resolvedPlatform, ); if (!installed) { - installProgress.fail( - 'Failed to install overlays into Flutter SDK cache', - ); + installProgress.fail('Failed to install overlays into Flutter SDK cache'); return ExitCode.software.code; } installProgress.complete('Overlays installed into Flutter SDK cache'); @@ -194,9 +196,7 @@ class CodePushSetupSubCommand extends Command { manager.cleanupOldVersions(keepVersion: flutterVersion); } - _logger.success( - 'Code push is ready for Flutter $flutterVersion.', - ); + _logger.success('Code push is ready for Flutter $flutterVersion.'); return ExitCode.success.code; } @@ -218,8 +218,8 @@ class CodePushSetupSubCommand extends Command { _logger.info('Supported Flutter versions for code push:'); _logger.info(''); - for (final entry in versions.entries.toList() - ..sort((a, b) => a.key.compareTo(b.key))) { + for (final entry + in versions.entries.toList()..sort((a, b) => a.key.compareTo(b.key))) { final ver = entry.key; final markers = []; if (ver == current) markers.add('current'); diff --git a/lib/src/commands/codepush_commands/_codepush_status.dart b/lib/src/commands/codepush_commands/_codepush_status.dart index 903f971..93ad968 100644 --- a/lib/src/commands/codepush_commands/_codepush_status.dart +++ b/lib/src/commands/codepush_commands/_codepush_status.dart @@ -7,20 +7,13 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushStatusSubCommand extends Command { CodePushStatusSubCommand(this._logger) { argParser - ..addOption( - 'app-id', - help: 'The app ID to check status for.', - ) + ..addOption('app-id', help: 'The app ID to check status for.') ..addOption( 'release-id', help: 'If set, output only the patches for this release (ignored without --json).', ) - ..addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + ..addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -73,18 +66,19 @@ class CodePushStatusSubCommand extends Command { if (statusCode != 200) { // --json is a structured-output contract: emit valid JSON with an // explicit error field and exit 0 so callers can parse it. - _logger.info(json.encode({ - 'release_id': releaseId, - 'patches': >[], - 'error': patchesResult['error'] ?? 'Unknown', - })); + _logger.info( + json.encode({ + 'release_id': releaseId, + 'patches': >[], + 'error': patchesResult['error'] ?? 'Unknown', + }), + ); return ExitCode.success.code; } final patches = patchesResult['patches'] as List? ?? []; - _logger.info(json.encode({ - 'release_id': releaseId, - 'patches': patches, - })); + _logger.info( + json.encode({'release_id': releaseId, 'patches': patches}), + ); return ExitCode.success.code; } @@ -97,11 +91,13 @@ class CodePushStatusSubCommand extends Command { if (statusCode == 401) { if (asJson) { - _logger.info(json.encode({ - 'configured': false, - 'logged_in': false, - 'app_id': appId, - })); + _logger.info( + json.encode({ + 'configured': false, + 'logged_in': false, + 'app_id': appId, + }), + ); return ExitCode.success.code; } progress?.fail('Session expired. Run "fcp codepush login" again.'); @@ -110,14 +106,16 @@ class CodePushStatusSubCommand extends Command { if (statusCode != 200) { if (asJson) { - _logger.info(json.encode({ - 'configured': true, - 'logged_in': true, - 'app_id': appId, - 'releases': >[], - 'total_patches': 0, - 'error': releasesResult['error'] ?? 'Unknown', - })); + _logger.info( + json.encode({ + 'configured': true, + 'logged_in': true, + 'app_id': appId, + 'releases': >[], + 'total_patches': 0, + 'error': releasesResult['error'] ?? 'Unknown', + }), + ); return ExitCode.success.code; } progress?.fail('Error: ${releasesResult['error'] ?? 'Unknown'}'); @@ -151,14 +149,16 @@ class CodePushStatusSubCommand extends Command { totalPatches += count; enrichedReleases.add(release); } - _logger.info(json.encode({ - 'configured': true, - 'logged_in': true, - 'app_id': appId, - 'app_name': appName, - 'releases': enrichedReleases, - 'total_patches': totalPatches, - })); + _logger.info( + json.encode({ + 'configured': true, + 'logged_in': true, + 'app_id': appId, + 'app_name': appName, + 'releases': enrichedReleases, + 'total_patches': totalPatches, + }), + ); return ExitCode.success.code; } @@ -166,7 +166,8 @@ class CodePushStatusSubCommand extends Command { if (releases.isEmpty) { _logger.info( - '\n No releases yet. Run "fcp codepush release" to create one.'); + '\n No releases yet. Run "fcp codepush release" to create one.', + ); return ExitCode.success.code; } @@ -201,13 +202,15 @@ class CodePushStatusSubCommand extends Command { return ExitCode.success.code; } catch (e) { if (asJson) { - _logger.info(json.encode({ - 'configured': false, - 'logged_in': false, - 'releases': >[], - 'total_patches': 0, - 'error': '$e', - })); + _logger.info( + json.encode({ + 'configured': false, + 'logged_in': false, + 'releases': >[], + 'total_patches': 0, + 'error': '$e', + }), + ); return ExitCode.success.code; } progress?.fail('Failed: $e'); diff --git a/lib/src/commands/codepush_commands/_codepush_versions.dart b/lib/src/commands/codepush_commands/_codepush_versions.dart index 8e07674..6eb82e6 100644 --- a/lib/src/commands/codepush_commands/_codepush_versions.dart +++ b/lib/src/commands/codepush_commands/_codepush_versions.dart @@ -15,11 +15,7 @@ import 'package:mason_logger/mason_logger.dart'; /// code push Flutter version (project-pinned wins over global). class CodePushVersionsSubCommand extends Command { CodePushVersionsSubCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -35,8 +31,10 @@ class CodePushVersionsSubCommand extends Command { final asJson = argResults?['json'] == true; final serverUrl = await CodePushClient.getServerUrl(); - final manager = - CodePushArtifactManager(logger: _logger, baseUrl: serverUrl); + final manager = CodePushArtifactManager( + logger: _logger, + baseUrl: serverUrl, + ); final progress = asJson ? null : _logger.progress('Fetching versions'); final manifest = await manager.fetchSupportedVersions(); @@ -47,11 +45,13 @@ class CodePushVersionsSubCommand extends Command { // explicit error field and return success so callers (VS Code / // IntelliJ tree providers) can parse the payload and render a // warning row instead of treating the process as a hard failure. - _logger.info(json.encode({ - 'selected': null, - 'versions': >[], - 'error': 'Failed to fetch version manifest from server.', - })); + _logger.info( + json.encode({ + 'selected': null, + 'versions': >[], + 'error': 'Failed to fetch version manifest from server.', + }), + ); return ExitCode.success.code; } return ExitCode.software.code; @@ -85,10 +85,7 @@ class CodePushVersionsSubCommand extends Command { } if (asJson) { - _logger.info(json.encode({ - 'selected': selected, - 'versions': versions, - })); + _logger.info(json.encode({'selected': selected, 'versions': versions})); return ExitCode.success.code; } @@ -102,8 +99,9 @@ class CodePushVersionsSubCommand extends Command { if (v['global'] == true) markers.add('global'); if (v['project_pinned'] == true) markers.add('pinned'); final suffix = markers.isEmpty ? '' : ' (${markers.join(', ')})'; - final marker = - name == selected ? '*' : (v['installed'] == true ? ' ' : '-'); + final marker = name == selected + ? '*' + : (v['installed'] == true ? ' ' : '-'); _logger.info(' $marker $name$suffix'); } if (selected == null) { diff --git a/lib/src/commands/config_command.dart b/lib/src/commands/config_command.dart index fb61215..4a3550c 100644 --- a/lib/src/commands/config_command.dart +++ b/lib/src/commands/config_command.dart @@ -64,11 +64,7 @@ class ConfigCommand extends Command { class _ConfigListSubCommand extends Command { _ConfigListSubCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -119,7 +115,8 @@ class _ConfigGetSubCommand extends Command { final String name = 'get'; @override - final String description = 'Get a configuration value. ' + final String description = + 'Get a configuration value. ' 'Usage: config get '; @override @@ -154,7 +151,8 @@ class _ConfigSetSubCommand extends Command { final String name = 'set'; @override - final String description = 'Set a configuration value. ' + final String description = + 'Set a configuration value. ' 'Usage: config set '; @override diff --git a/lib/src/commands/doctor_command.dart b/lib/src/commands/doctor_command.dart index 437ef49..24b87e6 100644 --- a/lib/src/commands/doctor_command.dart +++ b/lib/src/commands/doctor_command.dart @@ -111,8 +111,10 @@ Future _isGclientAvailable() async { // Check depot_tools_path from .flutter_compilerc final rcFile = File('$home/.flutter_compilerc'); - final depotPath = - await F.readValueForKeyFromRcConfig(rcFile, RunCommandKey.depotTools.key); + final depotPath = await F.readValueForKeyFromRcConfig( + rcFile, + RunCommandKey.depotTools.key, + ); if (depotPath != null && depotPath.isNotEmpty) { final gclient = File('$depotPath/$bin'); if (await gclient.exists()) return true; @@ -170,11 +172,9 @@ Future _checkEnvironmentForGather({ // Check for upstream and origin git remotes try { - final result = await Process.run( - 'git', - ['remote'], - workingDirectory: gitDir, - ); + final result = await Process.run('git', [ + 'remote', + ], workingDirectory: gitDir); if (result.exitCode == 0) { final remotes = (result.stdout as String).trim().split('\n'); final hasUpstream = remotes.contains('upstream'); @@ -219,11 +219,7 @@ Future _checkEnvironmentForGather({ class DoctorCommand extends Command { DoctorCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -256,9 +252,11 @@ class DoctorCommand extends Command { final status = check['status'] as String; if (category == 'tools') { - _logger.info(status == 'ok' - ? ' [+] $name is installed' - : ' [X] $name is NOT installed'); + _logger.info( + status == 'ok' + ? ' [+] $name is installed' + : ' [X] $name is NOT installed', + ); } else if (category == 'engine_tools') { final displayName = switch (name) { 'gclient' => 'depot_tools (gclient)', @@ -266,9 +264,11 @@ class DoctorCommand extends Command { 'visual_studio' => 'Visual Studio (cl.exe)', _ => name, }; - _logger.info(status == 'ok' - ? ' [+] $displayName is installed' - : ' [X] $displayName is NOT installed'); + _logger.info( + status == 'ok' + ? ' [+] $displayName is installed' + : ' [X] $displayName is NOT installed', + ); } else if (category == 'config') { if (status == 'ok') { _logger.info(' [+] .flutter_compilerc is valid'); diff --git a/lib/src/commands/flutter_switch_command.dart b/lib/src/commands/flutter_switch_command.dart index 052fc2f..e833f65 100644 --- a/lib/src/commands/flutter_switch_command.dart +++ b/lib/src/commands/flutter_switch_command.dart @@ -26,8 +26,9 @@ class FlutterSwitchCommand extends Command { @override Future run() async { - final mode = - argResults?.rest.isNotEmpty == true ? argResults?.rest[0] : null; + final mode = argResults?.rest.isNotEmpty == true + ? argResults?.rest[0] + : null; if (mode == null) { _logger.info('Switching to other flutter installation'); await F.switchFlutterEnvironment(); diff --git a/lib/src/commands/install_commands/_devtools.dart b/lib/src/commands/install_commands/_devtools.dart index d766ee9..775c433 100644 --- a/lib/src/commands/install_commands/_devtools.dart +++ b/lib/src/commands/install_commands/_devtools.dart @@ -70,9 +70,11 @@ Future setupDevToolsEnvironment(Logger l) async { l.info('Fetching upstream...'); await F.runCommand('git', ['fetch', 'upstream'], workingDirectory: cloneDir); try { - await F.runCommand( - 'git', ['branch', '--set-upstream-to=upstream/master', 'master'], - workingDirectory: cloneDir); + await F.runCommand('git', [ + 'branch', + '--set-upstream-to=upstream/master', + 'master', + ], workingDirectory: cloneDir); } catch (_) { // May fail if already set or branch name differs — non-fatal l.warn('Could not set upstream tracking branch (may already be set).'); @@ -86,10 +88,7 @@ Future setupDevToolsEnvironment(Logger l) async { } // Run Flutter pub get for the tool directory - await F.runCommand( - 'flutter', - ['pub', 'get', '--directory', toolDir.path], - ); + await F.runCommand('flutter', ['pub', 'get', '--directory', toolDir.path]); // Save devtools path to .flutter_compilerc final home = F.homeDir(); @@ -110,8 +109,10 @@ Future setupDevToolsEnvironment(Logger l) async { envContents = await envFile.readAsString(); } - final devtoolsToolBinPath = - Constants.platformDevToolsPATHExport.replaceAll('{{path}}', cloneDir); + final devtoolsToolBinPath = Constants.platformDevToolsPATHExport.replaceAll( + '{{path}}', + cloneDir, + ); if (!envContents.contains(devtoolsToolBinPath.trim())) { envContents += devtoolsToolBinPath; await envFile.parent.create(recursive: true); @@ -121,21 +122,26 @@ Future setupDevToolsEnvironment(Logger l) async { // Optional step: Check and update the DevTools Flutter SDK try { - await F.runCommand( - 'devtools_tool', ['update-flutter-sdk', '--update-on-path']); + await F.runCommand('devtools_tool', [ + 'update-flutter-sdk', + '--update-on-path', + ]); } catch (e) { l.warn( - 'devtools_tool update-flutter-sdk failed (may need terminal restart): $e'); + 'devtools_tool update-flutter-sdk failed (may need terminal restart): $e', + ); } // Inform the user to restart their terminal l ..info( - '\nSetup complete! Please restart your terminal or source your shell configuration to apply PATH changes.\n' - .green) + '\nSetup complete! Please restart your terminal or source your shell configuration to apply PATH changes.\n' + .green, + ) ..info('To verify the setup, run the following command:') ..info( - '`flutter run` on a sample Flutter project and connect it to DevTools.'); + '`flutter run` on a sample Flutter project and connect it to DevTools.', + ); await displayIncrementalInfo(); return ExitCode.success.code; @@ -144,26 +150,38 @@ Future setupDevToolsEnvironment(Logger l) async { /// Ensure a git remote exists with the given URL. /// If it already exists, update its URL. If not, add it. Future _ensureRemote( - Logger l, String name, String url, String workingDirectory) async { - final result = await Process.run( - 'git', - ['remote', 'get-url', name], - workingDirectory: workingDirectory, - ); + Logger l, + String name, + String url, + String workingDirectory, +) async { + final result = await Process.run('git', [ + 'remote', + 'get-url', + name, + ], workingDirectory: workingDirectory); if (result.exitCode == 0) { // Remote exists — update URL if different final currentUrl = (result.stdout as String).trim(); if (currentUrl != url) { - await F.runCommand('git', ['remote', 'set-url', name, url], - workingDirectory: workingDirectory); + await F.runCommand('git', [ + 'remote', + 'set-url', + name, + url, + ], workingDirectory: workingDirectory); l.info('Updated remote "$name" to $url'.green); } else { l.info('Remote "$name" already set to $url'.green); } } else { // Remote doesn't exist — add it - await F.runCommand('git', ['remote', 'add', name, url], - workingDirectory: workingDirectory); + await F.runCommand('git', [ + 'remote', + 'add', + name, + url, + ], workingDirectory: workingDirectory); l.info('Added remote "$name" → $url'.green); } } diff --git a/lib/src/commands/install_commands/_engine.dart b/lib/src/commands/install_commands/_engine.dart index 52770ea..75cc12e 100644 --- a/lib/src/commands/install_commands/_engine.dart +++ b/lib/src/commands/install_commands/_engine.dart @@ -39,8 +39,11 @@ class EngineSubCommand extends Command { } } -Future setupEngineEnvironment(Logger l, String platform, - {bool force = false}) async { +Future setupEngineEnvironment( + Logger l, + String platform, { + bool force = false, +}) async { l.info('Flutter Engine Development Environment Setup for $platform'.blue); final os = Platform.operatingSystem; @@ -95,11 +98,11 @@ Future setupEngineEnvironment(Logger l, String platform, } // Verify it's a git repo with an origin remote - final originResult = await Process.run( - 'git', - ['remote', 'get-url', 'origin'], - workingDirectory: flutterDir, - ); + final originResult = await Process.run('git', [ + 'remote', + 'get-url', + 'origin', + ], workingDirectory: flutterDir); if (originResult.exitCode != 0) { l.err( 'Could not read git origin URL from $flutterDir.\n' @@ -119,8 +122,10 @@ Future setupEngineEnvironment(Logger l, String platform, if (await gclientFile.exists()) { l.info('.gclient already exists, skipping generation.'.green); } else { - final gclientContent = - Constants.gclientFileTemplate.replaceAll('{{flutter_url}}', originUrl); + final gclientContent = Constants.gclientFileTemplate.replaceAll( + '{{flutter_url}}', + originUrl, + ); await F.writeFile('$flutterDir/.gclient', gclientContent); l.info('Generated .gclient file.'.green); } @@ -201,8 +206,10 @@ Future _ensureDepotTools(Logger l) async { if (await envFile.exists()) { envContents = await envFile.readAsString(); } - final depotToolsExport = Constants.platformDepotToolsPATHExport - .replaceAll('{{path}}', depotToolsPath); + final depotToolsExport = Constants.platformDepotToolsPATHExport.replaceAll( + '{{path}}', + depotToolsPath, + ); if (!envContents.contains(depotToolsExport.trim())) { envContents += depotToolsExport; await envFile.parent.create(recursive: true); diff --git a/lib/src/commands/install_commands/_flutter.dart b/lib/src/commands/install_commands/_flutter.dart index e835273..e77fa1b 100644 --- a/lib/src/commands/install_commands/_flutter.dart +++ b/lib/src/commands/install_commands/_flutter.dart @@ -9,14 +9,11 @@ import 'package:mason_logger/mason_logger.dart'; class FlutterSubCommand extends Command { FlutterSubCommand(this._logger) { argParser - ..addFlag( - 'flutter', - abbr: 'f', - help: 'Install Flutter environment', - ) + ..addFlag('flutter', abbr: 'f', help: 'Install Flutter environment') ..addOption( 'ide', - help: 'Calling IDE (vscode, intellij). ' + help: + 'Calling IDE (vscode, intellij). ' 'Skips or auto-accepts IDE-specific prompts.', allowed: ['vscode', 'intellij'], ); @@ -113,12 +110,8 @@ Future setupFlutterEnvironment(Logger l, {String? ide}) async { .green, ) ..info('\nRun\n') - ..info( - 'flutter_compile switch compiled'.blue, - ) - ..info( - '\nto switch to the compiled Flutter installation.', - ); + ..info('flutter_compile switch compiled'.blue) + ..info('\nto switch to the compiled Flutter installation.'); return ExitCode.success.code; } @@ -126,24 +119,36 @@ Future setupFlutterEnvironment(Logger l, {String? ide}) async { /// Ensure a git remote exists with the given URL. /// If it already exists, update its URL. If not, add it. Future _ensureRemote( - Logger l, String name, String url, String workingDirectory) async { - final result = await Process.run( - 'git', - ['remote', 'get-url', name], - workingDirectory: workingDirectory, - ); + Logger l, + String name, + String url, + String workingDirectory, +) async { + final result = await Process.run('git', [ + 'remote', + 'get-url', + name, + ], workingDirectory: workingDirectory); if (result.exitCode == 0) { final currentUrl = (result.stdout as String).trim(); if (currentUrl != url) { - await F.runCommand('git', ['remote', 'set-url', name, url], - workingDirectory: workingDirectory); + await F.runCommand('git', [ + 'remote', + 'set-url', + name, + url, + ], workingDirectory: workingDirectory); l.info('Updated remote "$name" to $url'.green); } else { l.info('Remote "$name" already set to $url'.green); } } else { - await F.runCommand('git', ['remote', 'add', name, url], - workingDirectory: workingDirectory); + await F.runCommand('git', [ + 'remote', + 'add', + name, + url, + ], workingDirectory: workingDirectory); l.info('Added remote "$name" → $url'.green); } } diff --git a/lib/src/commands/run_command.dart b/lib/src/commands/run_command.dart index 7abe60f..326ea67 100644 --- a/lib/src/commands/run_command.dart +++ b/lib/src/commands/run_command.dart @@ -50,7 +50,8 @@ class RunCommand extends Command { final List aliases = ['r']; @override - final String description = 'Run a Flutter app with a local engine build.\n\n' + final String description = + 'Run a Flutter app with a local engine build.\n\n' 'Extra arguments after -- are forwarded to flutter run ' '(e.g. -- -d chrome).'; @@ -119,15 +120,12 @@ class RunCommand extends Command { _logger.info('Running with local engine: $outputDir'); - await F.runCommand( - 'flutter', - [ - 'run', - '--local-engine=$outputDir', - '--local-engine-src-path=$srcDir', - ...extraArgs, - ], - ); + await F.runCommand('flutter', [ + 'run', + '--local-engine=$outputDir', + '--local-engine-src-path=$srcDir', + ...extraArgs, + ]); return ExitCode.success.code; } diff --git a/lib/src/commands/sdk_commands/_sdk_exec.dart b/lib/src/commands/sdk_commands/_sdk_exec.dart index 4cc3c5b..c7b25a1 100644 --- a/lib/src/commands/sdk_commands/_sdk_exec.dart +++ b/lib/src/commands/sdk_commands/_sdk_exec.dart @@ -51,7 +51,8 @@ class SdkExecSubCommand extends Command { final sdkPath = F.sdkVersionPath(version); final environment = { ...F.sdkEnvironment(sdkPath), - 'PATH': '$sdkPath/bin${F.envPathSeparator}$sdkPath/bin/cache/dart-sdk/bin' + 'PATH': + '$sdkPath/bin${F.envPathSeparator}$sdkPath/bin/cache/dart-sdk/bin' '${F.envPathSeparator}${Platform.environment['PATH'] ?? ''}', }; diff --git a/lib/src/commands/sdk_commands/_sdk_global.dart b/lib/src/commands/sdk_commands/_sdk_global.dart index 937f800..c1cca6c 100644 --- a/lib/src/commands/sdk_commands/_sdk_global.dart +++ b/lib/src/commands/sdk_commands/_sdk_global.dart @@ -34,9 +34,7 @@ class SdkGlobalSubCommand extends Command { final globalVersion = await F.readGlobalSdkVersion(); if (globalVersion == null) { _logger.info('No global SDK version set.'); - _logger.info( - '\nRun "flutter_compile sdk global " to set one.', - ); + _logger.info('\nRun "flutter_compile sdk global " to set one.'); } else { _logger.info('Global SDK version: $globalVersion'); } diff --git a/lib/src/commands/sdk_commands/_sdk_install.dart b/lib/src/commands/sdk_commands/_sdk_install.dart index 4b5d4f3..8394d1f 100644 --- a/lib/src/commands/sdk_commands/_sdk_install.dart +++ b/lib/src/commands/sdk_commands/_sdk_install.dart @@ -18,7 +18,8 @@ class SdkInstallSubCommand extends Command { @override final String name = 'install'; @override - final String description = 'Install a Flutter SDK version or channel.\n\n' + final String description = + 'Install a Flutter SDK version or channel.\n\n' 'Usage: flutter_compile sdk install [--force] \n' 'Examples:\n' ' flutter_compile sdk install 3.19.0\n' @@ -56,17 +57,12 @@ Future installSdk(Logger l, String version, {bool force = false}) async { await Directory('$home${Constants.sdkVersionsPath}').create(recursive: true); - await F.cloneRepository( - Constants.flutterGitUrl, - targetPath, - force: force, - ); + await F.cloneRepository(Constants.flutterGitUrl, targetPath, force: force); - await F.runCommand( - 'git', - ['checkout', version], - workingDirectory: targetPath, - ); + await F.runCommand('git', [ + 'checkout', + version, + ], workingDirectory: targetPath); l.info('Caching Flutter SDK artifacts...'); await F.runCommand( diff --git a/lib/src/commands/sdk_commands/_sdk_list.dart b/lib/src/commands/sdk_commands/_sdk_list.dart index 9b4dd4a..dbb3245 100644 --- a/lib/src/commands/sdk_commands/_sdk_list.dart +++ b/lib/src/commands/sdk_commands/_sdk_list.dart @@ -8,11 +8,7 @@ import 'package:mason_logger/mason_logger.dart'; class SdkListSubCommand extends Command { SdkListSubCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -40,12 +36,13 @@ Future>> gatherSdkList() async { return >[]; } - final entries = versionsDir - .listSync() - .whereType() - .where((d) => d.path.split('/').last != Constants.defaultSdkLink) - .toList() - ..sort((a, b) => a.path.compareTo(b.path)); + final entries = + versionsDir + .listSync() + .whereType() + .where((d) => d.path.split('/').last != Constants.defaultSdkLink) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); final globalVersion = await F.readGlobalSdkVersion(); final projectVersion = await F.readProjectSdkVersion(); @@ -61,8 +58,9 @@ Future>> gatherSdkList() async { }); } - final compiledFlutterDir = - Directory('$home${Constants.flutterCompileInstallPath}'); + final compiledFlutterDir = Directory( + '$home${Constants.flutterCompileInstallPath}', + ); if (compiledFlutterDir.existsSync()) { sdks.add({ 'version': 'compiled', @@ -84,9 +82,7 @@ Future listSdks(Logger l, {bool asJson = false}) async { l.info(json.encode(>[])); } else { l.info('No Flutter SDKs installed.'); - l.info( - '\nRun "flutter_compile sdk install " to install one.', - ); + l.info('\nRun "flutter_compile sdk install " to install one.'); } return ExitCode.success.code; } @@ -113,9 +109,7 @@ Future listSdks(Logger l, {bool asJson = false}) async { if (contributor.isNotEmpty) { l.info('\nContributor environments:'); for (final sdk in contributor) { - l.info( - ' ${sdk['version']} ${sdk['path']} (via install flutter)', - ); + l.info(' ${sdk['version']} ${sdk['path']} (via install flutter)'); } } diff --git a/lib/src/commands/sdk_commands/_sdk_remove.dart b/lib/src/commands/sdk_commands/_sdk_remove.dart index 1ec386c..e4c4d30 100644 --- a/lib/src/commands/sdk_commands/_sdk_remove.dart +++ b/lib/src/commands/sdk_commands/_sdk_remove.dart @@ -13,7 +13,8 @@ class SdkRemoveSubCommand extends Command { @override final String name = 'remove'; @override - final String description = 'Remove an installed Flutter SDK version.\n\n' + final String description = + 'Remove an installed Flutter SDK version.\n\n' 'Usage: flutter_compile sdk remove \n' 'Example: flutter_compile sdk remove 3.19.0'; diff --git a/lib/src/commands/sdk_commands/_sdk_use.dart b/lib/src/commands/sdk_commands/_sdk_use.dart index f1fcc32..f89ac2d 100644 --- a/lib/src/commands/sdk_commands/_sdk_use.dart +++ b/lib/src/commands/sdk_commands/_sdk_use.dart @@ -37,9 +37,7 @@ class SdkUseSubCommand extends Command { 'No project SDK version set ' '(no ${Constants.flutterVersionFile} found).', ); - _logger.info( - '\nRun "flutter_compile sdk use " to pin one.', - ); + _logger.info('\nRun "flutter_compile sdk use " to pin one.'); } else { _logger.info('Project SDK version: $projectVersion'); } @@ -55,8 +53,9 @@ class SdkUseSubCommand extends Command { return ExitCode.usage.code; } - final file = - File('${Directory.current.path}/${Constants.flutterVersionFile}'); + final file = File( + '${Directory.current.path}/${Constants.flutterVersionFile}', + ); await file.writeAsString('$version\n'); _logger.success( diff --git a/lib/src/commands/status_command.dart b/lib/src/commands/status_command.dart index 010b221..c259673 100644 --- a/lib/src/commands/status_command.dart +++ b/lib/src/commands/status_command.dart @@ -63,11 +63,7 @@ Future> gatherStatus() async { class StatusCommand extends Command { StatusCommand(this._logger) { - argParser.addFlag( - 'json', - help: 'Output as JSON.', - negatable: false, - ); + argParser.addFlag('json', help: 'Output as JSON.', negatable: false); } final Logger _logger; @@ -125,9 +121,7 @@ class StatusCommand extends Command { for (final b in builds) { final name = b['name']!; final size = b['size']!; - _logger.info( - ' $name${' ' * (30 - name.length).clamp(0, 30)}$size', - ); + _logger.info(' $name${' ' * (30 - name.length).clamp(0, 30)}$size'); } } } diff --git a/lib/src/commands/sync_commands/_sync_devtools.dart b/lib/src/commands/sync_commands/_sync_devtools.dart index c12d061..782ede1 100644 --- a/lib/src/commands/sync_commands/_sync_devtools.dart +++ b/lib/src/commands/sync_commands/_sync_devtools.dart @@ -43,16 +43,14 @@ class SyncDevtoolsSubCommand extends Command { return ExitCode.config.code; } - await F.runCommand( - 'git', - ['fetch', 'upstream'], - workingDirectory: devtoolsPath, - ); - await F.runCommand( - 'git', - ['rebase', 'upstream/master'], - workingDirectory: devtoolsPath, - ); + await F.runCommand('git', [ + 'fetch', + 'upstream', + ], workingDirectory: devtoolsPath); + await F.runCommand('git', [ + 'rebase', + 'upstream/master', + ], workingDirectory: devtoolsPath); _logger.success('DevTools synced successfully.'); return ExitCode.success.code; diff --git a/lib/src/commands/sync_commands/_sync_engine.dart b/lib/src/commands/sync_commands/_sync_engine.dart index 7186891..9c0c04d 100644 --- a/lib/src/commands/sync_commands/_sync_engine.dart +++ b/lib/src/commands/sync_commands/_sync_engine.dart @@ -76,16 +76,14 @@ class SyncEngineSubCommand extends Command { // Git remotes are on the Flutter repo root (parent of engine dir) try { - await F.runCommand( - 'git', - ['fetch', 'upstream'], - workingDirectory: flutterRoot, - ); - await F.runCommand( - 'git', - ['rebase', 'upstream/master'], - workingDirectory: flutterRoot, - ); + await F.runCommand('git', [ + 'fetch', + 'upstream', + ], workingDirectory: flutterRoot); + await F.runCommand('git', [ + 'rebase', + 'upstream/master', + ], workingDirectory: flutterRoot); } on Exception catch (e) { _logger.err( 'Git sync failed: $e\n' diff --git a/lib/src/commands/sync_commands/_sync_flutter.dart b/lib/src/commands/sync_commands/_sync_flutter.dart index caab4a7..1c94bf2 100644 --- a/lib/src/commands/sync_commands/_sync_flutter.dart +++ b/lib/src/commands/sync_commands/_sync_flutter.dart @@ -46,23 +46,19 @@ class SyncFlutterSubCommand extends Command { return ExitCode.config.code; } - await F.runCommand( - 'git', - ['fetch', 'upstream'], - workingDirectory: flutterDir, - ); - await F.runCommand( - 'git', - ['rebase', 'upstream/master'], - workingDirectory: flutterDir, - ); + await F.runCommand('git', [ + 'fetch', + 'upstream', + ], workingDirectory: flutterDir); + await F.runCommand('git', [ + 'rebase', + 'upstream/master', + ], workingDirectory: flutterDir); _logger.info('Running flutter update-packages...'); - await F.runCommand( - '$flutterPath/flutter', - ['update-packages'], - workingDirectory: flutterDir, - ); + await F.runCommand('$flutterPath/flutter', [ + 'update-packages', + ], workingDirectory: flutterDir); _logger.success('Flutter framework synced successfully.'); return ExitCode.success.code; diff --git a/lib/src/commands/sync_commands/sync_command.dart b/lib/src/commands/sync_commands/sync_command.dart index 83892b0..36b2760 100644 --- a/lib/src/commands/sync_commands/sync_command.dart +++ b/lib/src/commands/sync_commands/sync_command.dart @@ -53,10 +53,14 @@ class SyncCommand extends Command { _logger.info(''); } - final succeeded = - results.entries.where((e) => e.value).map((e) => e.key).toList(); - final failed = - results.entries.where((e) => !e.value).map((e) => e.key).toList(); + final succeeded = results.entries + .where((e) => e.value) + .map((e) => e.key) + .toList(); + final failed = results.entries + .where((e) => !e.value) + .map((e) => e.key) + .toList(); if (succeeded.isNotEmpty) { _logger.success('Synced: ${succeeded.join(', ')}'); diff --git a/lib/src/commands/test_command.dart b/lib/src/commands/test_command.dart index 045b51f..c931361 100644 --- a/lib/src/commands/test_command.dart +++ b/lib/src/commands/test_command.dart @@ -50,7 +50,8 @@ class TestCommand extends Command { final List aliases = ['t']; @override - final String description = 'Run Flutter tests with a local engine build.\n\n' + final String description = + 'Run Flutter tests with a local engine build.\n\n' 'Extra arguments after -- are forwarded to flutter test ' '(e.g. -- test/my_test.dart).'; @@ -119,15 +120,12 @@ class TestCommand extends Command { _logger.info('Running tests with local engine: $outputDir'); - await F.runCommand( - 'flutter', - [ - 'test', - '--local-engine=$outputDir', - '--local-engine-src-path=$srcDir', - ...extraArgs, - ], - ); + await F.runCommand('flutter', [ + 'test', + '--local-engine=$outputDir', + '--local-engine-src-path=$srcDir', + ...extraArgs, + ]); return ExitCode.success.code; } diff --git a/lib/src/commands/uninstall_command.dart b/lib/src/commands/uninstall_command.dart index 94ad9c7..e47d1a8 100644 --- a/lib/src/commands/uninstall_command.dart +++ b/lib/src/commands/uninstall_command.dart @@ -39,8 +39,11 @@ class UninstallCommand extends Command { class FlutterUninstallSubCommand extends Command { FlutterUninstallSubCommand(this._logger) { - argParser.addFlag('flutter', - abbr: 'f', help: 'Uninstall Flutter environment'); + argParser.addFlag( + 'flutter', + abbr: 'f', + help: 'Uninstall Flutter environment', + ); } final Logger _logger; @@ -58,8 +61,11 @@ class FlutterUninstallSubCommand extends Command { class DevToolsUninstallSubCommand extends Command { DevToolsUninstallSubCommand(this._logger) { - argParser.addFlag('devtools', - abbr: 'd', help: 'Uninstall DevTools environment'); + argParser.addFlag( + 'devtools', + abbr: 'd', + help: 'Uninstall DevTools environment', + ); } final Logger _logger; @@ -177,7 +183,9 @@ Future uninstallDevToolsEnvironment(Logger l) async { // Read devtools path from config, fall back to default var devtoolsPath = await F.readValueForKeyFromRcConfig( - rcConfigFile, RunCommandKey.devTools.key); + rcConfigFile, + RunCommandKey.devTools.key, + ); devtoolsPath ??= '$home${Constants.devToolsInstallPath}'; // Delete the directory if it exists @@ -194,8 +202,10 @@ Future uninstallDevToolsEnvironment(Logger l) async { final envFile = File(envPath); if (await envFile.exists()) { var envContents = await envFile.readAsString(); - final devtoolsExport = Constants.platformDevToolsPATHExport - .replaceAll('{{path}}', devtoolsPath); + final devtoolsExport = Constants.platformDevToolsPATHExport.replaceAll( + '{{path}}', + devtoolsPath, + ); if (envContents.contains(devtoolsExport)) { envContents = envContents.replaceAll(devtoolsExport, ''); await envFile.writeAsString(envContents); @@ -208,8 +218,10 @@ Future uninstallDevToolsEnvironment(Logger l) async { final configFile = File(configPath); if (await configFile.exists()) { var contents = await configFile.readAsString(); - final devtoolsExport = Constants.platformDevToolsPATHExport - .replaceAll('{{path}}', devtoolsPath); + final devtoolsExport = Constants.platformDevToolsPATHExport.replaceAll( + '{{path}}', + devtoolsPath, + ); if (contents.contains(devtoolsExport)) { contents = contents.replaceAll(devtoolsExport, ''); await configFile.writeAsString(contents); @@ -278,8 +290,9 @@ Future uninstallEngineEnvironment(Logger l) async { if (_depotToolsBlockPattern.hasMatch(envContents)) { envContents = envContents.replaceAll(_depotToolsBlockPattern, ''); await envFile.writeAsString(envContents); - l.info('Removed depot_tools PATH export from .${Constants.envFile}.' - .green); + l.info( + 'Removed depot_tools PATH export from .${Constants.envFile}.'.green, + ); } } @@ -298,9 +311,7 @@ Future uninstallEngineEnvironment(Logger l) async { if (await rcConfigFile.exists()) { final lines = await rcConfigFile.readAsLines(); final filtered = lines - .where( - (line) => !line.startsWith('${RunCommandKey.depotTools.key}:'), - ) + .where((line) => !line.startsWith('${RunCommandKey.depotTools.key}:')) .toList(); await rcConfigFile.writeAsString('${filtered.join('\n')}\n'); l.info('Removed depot_tools_path from .flutter_compilerc.'.green); diff --git a/lib/src/commands/update_command.dart b/lib/src/commands/update_command.dart index 684774f..bb8a9c2 100644 --- a/lib/src/commands/update_command.dart +++ b/lib/src/commands/update_command.dart @@ -13,11 +13,9 @@ import 'package:pub_updater/pub_updater.dart'; /// {@endtemplate} class UpdateCommand extends Command { /// {@macro update_command} - UpdateCommand({ - required Logger logger, - PubUpdater? pubUpdater, - }) : _logger = logger, - _pubUpdater = pubUpdater ?? PubUpdater(); + UpdateCommand({required Logger logger, PubUpdater? pubUpdater}) + : _logger = logger, + _pubUpdater = pubUpdater ?? PubUpdater(); final Logger _logger; final PubUpdater _pubUpdater; diff --git a/lib/src/daemon/daemon_peer.dart b/lib/src/daemon/daemon_peer.dart index 35f4512..e7a02ef 100644 --- a/lib/src/daemon/daemon_peer.dart +++ b/lib/src/daemon/daemon_peer.dart @@ -17,7 +17,7 @@ import 'package:stream_channel/stream_channel.dart'; class DaemonPeer { DaemonPeer({required Logger logger, StreamChannel? channel}) - : _channel = channel; + : _channel = channel; final StreamChannel? _channel; late rpc.Peer _peer; diff --git a/lib/src/shared/codepush_archive_service.dart b/lib/src/shared/codepush_archive_service.dart index 56f42d1..5efb4e3 100644 --- a/lib/src/shared/codepush_archive_service.dart +++ b/lib/src/shared/codepush_archive_service.dart @@ -23,11 +23,9 @@ import 'package:mason_logger/mason_logger.dart'; /// `.fcp-archive/` to the repo's local-only `.git/info/exclude` on /// first use. No project-level `.gitignore` is touched. class CodePushArchiveService { - CodePushArchiveService({ - required Logger logger, - Directory? projectDir, - }) : _logger = logger, - _projectDir = projectDir ?? Directory.current; + CodePushArchiveService({required Logger logger, Directory? projectDir}) + : _logger = logger, + _projectDir = projectDir ?? Directory.current; static const int _archiveFormatVersion = 1; static const String _archiveDirName = '.fcp-archive'; @@ -76,8 +74,11 @@ class CodePushArchiveService { releaseDir.createSync(recursive: true); final runnerCopy = '${releaseDir.path}/Runner.app'; - final cpRunner = - Process.runSync('cp', ['-R', runnerApp.path, runnerCopy]); + final cpRunner = Process.runSync('cp', [ + '-R', + runnerApp.path, + runnerCopy, + ]); if (cpRunner.exitCode != 0) { _logger.warn('Could not archive Runner.app: ${cpRunner.stderr}'); return false; @@ -89,10 +90,7 @@ class CodePushArchiveService { var archivedDsym = false; if (dsymSource.existsSync()) { final dsymCopy = '${releaseDir.path}/Runner.app.dSYM'; - final cpDsym = Process.runSync( - 'cp', - ['-R', dsymSource.path, dsymCopy], - ); + final cpDsym = Process.runSync('cp', ['-R', dsymSource.path, dsymCopy]); if (cpDsym.exitCode == 0) { archivedDsym = true; } else { @@ -151,8 +149,9 @@ class CodePushArchiveService { infoDir.createSync(recursive: true); } final excludeFile = File('${infoDir.path}/exclude'); - final existing = - excludeFile.existsSync() ? excludeFile.readAsStringSync() : ''; + final existing = excludeFile.existsSync() + ? excludeFile.readAsStringSync() + : ''; final present = existing.split('\n').any((line) { final trimmed = line.trim(); return trimmed == _excludeRule || @@ -174,10 +173,12 @@ class CodePushArchiveService { String? _findGitDir() { try { - final result = Process.runSync( - 'git', - ['-C', _projectDir.path, 'rev-parse', '--git-dir'], - ); + final result = Process.runSync('git', [ + '-C', + _projectDir.path, + 'rev-parse', + '--git-dir', + ]); if (result.exitCode != 0) return null; final raw = (result.stdout as String).trim(); if (raw.isEmpty) return null; diff --git a/lib/src/shared/codepush_client.dart b/lib/src/shared/codepush_client.dart index 5598d59..4d2e24e 100644 --- a/lib/src/shared/codepush_client.dart +++ b/lib/src/shared/codepush_client.dart @@ -14,8 +14,8 @@ import 'package:flutter_compile/src/shared/functions.dart'; /// even if a rogue CA issues a certificate for the server domain. class CodePushClient { CodePushClient({String? serverUrl, String? pinnedCertificatePath}) - : _serverUrl = serverUrl ?? Constants.codePushDefaultServer, - _http = _createHttpClient(pinnedCertificatePath); + : _serverUrl = serverUrl ?? Constants.codePushDefaultServer, + _http = _createHttpClient(pinnedCertificatePath); final String _serverUrl; final HttpClient _http; @@ -149,10 +149,10 @@ class CodePushClient { required String email, String? name, }) async { - return _post('/api/v1/auth/register', body: { - 'email': email, - if (name != null) 'name': name, - }); + return _post( + '/api/v1/auth/register', + body: {'email': email, if (name != null) 'name': name}, + ); } /// GET /api/v1/account — get user profile and subscription status. @@ -307,10 +307,7 @@ class CodePushClient { return _patch( '/api/v1/apps', token: token, - body: { - 'app_id': appId, - 'public_key': publicKeyPem, - }, + body: {'app_id': appId, 'public_key': publicKeyPem}, ); } @@ -332,8 +329,10 @@ class CodePushClient { required String releaseId, }) async { // First get the release info to find the snapshot URL. - final info = - await _get('/api/v1/releases?release_id=$releaseId', token: token); + final info = await _get( + '/api/v1/releases?release_id=$releaseId', + token: token, + ); final releases = info['releases'] as List?; if (releases == null || releases.isEmpty) return null; @@ -426,13 +425,16 @@ class CodePushClient { try { // 1. Generate random AES-256 key (32 bytes) and IV (16 bytes). final random = Random.secure(); - final aesKey = - Uint8List.fromList(List.generate(32, (_) => random.nextInt(256))); - final iv = - Uint8List.fromList(List.generate(16, (_) => random.nextInt(256))); + final aesKey = Uint8List.fromList( + List.generate(32, (_) => random.nextInt(256)), + ); + final iv = Uint8List.fromList( + List.generate(16, (_) => random.nextInt(256)), + ); - final aesKeyHex = - aesKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); + final aesKeyHex = aesKey + .map((b) => b.toRadixString(16).padLeft(2, '0')) + .join(); final ivHex = iv.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); // 2. Write source to temp file and encrypt with AES-256-CBC. @@ -520,10 +522,7 @@ class CodePushClient { // --- HTTP helpers --- - Future> _get( - String path, { - String? token, - }) async { + Future> _get(String path, {String? token}) async { final uri = Uri.parse('$_serverUrl$path'); final request = await _http.getUrl(uri); if (token != null) { @@ -604,7 +603,8 @@ class CodePushClient { } Future> _parseResponse( - HttpClientResponse response) async { + HttpClientResponse response, + ) async { final body = await response.transform(utf8.decoder).join(); final statusCode = response.statusCode; if (body.isEmpty) { diff --git a/lib/src/shared/constants.dart b/lib/src/shared/constants.dart index 33f0e8f..b1a5cf7 100644 --- a/lib/src/shared/constants.dart +++ b/lib/src/shared/constants.dart @@ -26,7 +26,7 @@ $env:PATH = "{{path}}\cache\dart-sdk\bin;$env:PATH" '''; -// Engine Constants + // Engine Constants // Engine lives inside the Flutter contributor checkout static const engineInstallPath = '$flutterCompileInstallPath/engine'; static const depotToolsInstallPath = '$baseCliPath/depot_tools'; @@ -62,7 +62,7 @@ solutions = [ static const engineUpstreamSSH = 'git@github.com:flutter/flutter.git'; static const engineUpstreamHTTPS = 'https://github.com/flutter/flutter.git'; -// DevTools Constants + // DevTools Constants static const devToolsInstallPath = '$baseCliPath/devtools'; static const devToolsPATHExport = r''' @@ -289,15 +289,11 @@ enum RunCommandKey { flutterCompile('flutter_path'), // key for the path to the flutter compile devTools('devtools_path'), // key for the path to the devtools engine('engine_path'), // key for the path to the engine - depotTools('depot_tools_path'), // key for the path to depot_tools - + depotTools('depot_tools_path') // key for the path to depot_tools ; final String key; const RunCommandKey(this.key); } -enum FlutterMode { - normal, - compiled, -} +enum FlutterMode { normal, compiled } diff --git a/lib/src/shared/functions.dart b/lib/src/shared/functions.dart index 653305a..ddc38b9 100644 --- a/lib/src/shared/functions.dart +++ b/lib/src/shared/functions.dart @@ -55,8 +55,10 @@ class F { } if (await rcConfigFile.exists()) { - final persistedPath = - await readValueForKeyFromRcConfig(rcConfigFile, key.key); + final persistedPath = await readValueForKeyFromRcConfig( + rcConfigFile, + key.key, + ); if (persistedPath != null) { return persistedPath; } @@ -188,8 +190,9 @@ class F { if (extractedBlocks.isEmpty) { // No legacy blocks found — just ensure source line. await ensureSourceLineInShellRc(); - final hadSourceLine = - Constants.platformSourceLinePattern.hasMatch(rcContents); + final hadSourceLine = Constants.platformSourceLinePattern.hasMatch( + rcContents, + ); return MigrateResult( blocksMoved: 0, sourceLineAdded: !hadSourceLine, @@ -219,8 +222,9 @@ class F { await envFile.writeAsString(envContents); // Strip blocks from the shell RC and add source line. - final hadSourceLine = - Constants.platformSourceLinePattern.hasMatch(rcContents); + final hadSourceLine = Constants.platformSourceLinePattern.hasMatch( + rcContents, + ); await ensureSourceLineInShellRc(); // Rewrite the SDK manager block with the new guarded template so @@ -255,8 +259,8 @@ class F { final shellConfig = shell.contains('bash') ? '.bashrc' : shell.contains('zsh') - ? '.zshrc' - : '.profile'; + ? '.zshrc' + : '.profile'; return '$home/$shellConfig'; } @@ -341,10 +345,12 @@ class F { await runCommand('brew', ['install', '--cask', 'android-platform-tools']); } else if (os == 'linux') { await runCommand('sudo', ['apt-get', 'update']); - await runCommand( - 'sudo', - ['apt-get', 'install', '-y', 'android-tools-adb'], - ); + await runCommand('sudo', [ + 'apt-get', + 'install', + '-y', + 'android-tools-adb', + ]); } else if (os == 'windows') { logger.info( 'On Windows, install Android platform tools manually or via Android Studio.', @@ -454,8 +460,9 @@ class F { .platformFlutterCompilePATHExport .replaceAll('{{path}}', flutterCompilePath); - final isUsingCompiledVersion = - _flutterCompileBlockPattern.hasMatch(contents); + final isUsingCompiledVersion = _flutterCompileBlockPattern.hasMatch( + contents, + ); if (mode == FlutterMode.compiled && !isUsingCompiledVersion) { contents += flutterCompilePATHExport; @@ -502,8 +509,9 @@ class F { /// Flutter repo so `fcp switch` doesn't fail with a broken Dart SDK download. static void _ensureDartSdkForCompiled(String compiledFlutterPath) { try { - final targetDart = - File('$compiledFlutterPath/bin/cache/dart-sdk/bin/dart'); + final targetDart = File( + '$compiledFlutterPath/bin/cache/dart-sdk/bin/dart', + ); if (targetDart.existsSync()) return; // Already has a Dart SDK. // Find the source Dart SDK. @@ -531,8 +539,9 @@ class F { final engineStamp = File('$compiledFlutterPath/bin/cache/engine.stamp'); if (engineStamp.existsSync()) { final hash = engineStamp.readAsStringSync().trim(); - File('$compiledFlutterPath/bin/cache/engine-dart-sdk.stamp') - .writeAsStringSync(hash); + File( + '$compiledFlutterPath/bin/cache/engine-dart-sdk.stamp', + ).writeAsStringSync(hash); } } catch (_) { // Best effort — don't break switch if this fails. @@ -569,10 +578,7 @@ class F { } } try { - await runCommand( - 'git', - ['clone', url, directory], - ); + await runCommand('git', ['clone', url, directory]); } catch (e) { // Clean up partial clone on failure if (dir.existsSync()) { @@ -594,8 +600,9 @@ class F { for (var line in lines) { final colonIndex = line.indexOf(':'); if (colonIndex != -1) { - keyValuePairs[line.substring(0, colonIndex)] = - line.substring(colonIndex + 1); + keyValuePairs[line.substring(0, colonIndex)] = line.substring( + colonIndex + 1, + ); } } } @@ -630,8 +637,9 @@ class F { static String sdkPubCachePath(String sdkPath) => '$sdkPath/.pub-cache'; - static Map sdkEnvironment(String sdkPath) => - {'PUB_CACHE': sdkPubCachePath(sdkPath)}; + static Map sdkEnvironment(String sdkPath) => { + 'PUB_CACHE': sdkPubCachePath(sdkPath), + }; static String sdkVersionPath(String version) { final home = homeDir(); diff --git a/lib/src/tui/tui_app.dart b/lib/src/tui/tui_app.dart index 9a151e4..4982c12 100644 --- a/lib/src/tui/tui_app.dart +++ b/lib/src/tui/tui_app.dart @@ -182,8 +182,10 @@ class TuiApp { await F.cloneRepository(Constants.flutterGitUrl, sdkPath); // Checkout the specific version - await Process.run('git', ['checkout', version], - workingDirectory: sdkPath); + await Process.run('git', [ + 'checkout', + version, + ], workingDirectory: sdkPath); _state.statusMessage = 'SDK "$version" installed.'; _state.sdkList = await gatherSdkList(); diff --git a/lib/src/tui/tui_renderer.dart b/lib/src/tui/tui_renderer.dart index 453d0c4..87ee7ce 100644 --- a/lib/src/tui/tui_renderer.dart +++ b/lib/src/tui/tui_renderer.dart @@ -58,11 +58,7 @@ class TuiRenderer { buf.writeln(parts.join(' ')); } - static void _renderContent( - StringBuffer buf, - TuiState state, - int width, - ) { + static void _renderContent(StringBuffer buf, TuiState state, int width) { if (state.loading) { buf.writeln(' Loading...'); return; @@ -119,8 +115,8 @@ class TuiRenderer { final icon = status == 'ok' ? '+' : status == 'not_configured' - ? '-' - : 'X'; + ? '-' + : 'X'; buf.writeln(' [$icon] $name: $status'); } } @@ -157,8 +153,8 @@ class TuiRenderer { final icon = status == 'ok' ? '+' : status == 'not_found' || status == 'not_configured' - ? '-' - : 'X'; + ? '-' + : 'X'; String displayName; if (category == 'engine_tools' && name == 'gclient') { @@ -187,9 +183,7 @@ class TuiRenderer { } if (state.currentTab == 0) { - buf.write( - ' [Enter] Set global [i] Install [r] Refresh [q] Quit', - ); + buf.write(' [Enter] Set global [i] Install [r] Refresh [q] Quit'); } else if (state.currentTab == 3) { buf.write(' [r] Refresh [q] Quit'); } else { diff --git a/test/ensure_build_test.dart b/test/ensure_build_test.dart index 17ddd58..e390cb4 100644 --- a/test/ensure_build_test.dart +++ b/test/ensure_build_test.dart @@ -6,14 +6,17 @@ import 'package:test/test.dart'; void main() { test('packageVersion matches pubspec.yaml version', () { final pubspec = File('pubspec.yaml').readAsStringSync(); - final match = - RegExp(r'^version:\s*(\S+)', multiLine: true).firstMatch(pubspec); + final match = RegExp( + r'^version:\s*(\S+)', + multiLine: true, + ).firstMatch(pubspec); expect(match, isNotNull, reason: 'pubspec.yaml is missing a version: line'); final pubspecVersion = match!.group(1); expect( packageVersion, pubspecVersion, - reason: 'lib/src/version.dart is out of sync with pubspec.yaml. ' + reason: + 'lib/src/version.dart is out of sync with pubspec.yaml. ' 'Bump both together when cutting a release.', ); }); diff --git a/test/helpers/test_helpers.dart b/test/helpers/test_helpers.dart index 8762701..3fa4e89 100644 --- a/test/helpers/test_helpers.dart +++ b/test/helpers/test_helpers.dart @@ -14,21 +14,19 @@ class MockProgress extends Mock implements Progress {} Logger logger, PubUpdater pubUpdater, FlutterCompileCommandRunner commandRunner, -}) createTestCommandRunner() { +}) +createTestCommandRunner() { final logger = MockLogger(); final pubUpdater = MockPubUpdater(); - when(() => pubUpdater.getLatestVersion(any())) - .thenAnswer((_) async => packageVersion); + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => packageVersion); final commandRunner = FlutterCompileCommandRunner( logger: logger, pubUpdater: pubUpdater, ); - return ( - logger: logger, - pubUpdater: pubUpdater, - commandRunner: commandRunner, - ); + return (logger: logger, pubUpdater: pubUpdater, commandRunner: commandRunner); } diff --git a/test/src/command_runner_test.dart b/test/src/command_runner_test.dart index 0936e76..d1b4894 100644 --- a/test/src/command_runner_test.dart +++ b/test/src/command_runner_test.dart @@ -18,7 +18,8 @@ import '../helpers/test_helpers.dart'; // mismatch triggered the banner — including `local > pub.dev`). const latestVersion = '9999.0.0'; -final updatePrompt = ''' +final updatePrompt = + ''' ${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} Run ${lightCyan.wrap('$executableName update')} to update'''; @@ -45,19 +46,16 @@ void main() { verify(() => logger.info(updatePrompt)).called(1); }); - test( - 'Does not show update message when the shell calls the ' - 'completion command', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - - final result = await commandRunner.run(['completion']); - expect(result, equals(ExitCode.success.code)); - verifyNever(() => logger.info(updatePrompt)); - }, - ); + test('Does not show update message when the shell calls the ' + 'completion command', () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + + final result = await commandRunner.run(['completion']); + expect(result, equals(ExitCode.success.code)); + verifyNever(() => logger.info(updatePrompt)); + }); test('does not show update message when using update command', () async { when( @@ -91,12 +89,14 @@ void main() { verifyNever(() => logger.info(updatePrompt)); }); - test('can be instantiated without an explicit analytics/logger instance', - () { - final commandRunner = FlutterCompileCommandRunner(); - expect(commandRunner, isNotNull); - expect(commandRunner, isA>()); - }); + test( + 'can be instantiated without an explicit analytics/logger instance', + () { + final commandRunner = FlutterCompileCommandRunner(); + expect(commandRunner, isNotNull); + expect(commandRunner, isA>()); + }, + ); test('handles FormatException', () async { const exception = FormatException('oops!'); diff --git a/test/src/commands/config_command_test.dart b/test/src/commands/config_command_test.dart index 1354cc1..d07fe6d 100644 --- a/test/src/commands/config_command_test.dart +++ b/test/src/commands/config_command_test.dart @@ -55,9 +55,12 @@ void main() { tearDown(tempHome.tearDown); test('config set writes value and config get reads it back', () async { - var result = await commandRunner.run( - ['config', 'set', 'engine', '/tmp/engine'], - ); + var result = await commandRunner.run([ + 'config', + 'set', + 'engine', + '/tmp/engine', + ]); expect(result, equals(ExitCode.success.code)); verify(() => logger.info('Set engine_path:/tmp/engine')).called(1); @@ -87,9 +90,7 @@ void main() { test('config list --json outputs JSON', () async { await commandRunner.run(['config', 'set', 'flutter', '/tmp/flutter']); await commandRunner.run(['config', 'list', '--json']); - verify( - () => logger.info('{"flutter_path":"/tmp/flutter"}'), - ).called(1); + verify(() => logger.info('{"flutter_path":"/tmp/flutter"}')).called(1); }); test('config set with no args returns usage', () async { diff --git a/test/src/commands/sdk_command_test.dart b/test/src/commands/sdk_command_test.dart index 4e0e9b1..f26f620 100644 --- a/test/src/commands/sdk_command_test.dart +++ b/test/src/commands/sdk_command_test.dart @@ -74,9 +74,7 @@ void main() { test('sdk remove with no version arg returns usage exit code', () async { final result = await commandRunner.run(['sdk', 'remove']); expect(result, equals(ExitCode.usage.code)); - verify( - () => logger.err('Please specify a version to remove.'), - ).called(1); + verify(() => logger.err('Please specify a version to remove.')).called(1); }); }); } diff --git a/test/src/commands/sdk_exec_command_test.dart b/test/src/commands/sdk_exec_command_test.dart index fb40349..1e395cd 100644 --- a/test/src/commands/sdk_exec_command_test.dart +++ b/test/src/commands/sdk_exec_command_test.dart @@ -29,9 +29,7 @@ void main() { test('no args returns usage exit code', () async { final result = await commandRunner.run(['sdk', 'exec']); expect(result, equals(ExitCode.usage.code)); - verify( - () => logger.err('Please specify a command to run.'), - ).called(1); + verify(() => logger.err('Please specify a command to run.')).called(1); }); test('no SDK configured returns usage exit code', () async { diff --git a/test/src/commands/sdk_global_command_test.dart b/test/src/commands/sdk_global_command_test.dart index 165c713..8141b47 100644 --- a/test/src/commands/sdk_global_command_test.dart +++ b/test/src/commands/sdk_global_command_test.dart @@ -33,8 +33,11 @@ void main() { }); test('non-installed version returns usage exit code', () async { - final result = - await commandRunner.run(['sdk', 'global', 'non_existent_version']); + final result = await commandRunner.run([ + 'sdk', + 'global', + 'non_existent_version', + ]); expect(result, equals(ExitCode.usage.code)); verify( () => logger.err( diff --git a/test/src/commands/sdk_install_test.dart b/test/src/commands/sdk_install_test.dart index d4cd04e..62b9946 100644 --- a/test/src/commands/sdk_install_test.dart +++ b/test/src/commands/sdk_install_test.dart @@ -43,45 +43,51 @@ void main() { expect(F.isSdkInstalled('3.19.0'), isTrue); }); - test('cloneRepository skips when valid git repo exists and force=false', - () async { - final dir = Directory('${tempHome.path}/repo'); - Directory('${dir.path}/.git').createSync(recursive: true); - File('${dir.path}/.git/HEAD').writeAsStringSync('ref: refs/heads/main\n'); + test( + 'cloneRepository skips when valid git repo exists and force=false', + () async { + final dir = Directory('${tempHome.path}/repo'); + Directory('${dir.path}/.git').createSync(recursive: true); + File( + '${dir.path}/.git/HEAD', + ).writeAsStringSync('ref: refs/heads/main\n'); - await F.cloneRepository('https://example.com/repo.git', dir.path); - // Should have skipped — verify the info message - verify( - () => logger.info( - 'Directory ${dir.path} already exists. Skipping clone.', - ), - ).called(1); - }); + await F.cloneRepository('https://example.com/repo.git', dir.path); + // Should have skipped — verify the info message + verify( + () => logger.info( + 'Directory ${dir.path} already exists. Skipping clone.', + ), + ).called(1); + }, + ); - test('cloneRepository cleans up invalid repo directory and re-clones', - () async { - final dir = Directory('${tempHome.path}/repo'); - dir.createSync(); - // No .git/HEAD — invalid repo + test( + 'cloneRepository cleans up invalid repo directory and re-clones', + () async { + final dir = Directory('${tempHome.path}/repo'); + dir.createSync(); + // No .git/HEAD — invalid repo - // The actual clone will fail because the URL is fake, - // but we verify the cleanup-and-reclone path was taken - try { - await F.cloneRepository( - 'https://invalid.example.com/repo.git', - dir.path, - ); - } catch (_) { - // Expected: git clone fails for invalid URL - } + // The actual clone will fail because the URL is fake, + // but we verify the cleanup-and-reclone path was taken + try { + await F.cloneRepository( + 'https://invalid.example.com/repo.git', + dir.path, + ); + } catch (_) { + // Expected: git clone fails for invalid URL + } - // The invalid directory should have been cleaned up by the catch block - verify( - () => logger.info( - 'Directory ${dir.path} exists but is not a valid git repo. ' - 'Cleaning up and re-cloning...', - ), - ).called(1); - }); + // The invalid directory should have been cleaned up by the catch block + verify( + () => logger.info( + 'Directory ${dir.path} exists but is not a valid git repo. ' + 'Cleaning up and re-cloning...', + ), + ).called(1); + }, + ); }); } diff --git a/test/src/commands/sdk_use_command_test.dart b/test/src/commands/sdk_use_command_test.dart index 7b89e64..f7161af 100644 --- a/test/src/commands/sdk_use_command_test.dart +++ b/test/src/commands/sdk_use_command_test.dart @@ -33,8 +33,11 @@ void main() { }); test('non-installed version returns usage exit code', () async { - final result = - await commandRunner.run(['sdk', 'use', 'non_existent_version']); + final result = await commandRunner.run([ + 'sdk', + 'use', + 'non_existent_version', + ]); expect(result, equals(ExitCode.usage.code)); verify( () => logger.err( diff --git a/test/src/commands/status_command_test.dart b/test/src/commands/status_command_test.dart index f82e2d3..f54eb60 100644 --- a/test/src/commands/status_command_test.dart +++ b/test/src/commands/status_command_test.dart @@ -79,9 +79,7 @@ void main() { final result = await commandRunner.run(['status']); expect(result, equals(ExitCode.success.code)); verify(() => logger.info('Engine path: $enginePath')).called(1); - verify( - () => logger.info('Available builds: (none)'), - ).called(1); + verify(() => logger.info('Available builds: (none)')).called(1); }); }); } diff --git a/test/src/commands/update_command_test.dart b/test/src/commands/update_command_test.dart index e320504..2309054 100644 --- a/test/src/commands/update_command_test.dart +++ b/test/src/commands/update_command_test.dart @@ -52,49 +52,43 @@ void main() { expect(command, isNotNull); }); - test( - 'handles pub latest version query errors', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }, - ); - - test( - 'handles pub update errors', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verify( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).called(1); - }, - ); + test('handles pub latest version query errors', () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenThrow(Exception('oops')); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.software.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.err('Exception: oops')); + verifyNever( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ); + }); + + test('handles pub update errors', () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + when( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).thenThrow(Exception('oops')); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.software.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.err('Exception: oops')); + verify( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).called(1); + }); test('handles pub update process errors', () async { const error = 'Oh no! Installing this is not possible right now!'; @@ -123,54 +117,48 @@ void main() { ).called(1); }); - test( - 'updates when newer version exists', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenAnswer( - (_) async => ProcessResult(0, ExitCode.success.code, null, null), - ); - when(() => logger.progress(any())).thenReturn(MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.progress('Updating to $latestVersion')).called(1); - verify( - () => pubUpdater.update( - packageName: packageName, - versionConstraint: latestVersion, - ), - ).called(1); - }, - ); - - test( - 'does not update when already on latest version', - () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); - when(() => logger.progress(any())).thenReturn(MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify( - () => logger.info('CLI is already at the latest version.'), - ).called(1); - verifyNever(() => logger.progress('Updating to $latestVersion')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }, - ); + test('updates when newer version exists', () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + when( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).thenAnswer( + (_) async => ProcessResult(0, ExitCode.success.code, null, null), + ); + when(() => logger.progress(any())).thenReturn(MockProgress()); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.success.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.progress('Updating to $latestVersion')).called(1); + verify( + () => pubUpdater.update( + packageName: packageName, + versionConstraint: latestVersion, + ), + ).called(1); + }); + + test('does not update when already on latest version', () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => packageVersion); + when(() => logger.progress(any())).thenReturn(MockProgress()); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.success.code)); + verify( + () => logger.info('CLI is already at the latest version.'), + ).called(1); + verifyNever(() => logger.progress('Updating to $latestVersion')); + verifyNever( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ); + }); }); } diff --git a/test/src/daemon/daemon_peer_test.dart b/test/src/daemon/daemon_peer_test.dart index 42050ce..3964616 100644 --- a/test/src/daemon/daemon_peer_test.dart +++ b/test/src/daemon/daemon_peer_test.dart @@ -28,10 +28,8 @@ void main() { /// Returns (clientChannel, daemonChannel) where: /// - clientChannel is used by the test to send/receive JSON-RPC /// - daemonChannel is used by the DaemonPeer - ({ - StreamChannel client, - StreamChannel daemon, - }) createChannelPair() { + ({StreamChannel client, StreamChannel daemon}) + createChannelPair() { final clientToServer = StreamController(); final serverToClient = StreamController(); @@ -50,21 +48,14 @@ void main() { test('version returns current package version', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); // Start peer in background unawaited(peer.start()); // Skip the daemon.connected notification // Read until we get a proper response - final request = { - 'jsonrpc': '2.0', - 'method': 'version', - 'id': 1, - }; + final request = {'jsonrpc': '2.0', 'method': 'version', 'id': 1}; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -76,28 +67,17 @@ void main() { } // Shutdown - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); test('sdk.list returns list shape', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); unawaited(peer.start()); - final request = { - 'jsonrpc': '2.0', - 'method': 'sdk.list', - 'id': 1, - }; + final request = {'jsonrpc': '2.0', 'method': 'sdk.list', 'id': 1}; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -108,28 +88,17 @@ void main() { } } - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); test('sdk.global.get returns null with no config', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); unawaited(peer.start()); - final request = { - 'jsonrpc': '2.0', - 'method': 'sdk.global.get', - 'id': 1, - }; + final request = {'jsonrpc': '2.0', 'method': 'sdk.global.get', 'id': 1}; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -140,28 +109,17 @@ void main() { } } - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); test('config.list returns empty map with no config', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); unawaited(peer.start()); - final request = { - 'jsonrpc': '2.0', - 'method': 'config.list', - 'id': 1, - }; + final request = {'jsonrpc': '2.0', 'method': 'config.list', 'id': 1}; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -173,20 +131,13 @@ void main() { } } - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); test('config.set writes to rc file', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); unawaited(peer.start()); @@ -209,28 +160,17 @@ void main() { } } - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); test('shutdown closes peer', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); final peerFuture = peer.start(); - final request = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 1, - }; + final request = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 1}; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -247,10 +187,7 @@ void main() { test('invalid method returns JSON-RPC error', () async { final channels = createChannelPair(); - final peer = DaemonPeer( - logger: logger, - channel: channels.daemon, - ); + final peer = DaemonPeer(logger: logger, channel: channels.daemon); unawaited(peer.start()); @@ -269,11 +206,7 @@ void main() { } } - final shutdownReq = { - 'jsonrpc': '2.0', - 'method': 'shutdown', - 'id': 2, - }; + final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; channels.client.sink.add(json.encode(shutdownReq)); }); }); diff --git a/test/src/daemon/file_watcher_test.dart b/test/src/daemon/file_watcher_test.dart index d90a07f..1d08369 100644 --- a/test/src/daemon/file_watcher_test.dart +++ b/test/src/daemon/file_watcher_test.dart @@ -34,8 +34,9 @@ void main() { watchedFile.writeAsStringSync('changed'); - final result = - await completer.future.timeout(const Duration(seconds: 15)); + final result = await completer.future.timeout( + const Duration(seconds: 15), + ); expect(result, equals(watchedFile.path)); watcher.stop(); diff --git a/test/src/shared/codepush_build_service_test.dart b/test/src/shared/codepush_build_service_test.dart index 45c803f..4dec4e2 100644 --- a/test/src/shared/codepush_build_service_test.dart +++ b/test/src/shared/codepush_build_service_test.dart @@ -45,8 +45,10 @@ void main() { }); group('validatePayloadMagic', () { - List mk(List header, [int tailLen = 32]) => - [...header, ...List.filled(tailLen, 0x00)]; + List mk(List header, [int tailLen = 32]) => [ + ...header, + ...List.filled(tailLen, 0x00), + ]; const iosHeader = [0x33, 0x43, 0x42, 0x44]; const macHeaderLe = [0xCF, 0xFA, 0xED, 0xFE]; @@ -101,10 +103,10 @@ void main() { }); test('rejects short payloads', () { - final err = CodePushBuildService.validatePayloadMagic( - [0x90, 0xAB], - 'ios', - ); + final err = CodePushBuildService.validatePayloadMagic([ + 0x90, + 0xAB, + ], 'ios'); expect(err, isNotNull); expect(err, contains('too small')); }); @@ -112,7 +114,8 @@ void main() { group('parseFlutterVersionOutput', () { test('extracts stable channel version', () { - const output = 'Flutter 3.41.2 • channel stable • https://github.com/' + const output = + 'Flutter 3.41.2 • channel stable • https://github.com/' 'flutter/flutter.git\n' 'Framework • revision abc123 (3 days ago)\n'; expect( @@ -134,31 +137,28 @@ void main() { }); test('returns null when first line does not start with Flutter', () { - const output = 'Downloading Flutter SDK...\n' + const output = + 'Downloading Flutter SDK...\n' 'Flutter 3.41.2 • channel stable\n'; - expect( - CodePushBuildService.parseFlutterVersionOutput(output), - isNull, - ); + expect(CodePushBuildService.parseFlutterVersionOutput(output), isNull); }); test('tolerates trailing whitespace on the first line', () { const output = 'Flutter 3.5.0 \n'; - expect( - CodePushBuildService.parseFlutterVersionOutput(output), - '3.5.0', - ); + expect(CodePushBuildService.parseFlutterVersionOutput(output), '3.5.0'); }); }); group('resolveFlutterVersion', () { - test('explicit value short-circuits detection and stored config', - () async { - final resolved = await service.resolveFlutterVersion( - explicit: '3.29.1', - ); - expect(resolved, '3.29.1'); - }); + test( + 'explicit value short-circuits detection and stored config', + () async { + final resolved = await service.resolveFlutterVersion( + explicit: '3.29.1', + ); + expect(resolved, '3.29.1'); + }, + ); test('empty explicit is treated as absent and falls through', () async { // We cannot assert the downstream result without mocking @@ -171,21 +171,19 @@ void main() { group('finalizeBuild', () { test( - 'ios is a no-op because the patched engine must be present at build time', - () async { - final result = await service.finalizeBuild( - buildPlatform: 'ios', - flutterVersion: '3.41.2', - artifactManager: CodePushArtifactManager(logger: logger), - ); + 'ios is a no-op because the patched engine must be present at build time', + () async { + final result = await service.finalizeBuild( + buildPlatform: 'ios', + flutterVersion: '3.41.2', + artifactManager: CodePushArtifactManager(logger: logger), + ); - expect(result.success, isTrue); - expect( - result.message, - 'iOS build already finalized during prepare.', - ); - expect(result.command, isNull); - }); + expect(result.success, isTrue); + expect(result.message, 'iOS build already finalized during prepare.'); + expect(result.command, isNull); + }, + ); }); }); diff --git a/test/src/shared/constants_test.dart b/test/src/shared/constants_test.dart index 10eb254..a547df3 100644 --- a/test/src/shared/constants_test.dart +++ b/test/src/shared/constants_test.dart @@ -103,10 +103,7 @@ void main() { Constants.platformSdkPATHExport, equals(Constants.sdkPATHExport), ); - expect( - Constants.platformRestartShell, - equals(Constants.restartShell), - ); + expect(Constants.platformRestartShell, equals(Constants.restartShell)); } }); }); @@ -120,8 +117,10 @@ void main() { }); test('FlutterMode has normal and compiled', () { - expect(FlutterMode.values, - containsAll([FlutterMode.normal, FlutterMode.compiled])); + expect( + FlutterMode.values, + containsAll([FlutterMode.normal, FlutterMode.compiled]), + ); }); }); diff --git a/test/src/shared/functions_test.dart b/test/src/shared/functions_test.dart index 312ce43..e762eab 100644 --- a/test/src/shared/functions_test.dart +++ b/test/src/shared/functions_test.dart @@ -76,8 +76,9 @@ void main() { test('returns true for directory with .git/HEAD', () { Directory('${tempDir.path}/.git').createSync(); - File('${tempDir.path}/.git/HEAD') - .writeAsStringSync('ref: refs/heads/main\n'); + File( + '${tempDir.path}/.git/HEAD', + ).writeAsStringSync('ref: refs/heads/main\n'); expect(F.isValidGitRepo(tempDir.path), isTrue); }); }); diff --git a/test/src/shared/ios_baseline_plist_test.dart b/test/src/shared/ios_baseline_plist_test.dart index 2389554..5cbc4a1 100644 --- a/test/src/shared/ios_baseline_plist_test.dart +++ b/test/src/shared/ios_baseline_plist_test.dart @@ -9,9 +9,11 @@ void main() { final baselineId = generateBaselineId(); expect( baselineId, - matches(RegExp( - r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', - )), + matches( + RegExp( + r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', + ), + ), ); }); @@ -45,9 +47,10 @@ void main() { ); }); - test('inserts a new FCPBaselineId key and can restore original content', - () { - const originalContent = ''' + test( + 'inserts a new FCPBaselineId key and can restore original content', + () { + const originalContent = ''' @@ -56,24 +59,25 @@ void main() { '''; - File(plistPath).writeAsStringSync(originalContent); + File(plistPath).writeAsStringSync(originalContent); - final original = writeBaselineIdToIosInfoPlist( - '12345678-1234-4234-8234-123456789abc', - plistPath: plistPath, - ); + final original = writeBaselineIdToIosInfoPlist( + '12345678-1234-4234-8234-123456789abc', + plistPath: plistPath, + ); - expect(original, originalContent); - final updated = File(plistPath).readAsStringSync(); - expect(updated, contains('FCPBaselineId')); - expect( - updated, - contains('12345678-1234-4234-8234-123456789abc'), - ); + expect(original, originalContent); + final updated = File(plistPath).readAsStringSync(); + expect(updated, contains('FCPBaselineId')); + expect( + updated, + contains('12345678-1234-4234-8234-123456789abc'), + ); - restoreIosInfoPlist(original!, plistPath: plistPath); - expect(File(plistPath).readAsStringSync(), originalContent); - }); + restoreIosInfoPlist(original!, plistPath: plistPath); + expect(File(plistPath).readAsStringSync(), originalContent); + }, + ); test('replaces an existing FCPBaselineId value', () { const originalContent = ''' diff --git a/test/src/shared/ios_patch_entry_target_test.dart b/test/src/shared/ios_patch_entry_target_test.dart index 371882e..ba07e46 100644 --- a/test/src/shared/ios_patch_entry_target_test.dart +++ b/test/src/shared/ios_patch_entry_target_test.dart @@ -47,23 +47,25 @@ Object? codePushPatch() => 1; ); }); - test('findCodePushPatchSourceCandidates ignores invocation-only wrappers', - () { - File('lib/code_push_local_patch.dart').writeAsStringSync(''' + test( + 'findCodePushPatchSourceCandidates ignores invocation-only wrappers', + () { + File('lib/code_push_local_patch.dart').writeAsStringSync(''' import 'screens/home_screen.dart'; @pragma('dyn-module:entry-point') Object? main() => codePushPatch(); '''); - File('lib/screens/home_screen.dart').writeAsStringSync(''' + File('lib/screens/home_screen.dart').writeAsStringSync(''' Object? codePushPatch() => 1; '''); - expect( - findCodePushPatchSourceCandidates(), - equals(['lib/screens/home_screen.dart']), - ); - }); + expect( + findCodePushPatchSourceCandidates(), + equals(['lib/screens/home_screen.dart']), + ); + }, + ); test('findCodePushPatchSourceCandidates returns sorted matches', () { File('lib/b.dart').writeAsStringSync('Object? codePushPatch() => 1;'); From 0fa4305a65638db282dcd440d9852d80da9a6001 Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Fri, 12 Jun 2026 00:39:23 -0400 Subject: [PATCH 6/7] Revert "style: format with Dart 3.12.2 to match CI" This reverts commit 6a486d181f4dfd74725a5bfeee1577ef9af05812. --- bin/flutter_compile.dart | 6 +- example/lib/main.dart | 5 +- lib/src/command_runner.dart | 24 ++- .../build_commands/_engine_build.dart | 11 +- lib/src/commands/clean_command.dart | 15 +- .../codepush_commands/_codepush_account.dart | 35 ++-- .../codepush_commands/_codepush_apps.dart | 15 +- .../codepush_commands/_codepush_init.dart | 71 +++----- .../codepush_commands/_codepush_keys.dart | 18 +- .../codepush_commands/_codepush_release.dart | 50 +++-- .../codepush_commands/_codepush_rollback.dart | 5 +- .../codepush_commands/_codepush_setup.dart | 24 +-- .../codepush_commands/_codepush_status.dart | 97 +++++----- .../codepush_commands/_codepush_versions.dart | 34 ++-- lib/src/commands/config_command.dart | 12 +- lib/src/commands/doctor_command.dart | 36 ++-- lib/src/commands/flutter_switch_command.dart | 5 +- .../commands/install_commands/_devtools.dart | 68 +++---- .../commands/install_commands/_engine.dart | 29 ++- .../commands/install_commands/_flutter.dart | 49 +++-- lib/src/commands/run_command.dart | 18 +- lib/src/commands/sdk_commands/_sdk_exec.dart | 3 +- .../commands/sdk_commands/_sdk_global.dart | 4 +- .../commands/sdk_commands/_sdk_install.dart | 18 +- lib/src/commands/sdk_commands/_sdk_list.dart | 32 ++-- .../commands/sdk_commands/_sdk_remove.dart | 3 +- lib/src/commands/sdk_commands/_sdk_use.dart | 9 +- lib/src/commands/status_command.dart | 10 +- .../sync_commands/_sync_devtools.dart | 18 +- .../commands/sync_commands/_sync_engine.dart | 18 +- .../commands/sync_commands/_sync_flutter.dart | 26 +-- .../commands/sync_commands/sync_command.dart | 12 +- lib/src/commands/test_command.dart | 18 +- lib/src/commands/uninstall_command.dart | 39 ++-- lib/src/commands/update_command.dart | 8 +- lib/src/daemon/daemon_peer.dart | 2 +- lib/src/shared/codepush_archive_service.dart | 35 ++-- lib/src/shared/codepush_client.dart | 46 ++--- lib/src/shared/constants.dart | 12 +- lib/src/shared/functions.dart | 60 +++--- lib/src/tui/tui_app.dart | 6 +- lib/src/tui/tui_renderer.dart | 18 +- test/ensure_build_test.dart | 9 +- test/helpers/test_helpers.dart | 14 +- test/src/command_runner_test.dart | 40 ++-- test/src/commands/config_command_test.dart | 13 +- test/src/commands/sdk_command_test.dart | 4 +- test/src/commands/sdk_exec_command_test.dart | 4 +- .../src/commands/sdk_global_command_test.dart | 7 +- test/src/commands/sdk_install_test.dart | 78 ++++---- test/src/commands/sdk_use_command_test.dart | 7 +- test/src/commands/status_command_test.dart | 4 +- test/src/commands/update_command_test.dart | 172 ++++++++++-------- test/src/daemon/daemon_peer_test.dart | 107 +++++++++-- test/src/daemon/file_watcher_test.dart | 5 +- .../shared/codepush_build_service_test.dart | 72 ++++---- test/src/shared/constants_test.dart | 11 +- test/src/shared/functions_test.dart | 5 +- test/src/shared/ios_baseline_plist_test.dart | 46 +++-- .../shared/ios_patch_entry_target_test.dart | 20 +- 60 files changed, 846 insertions(+), 796 deletions(-) diff --git a/bin/flutter_compile.dart b/bin/flutter_compile.dart index c91be7f..d87733a 100644 --- a/bin/flutter_compile.dart +++ b/bin/flutter_compile.dart @@ -13,8 +13,6 @@ Future main(List args) async { /// exited already. This is useful to prevent Future chains from proceeding /// after you've decided to exit. Future _flushThenExit(int status) { - return Future.wait([ - stdout.close(), - stderr.close(), - ]).then((_) => exit(status)); + return Future.wait([stdout.close(), stderr.close()]) + .then((_) => exit(status)); } diff --git a/example/lib/main.dart b/example/lib/main.dart index 5623453..fb6d46b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -5,6 +5,9 @@ void main() async { final result = await Process.run('flutter_compile', ['--help']); print(result.stdout); - await Future.wait([stdout.close(), stderr.close()]); + await Future.wait([ + stdout.close(), + stderr.close(), + ]); exit(0); } diff --git a/lib/src/command_runner.dart b/lib/src/command_runner.dart index a5c085a..4cdb75a 100644 --- a/lib/src/command_runner.dart +++ b/lib/src/command_runner.dart @@ -22,14 +22,16 @@ const description = /// {@endtemplate} class FlutterCompileCommandRunner extends CompletionCommandRunner { /// {@macro flutter_compile_command_runner} - FlutterCompileCommandRunner({Logger? logger, PubUpdater? pubUpdater}) - : _logger = logger ?? Logger(), - // Wrap PubUpdater's HTTP client with a cache-busting shim so that - // back-to-back version checks (e.g. `update` then `--version`) - // can't disagree because pub.dev's CDN served one a stale edge - // node. See pub_cache_busting_client.dart and issue #17. - _pubUpdater = pubUpdater ?? PubUpdater(PubCacheBustingClient()), - super(executableName, description) { + FlutterCompileCommandRunner({ + Logger? logger, + PubUpdater? pubUpdater, + }) : _logger = logger ?? Logger(), + // Wrap PubUpdater's HTTP client with a cache-busting shim so that + // back-to-back version checks (e.g. `update` then `--version`) + // can't disagree because pub.dev's CDN served one a stale edge + // node. See pub_cache_busting_client.dart and issue #17. + _pubUpdater = pubUpdater ?? PubUpdater(PubCacheBustingClient()), + super(executableName, description) { // Add root options and flags argParser ..addFlag( @@ -165,9 +167,11 @@ class FlutterCompileCommandRunner extends CompletionCommandRunner { } _logger ..info('') - ..info(''' + ..info( + ''' ${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} -Run ${lightCyan.wrap('$executableName update')} to update'''); +Run ${lightCyan.wrap('$executableName update')} to update''', + ); } catch (_) {} } diff --git a/lib/src/commands/build_commands/_engine_build.dart b/lib/src/commands/build_commands/_engine_build.dart index 997d808..0a085bb 100644 --- a/lib/src/commands/build_commands/_engine_build.dart +++ b/lib/src/commands/build_commands/_engine_build.dart @@ -35,7 +35,11 @@ class EngineBuildSubCommand extends Command { help: 'Build with --unoptimized (faster dev builds)', defaultsTo: true, ) - ..addFlag('simulator', help: 'Build for iOS simulator', defaultsTo: false) + ..addFlag( + 'simulator', + help: 'Build for iOS simulator', + defaultsTo: false, + ) ..addFlag( 'clean', help: 'Clean output directory before building', @@ -174,9 +178,8 @@ Future buildEngine( } // Decide whether to run GN - final buildNinjaExists = await File( - '$srcDir/out/$outputDir/build.ninja', - ).exists(); + final buildNinjaExists = + await File('$srcDir/out/$outputDir/build.ninja').exists(); final runGn = shouldRunGn( forceGn: forceGn, skipGn: skipGn, diff --git a/lib/src/commands/clean_command.dart b/lib/src/commands/clean_command.dart index 852f961..9c495d2 100644 --- a/lib/src/commands/clean_command.dart +++ b/lib/src/commands/clean_command.dart @@ -106,12 +106,15 @@ class CleanCommand extends Command { Future _dirSize(String path) async { if (Platform.isWindows) { - final result = await Process.run('powershell', [ - '-Command', - '(Get-ChildItem -Recurse -File "$path" ' - '| Measure-Object -Property Length -Sum).Sum / 1MB ' - '| ForEach-Object { "{0:N1}M" -f \$_ }', - ]); + final result = await Process.run( + 'powershell', + [ + '-Command', + '(Get-ChildItem -Recurse -File "$path" ' + '| Measure-Object -Property Length -Sum).Sum / 1MB ' + '| ForEach-Object { "{0:N1}M" -f \$_ }', + ], + ); return (result.stdout as String).trim(); } final result = await Process.run('du', ['-sh', path]); diff --git a/lib/src/commands/codepush_commands/_codepush_account.dart b/lib/src/commands/codepush_commands/_codepush_account.dart index b61fcc0..739c313 100644 --- a/lib/src/commands/codepush_commands/_codepush_account.dart +++ b/lib/src/commands/codepush_commands/_codepush_account.dart @@ -6,7 +6,11 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushAccountSubCommand extends Command { CodePushAccountSubCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -51,12 +55,10 @@ class CodePushAccountSubCommand extends Command { if (asJson) { // --json contract: structured output exits 0, callers inspect the // `error` field instead of the process status. - _logger.info( - json.encode({ - 'logged_in': false, - 'error': result['error'] ?? 'Unknown', - }), - ); + _logger.info(json.encode({ + 'logged_in': false, + 'error': result['error'] ?? 'Unknown', + })); return ExitCode.success.code; } progress?.fail('Error: ${result['error'] ?? 'Unknown'}'); @@ -67,17 +69,14 @@ class CodePushAccountSubCommand extends Command { final apps = result['apps'] as List?; if (asJson) { - _logger.info( - json.encode({ - 'logged_in': true, - 'email': user?['email'], - 'name': user?['name'], - 'tier': user?['tier'], - 'has_active_subscription': - user?['has_active_subscription'] ?? false, - 'apps': apps ?? [], - }), - ); + _logger.info(json.encode({ + 'logged_in': true, + 'email': user?['email'], + 'name': user?['name'], + 'tier': user?['tier'], + 'has_active_subscription': user?['has_active_subscription'] ?? false, + 'apps': apps ?? [], + })); return ExitCode.success.code; } diff --git a/lib/src/commands/codepush_commands/_codepush_apps.dart b/lib/src/commands/codepush_commands/_codepush_apps.dart index 8dc2c01..8603ce4 100644 --- a/lib/src/commands/codepush_commands/_codepush_apps.dart +++ b/lib/src/commands/codepush_commands/_codepush_apps.dart @@ -135,21 +135,18 @@ class _AppsCreateCommand extends Command { final request = await httpClient.postUrl(uri); request.headers.set('Authorization', 'Bearer $token'); request.headers.set('Content-Type', 'application/json'); - request.write( - json.encode({ - 'name': appName, - if (platform != null) 'platform': platform, - }), - ); + request.write(json.encode({ + 'name': appName, + if (platform != null) 'platform': platform, + })); final response = await request.close(); final body = await response.transform(utf8.decoder).join(); final data = json.decode(body) as Map; if (response.statusCode == 403) { - progress.fail( - data['message'] ?? 'App limit reached. Upgrade your plan.', - ); + progress + .fail(data['message'] ?? 'App limit reached. Upgrade your plan.'); return ExitCode.software.code; } diff --git a/lib/src/commands/codepush_commands/_codepush_init.dart b/lib/src/commands/codepush_commands/_codepush_init.dart index 99ffcde..0f2a027 100644 --- a/lib/src/commands/codepush_commands/_codepush_init.dart +++ b/lib/src/commands/codepush_commands/_codepush_init.dart @@ -13,7 +13,10 @@ class CodePushInitSubCommand extends Command { 'name', help: 'App name (defaults to pubspec name or directory name).', ) - ..addOption('platform', help: 'Target platform (android, ios).'); + ..addOption( + 'platform', + help: 'Target platform (android, ios).', + ); } final Logger _logger; @@ -38,10 +41,8 @@ class CodePushInitSubCommand extends Command { final pubspec = File('pubspec.yaml'); if (pubspec.existsSync()) { final content = pubspec.readAsStringSync(); - final match = RegExp( - r'^name:\s*(.+)$', - multiLine: true, - ).firstMatch(content); + final match = + RegExp(r'^name:\s*(.+)$', multiLine: true).firstMatch(content); if (match != null) appName = match.group(1)?.trim(); } appName ??= Directory.current.path.split('/').last; @@ -67,20 +68,16 @@ class CodePushInitSubCommand extends Command { final httpClient = HttpClient(); try { - final request = await httpClient.postUrl( - Uri.parse('$serverUrl/api/v1/apps'), - ); + final request = + await httpClient.postUrl(Uri.parse('$serverUrl/api/v1/apps')); request.headers.set('Authorization', 'Bearer $token'); request.headers.set('Content-Type', 'application/json'); request.headers.set('Accept', 'application/json'); - request.write( - json.encode({ - 'name': appName, - if (platform != null) 'platform': platform, - if (publicKeyPemForCreate != null) - 'public_key': publicKeyPemForCreate, - }), - ); + request.write(json.encode({ + 'name': appName, + if (platform != null) 'platform': platform, + if (publicKeyPemForCreate != null) 'public_key': publicKeyPemForCreate, + })); final response = await request.close(); final body = await response.transform(utf8.decoder).join(); @@ -123,9 +120,8 @@ class CodePushInitSubCommand extends Command { 'verification.', ); } else { - keyProgress.fail( - 'Could not generate signing keys (openssl missing?)', - ); + keyProgress + .fail('Could not generate signing keys (openssl missing?)'); _logger.warn( 'Patches will not be signed. Install openssl and re-run ' '`fcp codepush keys generate`.', @@ -154,8 +150,7 @@ class CodePushInitSubCommand extends Command { _logger.info(' 1. Wrap your app with CodePushOverlay in main.dart:'); _logger.info(''); _logger.info( - ' import \'package:flutterplaza_code_push/flutterplaza_code_push.dart\';', - ); + ' import \'package:flutterplaza_code_push/flutterplaza_code_push.dart\';'); _logger.info(''); _logger.info(' runApp('); _logger.info(' CodePushOverlay('); @@ -228,10 +223,8 @@ class CodePushInitSubCommand extends Command { String? _readPubspecVersion() { final pubspec = File('pubspec.yaml'); if (!pubspec.existsSync()) return null; - final match = RegExp( - r'^version:\s*(.+)$', - multiLine: true, - ).firstMatch(pubspec.readAsStringSync()); + final match = RegExp(r'^version:\s*(.+)$', multiLine: true) + .firstMatch(pubspec.readAsStringSync()); return match?.group(1)?.trim(); } @@ -263,9 +256,8 @@ class CodePushInitSubCommand extends Command { final manifestContent = manifest.readAsStringSync(); // Find package name from manifest, build.gradle.kts, or build.gradle - var packageName = RegExp( - r'package="([^"]+)"', - ).firstMatch(manifestContent)?.group(1); + var packageName = + RegExp(r'package="([^"]+)"').firstMatch(manifestContent)?.group(1); if (packageName == null) { // Try build.gradle.kts for (final gradleFile in [ @@ -274,16 +266,16 @@ class CodePushInitSubCommand extends Command { ]) { if (gradleFile.existsSync()) { final gradleContent = gradleFile.readAsStringSync(); - final nsMatch = RegExp( - r'namespace\s*[=:]\s*["\x27]([^"\x27]+)["\x27]', - ).firstMatch(gradleContent); + final nsMatch = + RegExp(r'namespace\s*[=:]\s*["\x27]([^"\x27]+)["\x27]') + .firstMatch(gradleContent); if (nsMatch != null) { packageName = nsMatch.group(1); break; } - final appIdMatch = RegExp( - r'applicationId\s*[=:]\s*["\x27]([^"\x27]+)["\x27]', - ).firstMatch(gradleContent); + final appIdMatch = + RegExp(r'applicationId\s*[=:]\s*["\x27]([^"\x27]+)["\x27]') + .firstMatch(gradleContent); if (appIdMatch != null) { packageName = appIdMatch.group(1); break; @@ -339,9 +331,8 @@ class CodePushApp : FlutterApplication() { ); } else if (!manifestContent.contains('CodePushApp')) { // App has a custom Application class (possibly per-flavor). - final existingMatch = RegExp( - r'android:name="([^"]+)"', - ).firstMatch(manifestContent); + final existingMatch = + RegExp(r'android:name="([^"]+)"').firstMatch(manifestContent); final existingClass = existingMatch?.group(1); _logger.warn( 'AndroidManifest.xml already has a custom Application class: ' @@ -384,8 +375,7 @@ class CodePushApp : FlutterApplication() { final publicKeyFile = File('$home/.flutter_codepush/codepush_public.pem'); if (publicKeyFile.existsSync()) { final pem = publicKeyFile.readAsStringSync().trim(); - publicKeyBlock = - '\tFLTCodePushPublicKey\n' + publicKeyBlock = '\tFLTCodePushPublicKey\n' '\t$pem\n'; } @@ -413,8 +403,7 @@ class CodePushApp : FlutterApplication() { progress.complete('iOS configured'); _logger.info( - ' Updated: Info.plist (FLTCodePushEnabled, release version, public key)', - ); + ' Updated: Info.plist (FLTCodePushEnabled, release version, public key)'); } // ── pubspec.yaml setup ──────────────────────────────────────── diff --git a/lib/src/commands/codepush_commands/_codepush_keys.dart b/lib/src/commands/codepush_commands/_codepush_keys.dart index 7a57ee7..738ccef 100644 --- a/lib/src/commands/codepush_commands/_codepush_keys.dart +++ b/lib/src/commands/codepush_commands/_codepush_keys.dart @@ -49,7 +49,9 @@ class CodePushKeysSubCommand extends Command { ' register Upload the public key to the server for the current app', ); _logger.info(''); - _logger.info('Typical migration flow for pre-0.15.0 users:'); + _logger.info( + 'Typical migration flow for pre-0.15.0 users:', + ); _logger.info(' 1. fcp codepush keys generate'); _logger.info(' 2. fcp codepush keys register'); _logger.info( @@ -107,7 +109,9 @@ class _KeysGenerateCommand extends Command { final progress = _logger.progress('Generating RSA-2048 keypair'); final result = await buildService.generateSigningKey(outputDir); if (result == null) { - progress.fail('Key generation failed. Is openssl installed and on PATH?'); + progress.fail( + 'Key generation failed. Is openssl installed and on PATH?', + ); return ExitCode.software.code; } progress.complete('Keypair generated'); @@ -133,14 +137,12 @@ class _KeysRegisterCommand extends Command { argParser ..addOption( 'app-id', - help: - 'App ID to register the public key against. Defaults to the ' + help: 'App ID to register the public key against. Defaults to the ' 'stored codepush_app_id from ~/.flutter_compilerc.', ) ..addOption( 'public-key', - help: - 'Path to the PEM-encoded public key file. Defaults to ' + help: 'Path to the PEM-encoded public key file. Defaults to ' '~/.flutter_codepush/codepush_public.pem, which is written ' 'by `fcp codepush keys generate`.', ); @@ -200,7 +202,9 @@ class _KeysRegisterCommand extends Command { final serverUrl = await CodePushClient.getServerUrl(); final client = CodePushClient(serverUrl: serverUrl); - final progress = _logger.progress('Registering public key with $serverUrl'); + final progress = _logger.progress( + 'Registering public key with $serverUrl', + ); try { final result = await client.registerAppPublicKey( diff --git a/lib/src/commands/codepush_commands/_codepush_release.dart b/lib/src/commands/codepush_commands/_codepush_release.dart index 2863e08..3257c48 100644 --- a/lib/src/commands/codepush_commands/_codepush_release.dart +++ b/lib/src/commands/codepush_commands/_codepush_release.dart @@ -13,13 +13,19 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushReleaseSubCommand extends Command { CodePushReleaseSubCommand(this._logger) { argParser - ..addOption('app-id', help: 'The app ID to create a release for.') + ..addOption( + 'app-id', + help: 'The app ID to create a release for.', + ) ..addOption( 'version', abbr: 'v', help: 'The version string for this release (e.g., 1.0.0+1).', ) - ..addOption('snapshot', help: 'Path to a pre-built release artifact.') + ..addOption( + 'snapshot', + help: 'Path to a pre-built release artifact.', + ) ..addOption( 'platform', abbr: 'p', @@ -32,14 +38,12 @@ class CodePushReleaseSubCommand extends Command { ) ..addMultiOption( 'dart-define', - help: - 'Additional --dart-define values to forward to flutter build ' + help: 'Additional --dart-define values to forward to flutter build ' 'when --build is used. Repeat for multiple values.', ) ..addOption( 'flutter-version', - help: - 'Flutter SDK version this release was built with (e.g., 3.41.2). ' + help: 'Flutter SDK version this release was built with (e.g., 3.41.2). ' 'Auto-detected from "flutter --version" if not specified. ' 'Required for server-side patch compilation.', ); @@ -77,18 +81,15 @@ class CodePushReleaseSubCommand extends Command { final pubspec = File('pubspec.yaml'); if (pubspec.existsSync()) { final content = pubspec.readAsStringSync(); - final match = RegExp( - r'^version:\s*(.+)$', - multiLine: true, - ).firstMatch(content); + final match = + RegExp(r'^version:\s*(.+)$', multiLine: true).firstMatch(content); if (match != null) { version = match.group(1)?.trim(); } } if (version == null || version.isEmpty) { _logger.err( - 'No version specified. Use --version or add one to pubspec.yaml.', - ); + 'No version specified. Use --version or add one to pubspec.yaml.'); return ExitCode.usage.code; } _logger.detail('Using version from pubspec.yaml: $version'); @@ -159,9 +160,8 @@ class CodePushReleaseSubCommand extends Command { // not leave the app repo dirty. if (platform == 'ios') { final generatedBaselineId = generateBaselineId(); - originalIosInfoPlist = writeBaselineIdToIosInfoPlist( - generatedBaselineId, - ); + originalIosInfoPlist = + writeBaselineIdToIosInfoPlist(generatedBaselineId); if (originalIosInfoPlist == null) { _logger.warn( 'Warning: ios/Runner/Info.plist not found. ' @@ -279,9 +279,8 @@ class CodePushReleaseSubCommand extends Command { if (vResult.exitCode == 0) { try { final vJson = (vResult.stdout as String).trim(); - final match = RegExp( - r'"frameworkVersion"\s*:\s*"([^"]+)"', - ).firstMatch(vJson); + final match = + RegExp(r'"frameworkVersion"\s*:\s*"([^"]+)"').firstMatch(vJson); flutterVersion = match?.group(1); } catch (_) {} } @@ -289,9 +288,8 @@ class CodePushReleaseSubCommand extends Command { if (flutterVersion == null || flutterVersion.isEmpty) { final plainResult = Process.runSync(flutter, ['--version']); if (plainResult.exitCode == 0) { - final match = RegExp( - r'Flutter (\d+\.\d+\.\d+)', - ).firstMatch(plainResult.stdout as String); + final match = RegExp(r'Flutter (\d+\.\d+\.\d+)') + .firstMatch(plainResult.stdout as String); flutterVersion = match?.group(1); } } @@ -308,12 +306,10 @@ class CodePushReleaseSubCommand extends Command { final serverUrl = await CodePushClient.getServerUrl(); final client = CodePushClient(serverUrl: serverUrl); - final versionSuffix = flutterVersion != null - ? ' (Flutter $flutterVersion)' - : ''; - final progress = _logger.progress( - 'Creating release v$version for $appId$versionSuffix', - ); + final versionSuffix = + flutterVersion != null ? ' (Flutter $flutterVersion)' : ''; + final progress = + _logger.progress('Creating release v$version for $appId$versionSuffix'); try { final result = await client.createRelease( diff --git a/lib/src/commands/codepush_commands/_codepush_rollback.dart b/lib/src/commands/codepush_commands/_codepush_rollback.dart index 113dfe9..75bf17d 100644 --- a/lib/src/commands/codepush_commands/_codepush_rollback.dart +++ b/lib/src/commands/codepush_commands/_codepush_rollback.dart @@ -37,7 +37,10 @@ class CodePushRollbackSubCommand extends Command { final progress = _logger.progress('Rolling back patch $patchId'); try { - final result = await client.rollbackPatch(token: token, patchId: patchId); + final result = await client.rollbackPatch( + token: token, + patchId: patchId, + ); final statusCode = result['status_code'] as int; diff --git a/lib/src/commands/codepush_commands/_codepush_setup.dart b/lib/src/commands/codepush_commands/_codepush_setup.dart index 8ec94a8..bfde82c 100644 --- a/lib/src/commands/codepush_commands/_codepush_setup.dart +++ b/lib/src/commands/codepush_commands/_codepush_setup.dart @@ -9,14 +9,12 @@ class CodePushSetupSubCommand extends Command { argParser ..addOption( 'flutter-version', - help: - 'Flutter SDK version to download artifacts for ' + help: 'Flutter SDK version to download artifacts for ' '(e.g., 3.24.0). Auto-detected if omitted.', ) ..addOption( 'platform', - help: - 'Target platform (e.g., darwin-arm64, linux-x64). ' + help: 'Target platform (e.g., darwin-arm64, linux-x64). ' 'Defaults to current platform.', ) ..addFlag( @@ -121,10 +119,8 @@ class CodePushSetupSubCommand extends Command { 'Flutter $flutterVersion is not yet supported for code push.', ); _logger.info(''); - _logger.info( - 'Run `fcp codepush setup --list-versions` to see ' - 'available Flutter versions.', - ); + _logger.info('Run `fcp codepush setup --list-versions` to see ' + 'available Flutter versions.'); return ExitCode.software.code; } checkProgress.complete('Flutter $flutterVersion is supported'); @@ -161,7 +157,9 @@ class CodePushSetupSubCommand extends Command { platform: resolvedPlatform, ); if (!installed) { - installProgress.fail('Failed to install overlays into Flutter SDK cache'); + installProgress.fail( + 'Failed to install overlays into Flutter SDK cache', + ); return ExitCode.software.code; } installProgress.complete('Overlays installed into Flutter SDK cache'); @@ -196,7 +194,9 @@ class CodePushSetupSubCommand extends Command { manager.cleanupOldVersions(keepVersion: flutterVersion); } - _logger.success('Code push is ready for Flutter $flutterVersion.'); + _logger.success( + 'Code push is ready for Flutter $flutterVersion.', + ); return ExitCode.success.code; } @@ -218,8 +218,8 @@ class CodePushSetupSubCommand extends Command { _logger.info('Supported Flutter versions for code push:'); _logger.info(''); - for (final entry - in versions.entries.toList()..sort((a, b) => a.key.compareTo(b.key))) { + for (final entry in versions.entries.toList() + ..sort((a, b) => a.key.compareTo(b.key))) { final ver = entry.key; final markers = []; if (ver == current) markers.add('current'); diff --git a/lib/src/commands/codepush_commands/_codepush_status.dart b/lib/src/commands/codepush_commands/_codepush_status.dart index 93ad968..903f971 100644 --- a/lib/src/commands/codepush_commands/_codepush_status.dart +++ b/lib/src/commands/codepush_commands/_codepush_status.dart @@ -7,13 +7,20 @@ import 'package:mason_logger/mason_logger.dart'; class CodePushStatusSubCommand extends Command { CodePushStatusSubCommand(this._logger) { argParser - ..addOption('app-id', help: 'The app ID to check status for.') + ..addOption( + 'app-id', + help: 'The app ID to check status for.', + ) ..addOption( 'release-id', help: 'If set, output only the patches for this release (ignored without --json).', ) - ..addFlag('json', help: 'Output as JSON.', negatable: false); + ..addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -66,19 +73,18 @@ class CodePushStatusSubCommand extends Command { if (statusCode != 200) { // --json is a structured-output contract: emit valid JSON with an // explicit error field and exit 0 so callers can parse it. - _logger.info( - json.encode({ - 'release_id': releaseId, - 'patches': >[], - 'error': patchesResult['error'] ?? 'Unknown', - }), - ); + _logger.info(json.encode({ + 'release_id': releaseId, + 'patches': >[], + 'error': patchesResult['error'] ?? 'Unknown', + })); return ExitCode.success.code; } final patches = patchesResult['patches'] as List? ?? []; - _logger.info( - json.encode({'release_id': releaseId, 'patches': patches}), - ); + _logger.info(json.encode({ + 'release_id': releaseId, + 'patches': patches, + })); return ExitCode.success.code; } @@ -91,13 +97,11 @@ class CodePushStatusSubCommand extends Command { if (statusCode == 401) { if (asJson) { - _logger.info( - json.encode({ - 'configured': false, - 'logged_in': false, - 'app_id': appId, - }), - ); + _logger.info(json.encode({ + 'configured': false, + 'logged_in': false, + 'app_id': appId, + })); return ExitCode.success.code; } progress?.fail('Session expired. Run "fcp codepush login" again.'); @@ -106,16 +110,14 @@ class CodePushStatusSubCommand extends Command { if (statusCode != 200) { if (asJson) { - _logger.info( - json.encode({ - 'configured': true, - 'logged_in': true, - 'app_id': appId, - 'releases': >[], - 'total_patches': 0, - 'error': releasesResult['error'] ?? 'Unknown', - }), - ); + _logger.info(json.encode({ + 'configured': true, + 'logged_in': true, + 'app_id': appId, + 'releases': >[], + 'total_patches': 0, + 'error': releasesResult['error'] ?? 'Unknown', + })); return ExitCode.success.code; } progress?.fail('Error: ${releasesResult['error'] ?? 'Unknown'}'); @@ -149,16 +151,14 @@ class CodePushStatusSubCommand extends Command { totalPatches += count; enrichedReleases.add(release); } - _logger.info( - json.encode({ - 'configured': true, - 'logged_in': true, - 'app_id': appId, - 'app_name': appName, - 'releases': enrichedReleases, - 'total_patches': totalPatches, - }), - ); + _logger.info(json.encode({ + 'configured': true, + 'logged_in': true, + 'app_id': appId, + 'app_name': appName, + 'releases': enrichedReleases, + 'total_patches': totalPatches, + })); return ExitCode.success.code; } @@ -166,8 +166,7 @@ class CodePushStatusSubCommand extends Command { if (releases.isEmpty) { _logger.info( - '\n No releases yet. Run "fcp codepush release" to create one.', - ); + '\n No releases yet. Run "fcp codepush release" to create one.'); return ExitCode.success.code; } @@ -202,15 +201,13 @@ class CodePushStatusSubCommand extends Command { return ExitCode.success.code; } catch (e) { if (asJson) { - _logger.info( - json.encode({ - 'configured': false, - 'logged_in': false, - 'releases': >[], - 'total_patches': 0, - 'error': '$e', - }), - ); + _logger.info(json.encode({ + 'configured': false, + 'logged_in': false, + 'releases': >[], + 'total_patches': 0, + 'error': '$e', + })); return ExitCode.success.code; } progress?.fail('Failed: $e'); diff --git a/lib/src/commands/codepush_commands/_codepush_versions.dart b/lib/src/commands/codepush_commands/_codepush_versions.dart index 6eb82e6..8e07674 100644 --- a/lib/src/commands/codepush_commands/_codepush_versions.dart +++ b/lib/src/commands/codepush_commands/_codepush_versions.dart @@ -15,7 +15,11 @@ import 'package:mason_logger/mason_logger.dart'; /// code push Flutter version (project-pinned wins over global). class CodePushVersionsSubCommand extends Command { CodePushVersionsSubCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -31,10 +35,8 @@ class CodePushVersionsSubCommand extends Command { final asJson = argResults?['json'] == true; final serverUrl = await CodePushClient.getServerUrl(); - final manager = CodePushArtifactManager( - logger: _logger, - baseUrl: serverUrl, - ); + final manager = + CodePushArtifactManager(logger: _logger, baseUrl: serverUrl); final progress = asJson ? null : _logger.progress('Fetching versions'); final manifest = await manager.fetchSupportedVersions(); @@ -45,13 +47,11 @@ class CodePushVersionsSubCommand extends Command { // explicit error field and return success so callers (VS Code / // IntelliJ tree providers) can parse the payload and render a // warning row instead of treating the process as a hard failure. - _logger.info( - json.encode({ - 'selected': null, - 'versions': >[], - 'error': 'Failed to fetch version manifest from server.', - }), - ); + _logger.info(json.encode({ + 'selected': null, + 'versions': >[], + 'error': 'Failed to fetch version manifest from server.', + })); return ExitCode.success.code; } return ExitCode.software.code; @@ -85,7 +85,10 @@ class CodePushVersionsSubCommand extends Command { } if (asJson) { - _logger.info(json.encode({'selected': selected, 'versions': versions})); + _logger.info(json.encode({ + 'selected': selected, + 'versions': versions, + })); return ExitCode.success.code; } @@ -99,9 +102,8 @@ class CodePushVersionsSubCommand extends Command { if (v['global'] == true) markers.add('global'); if (v['project_pinned'] == true) markers.add('pinned'); final suffix = markers.isEmpty ? '' : ' (${markers.join(', ')})'; - final marker = name == selected - ? '*' - : (v['installed'] == true ? ' ' : '-'); + final marker = + name == selected ? '*' : (v['installed'] == true ? ' ' : '-'); _logger.info(' $marker $name$suffix'); } if (selected == null) { diff --git a/lib/src/commands/config_command.dart b/lib/src/commands/config_command.dart index 4a3550c..fb61215 100644 --- a/lib/src/commands/config_command.dart +++ b/lib/src/commands/config_command.dart @@ -64,7 +64,11 @@ class ConfigCommand extends Command { class _ConfigListSubCommand extends Command { _ConfigListSubCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -115,8 +119,7 @@ class _ConfigGetSubCommand extends Command { final String name = 'get'; @override - final String description = - 'Get a configuration value. ' + final String description = 'Get a configuration value. ' 'Usage: config get '; @override @@ -151,8 +154,7 @@ class _ConfigSetSubCommand extends Command { final String name = 'set'; @override - final String description = - 'Set a configuration value. ' + final String description = 'Set a configuration value. ' 'Usage: config set '; @override diff --git a/lib/src/commands/doctor_command.dart b/lib/src/commands/doctor_command.dart index 24b87e6..437ef49 100644 --- a/lib/src/commands/doctor_command.dart +++ b/lib/src/commands/doctor_command.dart @@ -111,10 +111,8 @@ Future _isGclientAvailable() async { // Check depot_tools_path from .flutter_compilerc final rcFile = File('$home/.flutter_compilerc'); - final depotPath = await F.readValueForKeyFromRcConfig( - rcFile, - RunCommandKey.depotTools.key, - ); + final depotPath = + await F.readValueForKeyFromRcConfig(rcFile, RunCommandKey.depotTools.key); if (depotPath != null && depotPath.isNotEmpty) { final gclient = File('$depotPath/$bin'); if (await gclient.exists()) return true; @@ -172,9 +170,11 @@ Future _checkEnvironmentForGather({ // Check for upstream and origin git remotes try { - final result = await Process.run('git', [ - 'remote', - ], workingDirectory: gitDir); + final result = await Process.run( + 'git', + ['remote'], + workingDirectory: gitDir, + ); if (result.exitCode == 0) { final remotes = (result.stdout as String).trim().split('\n'); final hasUpstream = remotes.contains('upstream'); @@ -219,7 +219,11 @@ Future _checkEnvironmentForGather({ class DoctorCommand extends Command { DoctorCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -252,11 +256,9 @@ class DoctorCommand extends Command { final status = check['status'] as String; if (category == 'tools') { - _logger.info( - status == 'ok' - ? ' [+] $name is installed' - : ' [X] $name is NOT installed', - ); + _logger.info(status == 'ok' + ? ' [+] $name is installed' + : ' [X] $name is NOT installed'); } else if (category == 'engine_tools') { final displayName = switch (name) { 'gclient' => 'depot_tools (gclient)', @@ -264,11 +266,9 @@ class DoctorCommand extends Command { 'visual_studio' => 'Visual Studio (cl.exe)', _ => name, }; - _logger.info( - status == 'ok' - ? ' [+] $displayName is installed' - : ' [X] $displayName is NOT installed', - ); + _logger.info(status == 'ok' + ? ' [+] $displayName is installed' + : ' [X] $displayName is NOT installed'); } else if (category == 'config') { if (status == 'ok') { _logger.info(' [+] .flutter_compilerc is valid'); diff --git a/lib/src/commands/flutter_switch_command.dart b/lib/src/commands/flutter_switch_command.dart index e833f65..052fc2f 100644 --- a/lib/src/commands/flutter_switch_command.dart +++ b/lib/src/commands/flutter_switch_command.dart @@ -26,9 +26,8 @@ class FlutterSwitchCommand extends Command { @override Future run() async { - final mode = argResults?.rest.isNotEmpty == true - ? argResults?.rest[0] - : null; + final mode = + argResults?.rest.isNotEmpty == true ? argResults?.rest[0] : null; if (mode == null) { _logger.info('Switching to other flutter installation'); await F.switchFlutterEnvironment(); diff --git a/lib/src/commands/install_commands/_devtools.dart b/lib/src/commands/install_commands/_devtools.dart index 775c433..d766ee9 100644 --- a/lib/src/commands/install_commands/_devtools.dart +++ b/lib/src/commands/install_commands/_devtools.dart @@ -70,11 +70,9 @@ Future setupDevToolsEnvironment(Logger l) async { l.info('Fetching upstream...'); await F.runCommand('git', ['fetch', 'upstream'], workingDirectory: cloneDir); try { - await F.runCommand('git', [ - 'branch', - '--set-upstream-to=upstream/master', - 'master', - ], workingDirectory: cloneDir); + await F.runCommand( + 'git', ['branch', '--set-upstream-to=upstream/master', 'master'], + workingDirectory: cloneDir); } catch (_) { // May fail if already set or branch name differs — non-fatal l.warn('Could not set upstream tracking branch (may already be set).'); @@ -88,7 +86,10 @@ Future setupDevToolsEnvironment(Logger l) async { } // Run Flutter pub get for the tool directory - await F.runCommand('flutter', ['pub', 'get', '--directory', toolDir.path]); + await F.runCommand( + 'flutter', + ['pub', 'get', '--directory', toolDir.path], + ); // Save devtools path to .flutter_compilerc final home = F.homeDir(); @@ -109,10 +110,8 @@ Future setupDevToolsEnvironment(Logger l) async { envContents = await envFile.readAsString(); } - final devtoolsToolBinPath = Constants.platformDevToolsPATHExport.replaceAll( - '{{path}}', - cloneDir, - ); + final devtoolsToolBinPath = + Constants.platformDevToolsPATHExport.replaceAll('{{path}}', cloneDir); if (!envContents.contains(devtoolsToolBinPath.trim())) { envContents += devtoolsToolBinPath; await envFile.parent.create(recursive: true); @@ -122,26 +121,21 @@ Future setupDevToolsEnvironment(Logger l) async { // Optional step: Check and update the DevTools Flutter SDK try { - await F.runCommand('devtools_tool', [ - 'update-flutter-sdk', - '--update-on-path', - ]); + await F.runCommand( + 'devtools_tool', ['update-flutter-sdk', '--update-on-path']); } catch (e) { l.warn( - 'devtools_tool update-flutter-sdk failed (may need terminal restart): $e', - ); + 'devtools_tool update-flutter-sdk failed (may need terminal restart): $e'); } // Inform the user to restart their terminal l ..info( - '\nSetup complete! Please restart your terminal or source your shell configuration to apply PATH changes.\n' - .green, - ) + '\nSetup complete! Please restart your terminal or source your shell configuration to apply PATH changes.\n' + .green) ..info('To verify the setup, run the following command:') ..info( - '`flutter run` on a sample Flutter project and connect it to DevTools.', - ); + '`flutter run` on a sample Flutter project and connect it to DevTools.'); await displayIncrementalInfo(); return ExitCode.success.code; @@ -150,38 +144,26 @@ Future setupDevToolsEnvironment(Logger l) async { /// Ensure a git remote exists with the given URL. /// If it already exists, update its URL. If not, add it. Future _ensureRemote( - Logger l, - String name, - String url, - String workingDirectory, -) async { - final result = await Process.run('git', [ - 'remote', - 'get-url', - name, - ], workingDirectory: workingDirectory); + Logger l, String name, String url, String workingDirectory) async { + final result = await Process.run( + 'git', + ['remote', 'get-url', name], + workingDirectory: workingDirectory, + ); if (result.exitCode == 0) { // Remote exists — update URL if different final currentUrl = (result.stdout as String).trim(); if (currentUrl != url) { - await F.runCommand('git', [ - 'remote', - 'set-url', - name, - url, - ], workingDirectory: workingDirectory); + await F.runCommand('git', ['remote', 'set-url', name, url], + workingDirectory: workingDirectory); l.info('Updated remote "$name" to $url'.green); } else { l.info('Remote "$name" already set to $url'.green); } } else { // Remote doesn't exist — add it - await F.runCommand('git', [ - 'remote', - 'add', - name, - url, - ], workingDirectory: workingDirectory); + await F.runCommand('git', ['remote', 'add', name, url], + workingDirectory: workingDirectory); l.info('Added remote "$name" → $url'.green); } } diff --git a/lib/src/commands/install_commands/_engine.dart b/lib/src/commands/install_commands/_engine.dart index 75cc12e..52770ea 100644 --- a/lib/src/commands/install_commands/_engine.dart +++ b/lib/src/commands/install_commands/_engine.dart @@ -39,11 +39,8 @@ class EngineSubCommand extends Command { } } -Future setupEngineEnvironment( - Logger l, - String platform, { - bool force = false, -}) async { +Future setupEngineEnvironment(Logger l, String platform, + {bool force = false}) async { l.info('Flutter Engine Development Environment Setup for $platform'.blue); final os = Platform.operatingSystem; @@ -98,11 +95,11 @@ Future setupEngineEnvironment( } // Verify it's a git repo with an origin remote - final originResult = await Process.run('git', [ - 'remote', - 'get-url', - 'origin', - ], workingDirectory: flutterDir); + final originResult = await Process.run( + 'git', + ['remote', 'get-url', 'origin'], + workingDirectory: flutterDir, + ); if (originResult.exitCode != 0) { l.err( 'Could not read git origin URL from $flutterDir.\n' @@ -122,10 +119,8 @@ Future setupEngineEnvironment( if (await gclientFile.exists()) { l.info('.gclient already exists, skipping generation.'.green); } else { - final gclientContent = Constants.gclientFileTemplate.replaceAll( - '{{flutter_url}}', - originUrl, - ); + final gclientContent = + Constants.gclientFileTemplate.replaceAll('{{flutter_url}}', originUrl); await F.writeFile('$flutterDir/.gclient', gclientContent); l.info('Generated .gclient file.'.green); } @@ -206,10 +201,8 @@ Future _ensureDepotTools(Logger l) async { if (await envFile.exists()) { envContents = await envFile.readAsString(); } - final depotToolsExport = Constants.platformDepotToolsPATHExport.replaceAll( - '{{path}}', - depotToolsPath, - ); + final depotToolsExport = Constants.platformDepotToolsPATHExport + .replaceAll('{{path}}', depotToolsPath); if (!envContents.contains(depotToolsExport.trim())) { envContents += depotToolsExport; await envFile.parent.create(recursive: true); diff --git a/lib/src/commands/install_commands/_flutter.dart b/lib/src/commands/install_commands/_flutter.dart index e77fa1b..e835273 100644 --- a/lib/src/commands/install_commands/_flutter.dart +++ b/lib/src/commands/install_commands/_flutter.dart @@ -9,11 +9,14 @@ import 'package:mason_logger/mason_logger.dart'; class FlutterSubCommand extends Command { FlutterSubCommand(this._logger) { argParser - ..addFlag('flutter', abbr: 'f', help: 'Install Flutter environment') + ..addFlag( + 'flutter', + abbr: 'f', + help: 'Install Flutter environment', + ) ..addOption( 'ide', - help: - 'Calling IDE (vscode, intellij). ' + help: 'Calling IDE (vscode, intellij). ' 'Skips or auto-accepts IDE-specific prompts.', allowed: ['vscode', 'intellij'], ); @@ -110,8 +113,12 @@ Future setupFlutterEnvironment(Logger l, {String? ide}) async { .green, ) ..info('\nRun\n') - ..info('flutter_compile switch compiled'.blue) - ..info('\nto switch to the compiled Flutter installation.'); + ..info( + 'flutter_compile switch compiled'.blue, + ) + ..info( + '\nto switch to the compiled Flutter installation.', + ); return ExitCode.success.code; } @@ -119,36 +126,24 @@ Future setupFlutterEnvironment(Logger l, {String? ide}) async { /// Ensure a git remote exists with the given URL. /// If it already exists, update its URL. If not, add it. Future _ensureRemote( - Logger l, - String name, - String url, - String workingDirectory, -) async { - final result = await Process.run('git', [ - 'remote', - 'get-url', - name, - ], workingDirectory: workingDirectory); + Logger l, String name, String url, String workingDirectory) async { + final result = await Process.run( + 'git', + ['remote', 'get-url', name], + workingDirectory: workingDirectory, + ); if (result.exitCode == 0) { final currentUrl = (result.stdout as String).trim(); if (currentUrl != url) { - await F.runCommand('git', [ - 'remote', - 'set-url', - name, - url, - ], workingDirectory: workingDirectory); + await F.runCommand('git', ['remote', 'set-url', name, url], + workingDirectory: workingDirectory); l.info('Updated remote "$name" to $url'.green); } else { l.info('Remote "$name" already set to $url'.green); } } else { - await F.runCommand('git', [ - 'remote', - 'add', - name, - url, - ], workingDirectory: workingDirectory); + await F.runCommand('git', ['remote', 'add', name, url], + workingDirectory: workingDirectory); l.info('Added remote "$name" → $url'.green); } } diff --git a/lib/src/commands/run_command.dart b/lib/src/commands/run_command.dart index 326ea67..7abe60f 100644 --- a/lib/src/commands/run_command.dart +++ b/lib/src/commands/run_command.dart @@ -50,8 +50,7 @@ class RunCommand extends Command { final List aliases = ['r']; @override - final String description = - 'Run a Flutter app with a local engine build.\n\n' + final String description = 'Run a Flutter app with a local engine build.\n\n' 'Extra arguments after -- are forwarded to flutter run ' '(e.g. -- -d chrome).'; @@ -120,12 +119,15 @@ class RunCommand extends Command { _logger.info('Running with local engine: $outputDir'); - await F.runCommand('flutter', [ - 'run', - '--local-engine=$outputDir', - '--local-engine-src-path=$srcDir', - ...extraArgs, - ]); + await F.runCommand( + 'flutter', + [ + 'run', + '--local-engine=$outputDir', + '--local-engine-src-path=$srcDir', + ...extraArgs, + ], + ); return ExitCode.success.code; } diff --git a/lib/src/commands/sdk_commands/_sdk_exec.dart b/lib/src/commands/sdk_commands/_sdk_exec.dart index c7b25a1..4cc3c5b 100644 --- a/lib/src/commands/sdk_commands/_sdk_exec.dart +++ b/lib/src/commands/sdk_commands/_sdk_exec.dart @@ -51,8 +51,7 @@ class SdkExecSubCommand extends Command { final sdkPath = F.sdkVersionPath(version); final environment = { ...F.sdkEnvironment(sdkPath), - 'PATH': - '$sdkPath/bin${F.envPathSeparator}$sdkPath/bin/cache/dart-sdk/bin' + 'PATH': '$sdkPath/bin${F.envPathSeparator}$sdkPath/bin/cache/dart-sdk/bin' '${F.envPathSeparator}${Platform.environment['PATH'] ?? ''}', }; diff --git a/lib/src/commands/sdk_commands/_sdk_global.dart b/lib/src/commands/sdk_commands/_sdk_global.dart index c1cca6c..937f800 100644 --- a/lib/src/commands/sdk_commands/_sdk_global.dart +++ b/lib/src/commands/sdk_commands/_sdk_global.dart @@ -34,7 +34,9 @@ class SdkGlobalSubCommand extends Command { final globalVersion = await F.readGlobalSdkVersion(); if (globalVersion == null) { _logger.info('No global SDK version set.'); - _logger.info('\nRun "flutter_compile sdk global " to set one.'); + _logger.info( + '\nRun "flutter_compile sdk global " to set one.', + ); } else { _logger.info('Global SDK version: $globalVersion'); } diff --git a/lib/src/commands/sdk_commands/_sdk_install.dart b/lib/src/commands/sdk_commands/_sdk_install.dart index 8394d1f..4b5d4f3 100644 --- a/lib/src/commands/sdk_commands/_sdk_install.dart +++ b/lib/src/commands/sdk_commands/_sdk_install.dart @@ -18,8 +18,7 @@ class SdkInstallSubCommand extends Command { @override final String name = 'install'; @override - final String description = - 'Install a Flutter SDK version or channel.\n\n' + final String description = 'Install a Flutter SDK version or channel.\n\n' 'Usage: flutter_compile sdk install [--force] \n' 'Examples:\n' ' flutter_compile sdk install 3.19.0\n' @@ -57,12 +56,17 @@ Future installSdk(Logger l, String version, {bool force = false}) async { await Directory('$home${Constants.sdkVersionsPath}').create(recursive: true); - await F.cloneRepository(Constants.flutterGitUrl, targetPath, force: force); + await F.cloneRepository( + Constants.flutterGitUrl, + targetPath, + force: force, + ); - await F.runCommand('git', [ - 'checkout', - version, - ], workingDirectory: targetPath); + await F.runCommand( + 'git', + ['checkout', version], + workingDirectory: targetPath, + ); l.info('Caching Flutter SDK artifacts...'); await F.runCommand( diff --git a/lib/src/commands/sdk_commands/_sdk_list.dart b/lib/src/commands/sdk_commands/_sdk_list.dart index dbb3245..9b4dd4a 100644 --- a/lib/src/commands/sdk_commands/_sdk_list.dart +++ b/lib/src/commands/sdk_commands/_sdk_list.dart @@ -8,7 +8,11 @@ import 'package:mason_logger/mason_logger.dart'; class SdkListSubCommand extends Command { SdkListSubCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -36,13 +40,12 @@ Future>> gatherSdkList() async { return >[]; } - final entries = - versionsDir - .listSync() - .whereType() - .where((d) => d.path.split('/').last != Constants.defaultSdkLink) - .toList() - ..sort((a, b) => a.path.compareTo(b.path)); + final entries = versionsDir + .listSync() + .whereType() + .where((d) => d.path.split('/').last != Constants.defaultSdkLink) + .toList() + ..sort((a, b) => a.path.compareTo(b.path)); final globalVersion = await F.readGlobalSdkVersion(); final projectVersion = await F.readProjectSdkVersion(); @@ -58,9 +61,8 @@ Future>> gatherSdkList() async { }); } - final compiledFlutterDir = Directory( - '$home${Constants.flutterCompileInstallPath}', - ); + final compiledFlutterDir = + Directory('$home${Constants.flutterCompileInstallPath}'); if (compiledFlutterDir.existsSync()) { sdks.add({ 'version': 'compiled', @@ -82,7 +84,9 @@ Future listSdks(Logger l, {bool asJson = false}) async { l.info(json.encode(>[])); } else { l.info('No Flutter SDKs installed.'); - l.info('\nRun "flutter_compile sdk install " to install one.'); + l.info( + '\nRun "flutter_compile sdk install " to install one.', + ); } return ExitCode.success.code; } @@ -109,7 +113,9 @@ Future listSdks(Logger l, {bool asJson = false}) async { if (contributor.isNotEmpty) { l.info('\nContributor environments:'); for (final sdk in contributor) { - l.info(' ${sdk['version']} ${sdk['path']} (via install flutter)'); + l.info( + ' ${sdk['version']} ${sdk['path']} (via install flutter)', + ); } } diff --git a/lib/src/commands/sdk_commands/_sdk_remove.dart b/lib/src/commands/sdk_commands/_sdk_remove.dart index e4c4d30..1ec386c 100644 --- a/lib/src/commands/sdk_commands/_sdk_remove.dart +++ b/lib/src/commands/sdk_commands/_sdk_remove.dart @@ -13,8 +13,7 @@ class SdkRemoveSubCommand extends Command { @override final String name = 'remove'; @override - final String description = - 'Remove an installed Flutter SDK version.\n\n' + final String description = 'Remove an installed Flutter SDK version.\n\n' 'Usage: flutter_compile sdk remove \n' 'Example: flutter_compile sdk remove 3.19.0'; diff --git a/lib/src/commands/sdk_commands/_sdk_use.dart b/lib/src/commands/sdk_commands/_sdk_use.dart index f89ac2d..f1fcc32 100644 --- a/lib/src/commands/sdk_commands/_sdk_use.dart +++ b/lib/src/commands/sdk_commands/_sdk_use.dart @@ -37,7 +37,9 @@ class SdkUseSubCommand extends Command { 'No project SDK version set ' '(no ${Constants.flutterVersionFile} found).', ); - _logger.info('\nRun "flutter_compile sdk use " to pin one.'); + _logger.info( + '\nRun "flutter_compile sdk use " to pin one.', + ); } else { _logger.info('Project SDK version: $projectVersion'); } @@ -53,9 +55,8 @@ class SdkUseSubCommand extends Command { return ExitCode.usage.code; } - final file = File( - '${Directory.current.path}/${Constants.flutterVersionFile}', - ); + final file = + File('${Directory.current.path}/${Constants.flutterVersionFile}'); await file.writeAsString('$version\n'); _logger.success( diff --git a/lib/src/commands/status_command.dart b/lib/src/commands/status_command.dart index c259673..010b221 100644 --- a/lib/src/commands/status_command.dart +++ b/lib/src/commands/status_command.dart @@ -63,7 +63,11 @@ Future> gatherStatus() async { class StatusCommand extends Command { StatusCommand(this._logger) { - argParser.addFlag('json', help: 'Output as JSON.', negatable: false); + argParser.addFlag( + 'json', + help: 'Output as JSON.', + negatable: false, + ); } final Logger _logger; @@ -121,7 +125,9 @@ class StatusCommand extends Command { for (final b in builds) { final name = b['name']!; final size = b['size']!; - _logger.info(' $name${' ' * (30 - name.length).clamp(0, 30)}$size'); + _logger.info( + ' $name${' ' * (30 - name.length).clamp(0, 30)}$size', + ); } } } diff --git a/lib/src/commands/sync_commands/_sync_devtools.dart b/lib/src/commands/sync_commands/_sync_devtools.dart index 782ede1..c12d061 100644 --- a/lib/src/commands/sync_commands/_sync_devtools.dart +++ b/lib/src/commands/sync_commands/_sync_devtools.dart @@ -43,14 +43,16 @@ class SyncDevtoolsSubCommand extends Command { return ExitCode.config.code; } - await F.runCommand('git', [ - 'fetch', - 'upstream', - ], workingDirectory: devtoolsPath); - await F.runCommand('git', [ - 'rebase', - 'upstream/master', - ], workingDirectory: devtoolsPath); + await F.runCommand( + 'git', + ['fetch', 'upstream'], + workingDirectory: devtoolsPath, + ); + await F.runCommand( + 'git', + ['rebase', 'upstream/master'], + workingDirectory: devtoolsPath, + ); _logger.success('DevTools synced successfully.'); return ExitCode.success.code; diff --git a/lib/src/commands/sync_commands/_sync_engine.dart b/lib/src/commands/sync_commands/_sync_engine.dart index 9c0c04d..7186891 100644 --- a/lib/src/commands/sync_commands/_sync_engine.dart +++ b/lib/src/commands/sync_commands/_sync_engine.dart @@ -76,14 +76,16 @@ class SyncEngineSubCommand extends Command { // Git remotes are on the Flutter repo root (parent of engine dir) try { - await F.runCommand('git', [ - 'fetch', - 'upstream', - ], workingDirectory: flutterRoot); - await F.runCommand('git', [ - 'rebase', - 'upstream/master', - ], workingDirectory: flutterRoot); + await F.runCommand( + 'git', + ['fetch', 'upstream'], + workingDirectory: flutterRoot, + ); + await F.runCommand( + 'git', + ['rebase', 'upstream/master'], + workingDirectory: flutterRoot, + ); } on Exception catch (e) { _logger.err( 'Git sync failed: $e\n' diff --git a/lib/src/commands/sync_commands/_sync_flutter.dart b/lib/src/commands/sync_commands/_sync_flutter.dart index 1c94bf2..caab4a7 100644 --- a/lib/src/commands/sync_commands/_sync_flutter.dart +++ b/lib/src/commands/sync_commands/_sync_flutter.dart @@ -46,19 +46,23 @@ class SyncFlutterSubCommand extends Command { return ExitCode.config.code; } - await F.runCommand('git', [ - 'fetch', - 'upstream', - ], workingDirectory: flutterDir); - await F.runCommand('git', [ - 'rebase', - 'upstream/master', - ], workingDirectory: flutterDir); + await F.runCommand( + 'git', + ['fetch', 'upstream'], + workingDirectory: flutterDir, + ); + await F.runCommand( + 'git', + ['rebase', 'upstream/master'], + workingDirectory: flutterDir, + ); _logger.info('Running flutter update-packages...'); - await F.runCommand('$flutterPath/flutter', [ - 'update-packages', - ], workingDirectory: flutterDir); + await F.runCommand( + '$flutterPath/flutter', + ['update-packages'], + workingDirectory: flutterDir, + ); _logger.success('Flutter framework synced successfully.'); return ExitCode.success.code; diff --git a/lib/src/commands/sync_commands/sync_command.dart b/lib/src/commands/sync_commands/sync_command.dart index 36b2760..83892b0 100644 --- a/lib/src/commands/sync_commands/sync_command.dart +++ b/lib/src/commands/sync_commands/sync_command.dart @@ -53,14 +53,10 @@ class SyncCommand extends Command { _logger.info(''); } - final succeeded = results.entries - .where((e) => e.value) - .map((e) => e.key) - .toList(); - final failed = results.entries - .where((e) => !e.value) - .map((e) => e.key) - .toList(); + final succeeded = + results.entries.where((e) => e.value).map((e) => e.key).toList(); + final failed = + results.entries.where((e) => !e.value).map((e) => e.key).toList(); if (succeeded.isNotEmpty) { _logger.success('Synced: ${succeeded.join(', ')}'); diff --git a/lib/src/commands/test_command.dart b/lib/src/commands/test_command.dart index c931361..045b51f 100644 --- a/lib/src/commands/test_command.dart +++ b/lib/src/commands/test_command.dart @@ -50,8 +50,7 @@ class TestCommand extends Command { final List aliases = ['t']; @override - final String description = - 'Run Flutter tests with a local engine build.\n\n' + final String description = 'Run Flutter tests with a local engine build.\n\n' 'Extra arguments after -- are forwarded to flutter test ' '(e.g. -- test/my_test.dart).'; @@ -120,12 +119,15 @@ class TestCommand extends Command { _logger.info('Running tests with local engine: $outputDir'); - await F.runCommand('flutter', [ - 'test', - '--local-engine=$outputDir', - '--local-engine-src-path=$srcDir', - ...extraArgs, - ]); + await F.runCommand( + 'flutter', + [ + 'test', + '--local-engine=$outputDir', + '--local-engine-src-path=$srcDir', + ...extraArgs, + ], + ); return ExitCode.success.code; } diff --git a/lib/src/commands/uninstall_command.dart b/lib/src/commands/uninstall_command.dart index e47d1a8..94ad9c7 100644 --- a/lib/src/commands/uninstall_command.dart +++ b/lib/src/commands/uninstall_command.dart @@ -39,11 +39,8 @@ class UninstallCommand extends Command { class FlutterUninstallSubCommand extends Command { FlutterUninstallSubCommand(this._logger) { - argParser.addFlag( - 'flutter', - abbr: 'f', - help: 'Uninstall Flutter environment', - ); + argParser.addFlag('flutter', + abbr: 'f', help: 'Uninstall Flutter environment'); } final Logger _logger; @@ -61,11 +58,8 @@ class FlutterUninstallSubCommand extends Command { class DevToolsUninstallSubCommand extends Command { DevToolsUninstallSubCommand(this._logger) { - argParser.addFlag( - 'devtools', - abbr: 'd', - help: 'Uninstall DevTools environment', - ); + argParser.addFlag('devtools', + abbr: 'd', help: 'Uninstall DevTools environment'); } final Logger _logger; @@ -183,9 +177,7 @@ Future uninstallDevToolsEnvironment(Logger l) async { // Read devtools path from config, fall back to default var devtoolsPath = await F.readValueForKeyFromRcConfig( - rcConfigFile, - RunCommandKey.devTools.key, - ); + rcConfigFile, RunCommandKey.devTools.key); devtoolsPath ??= '$home${Constants.devToolsInstallPath}'; // Delete the directory if it exists @@ -202,10 +194,8 @@ Future uninstallDevToolsEnvironment(Logger l) async { final envFile = File(envPath); if (await envFile.exists()) { var envContents = await envFile.readAsString(); - final devtoolsExport = Constants.platformDevToolsPATHExport.replaceAll( - '{{path}}', - devtoolsPath, - ); + final devtoolsExport = Constants.platformDevToolsPATHExport + .replaceAll('{{path}}', devtoolsPath); if (envContents.contains(devtoolsExport)) { envContents = envContents.replaceAll(devtoolsExport, ''); await envFile.writeAsString(envContents); @@ -218,10 +208,8 @@ Future uninstallDevToolsEnvironment(Logger l) async { final configFile = File(configPath); if (await configFile.exists()) { var contents = await configFile.readAsString(); - final devtoolsExport = Constants.platformDevToolsPATHExport.replaceAll( - '{{path}}', - devtoolsPath, - ); + final devtoolsExport = Constants.platformDevToolsPATHExport + .replaceAll('{{path}}', devtoolsPath); if (contents.contains(devtoolsExport)) { contents = contents.replaceAll(devtoolsExport, ''); await configFile.writeAsString(contents); @@ -290,9 +278,8 @@ Future uninstallEngineEnvironment(Logger l) async { if (_depotToolsBlockPattern.hasMatch(envContents)) { envContents = envContents.replaceAll(_depotToolsBlockPattern, ''); await envFile.writeAsString(envContents); - l.info( - 'Removed depot_tools PATH export from .${Constants.envFile}.'.green, - ); + l.info('Removed depot_tools PATH export from .${Constants.envFile}.' + .green); } } @@ -311,7 +298,9 @@ Future uninstallEngineEnvironment(Logger l) async { if (await rcConfigFile.exists()) { final lines = await rcConfigFile.readAsLines(); final filtered = lines - .where((line) => !line.startsWith('${RunCommandKey.depotTools.key}:')) + .where( + (line) => !line.startsWith('${RunCommandKey.depotTools.key}:'), + ) .toList(); await rcConfigFile.writeAsString('${filtered.join('\n')}\n'); l.info('Removed depot_tools_path from .flutter_compilerc.'.green); diff --git a/lib/src/commands/update_command.dart b/lib/src/commands/update_command.dart index bb8a9c2..684774f 100644 --- a/lib/src/commands/update_command.dart +++ b/lib/src/commands/update_command.dart @@ -13,9 +13,11 @@ import 'package:pub_updater/pub_updater.dart'; /// {@endtemplate} class UpdateCommand extends Command { /// {@macro update_command} - UpdateCommand({required Logger logger, PubUpdater? pubUpdater}) - : _logger = logger, - _pubUpdater = pubUpdater ?? PubUpdater(); + UpdateCommand({ + required Logger logger, + PubUpdater? pubUpdater, + }) : _logger = logger, + _pubUpdater = pubUpdater ?? PubUpdater(); final Logger _logger; final PubUpdater _pubUpdater; diff --git a/lib/src/daemon/daemon_peer.dart b/lib/src/daemon/daemon_peer.dart index e7a02ef..35f4512 100644 --- a/lib/src/daemon/daemon_peer.dart +++ b/lib/src/daemon/daemon_peer.dart @@ -17,7 +17,7 @@ import 'package:stream_channel/stream_channel.dart'; class DaemonPeer { DaemonPeer({required Logger logger, StreamChannel? channel}) - : _channel = channel; + : _channel = channel; final StreamChannel? _channel; late rpc.Peer _peer; diff --git a/lib/src/shared/codepush_archive_service.dart b/lib/src/shared/codepush_archive_service.dart index 5efb4e3..56f42d1 100644 --- a/lib/src/shared/codepush_archive_service.dart +++ b/lib/src/shared/codepush_archive_service.dart @@ -23,9 +23,11 @@ import 'package:mason_logger/mason_logger.dart'; /// `.fcp-archive/` to the repo's local-only `.git/info/exclude` on /// first use. No project-level `.gitignore` is touched. class CodePushArchiveService { - CodePushArchiveService({required Logger logger, Directory? projectDir}) - : _logger = logger, - _projectDir = projectDir ?? Directory.current; + CodePushArchiveService({ + required Logger logger, + Directory? projectDir, + }) : _logger = logger, + _projectDir = projectDir ?? Directory.current; static const int _archiveFormatVersion = 1; static const String _archiveDirName = '.fcp-archive'; @@ -74,11 +76,8 @@ class CodePushArchiveService { releaseDir.createSync(recursive: true); final runnerCopy = '${releaseDir.path}/Runner.app'; - final cpRunner = Process.runSync('cp', [ - '-R', - runnerApp.path, - runnerCopy, - ]); + final cpRunner = + Process.runSync('cp', ['-R', runnerApp.path, runnerCopy]); if (cpRunner.exitCode != 0) { _logger.warn('Could not archive Runner.app: ${cpRunner.stderr}'); return false; @@ -90,7 +89,10 @@ class CodePushArchiveService { var archivedDsym = false; if (dsymSource.existsSync()) { final dsymCopy = '${releaseDir.path}/Runner.app.dSYM'; - final cpDsym = Process.runSync('cp', ['-R', dsymSource.path, dsymCopy]); + final cpDsym = Process.runSync( + 'cp', + ['-R', dsymSource.path, dsymCopy], + ); if (cpDsym.exitCode == 0) { archivedDsym = true; } else { @@ -149,9 +151,8 @@ class CodePushArchiveService { infoDir.createSync(recursive: true); } final excludeFile = File('${infoDir.path}/exclude'); - final existing = excludeFile.existsSync() - ? excludeFile.readAsStringSync() - : ''; + final existing = + excludeFile.existsSync() ? excludeFile.readAsStringSync() : ''; final present = existing.split('\n').any((line) { final trimmed = line.trim(); return trimmed == _excludeRule || @@ -173,12 +174,10 @@ class CodePushArchiveService { String? _findGitDir() { try { - final result = Process.runSync('git', [ - '-C', - _projectDir.path, - 'rev-parse', - '--git-dir', - ]); + final result = Process.runSync( + 'git', + ['-C', _projectDir.path, 'rev-parse', '--git-dir'], + ); if (result.exitCode != 0) return null; final raw = (result.stdout as String).trim(); if (raw.isEmpty) return null; diff --git a/lib/src/shared/codepush_client.dart b/lib/src/shared/codepush_client.dart index 4d2e24e..5598d59 100644 --- a/lib/src/shared/codepush_client.dart +++ b/lib/src/shared/codepush_client.dart @@ -14,8 +14,8 @@ import 'package:flutter_compile/src/shared/functions.dart'; /// even if a rogue CA issues a certificate for the server domain. class CodePushClient { CodePushClient({String? serverUrl, String? pinnedCertificatePath}) - : _serverUrl = serverUrl ?? Constants.codePushDefaultServer, - _http = _createHttpClient(pinnedCertificatePath); + : _serverUrl = serverUrl ?? Constants.codePushDefaultServer, + _http = _createHttpClient(pinnedCertificatePath); final String _serverUrl; final HttpClient _http; @@ -149,10 +149,10 @@ class CodePushClient { required String email, String? name, }) async { - return _post( - '/api/v1/auth/register', - body: {'email': email, if (name != null) 'name': name}, - ); + return _post('/api/v1/auth/register', body: { + 'email': email, + if (name != null) 'name': name, + }); } /// GET /api/v1/account — get user profile and subscription status. @@ -307,7 +307,10 @@ class CodePushClient { return _patch( '/api/v1/apps', token: token, - body: {'app_id': appId, 'public_key': publicKeyPem}, + body: { + 'app_id': appId, + 'public_key': publicKeyPem, + }, ); } @@ -329,10 +332,8 @@ class CodePushClient { required String releaseId, }) async { // First get the release info to find the snapshot URL. - final info = await _get( - '/api/v1/releases?release_id=$releaseId', - token: token, - ); + final info = + await _get('/api/v1/releases?release_id=$releaseId', token: token); final releases = info['releases'] as List?; if (releases == null || releases.isEmpty) return null; @@ -425,16 +426,13 @@ class CodePushClient { try { // 1. Generate random AES-256 key (32 bytes) and IV (16 bytes). final random = Random.secure(); - final aesKey = Uint8List.fromList( - List.generate(32, (_) => random.nextInt(256)), - ); - final iv = Uint8List.fromList( - List.generate(16, (_) => random.nextInt(256)), - ); + final aesKey = + Uint8List.fromList(List.generate(32, (_) => random.nextInt(256))); + final iv = + Uint8List.fromList(List.generate(16, (_) => random.nextInt(256))); - final aesKeyHex = aesKey - .map((b) => b.toRadixString(16).padLeft(2, '0')) - .join(); + final aesKeyHex = + aesKey.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); final ivHex = iv.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); // 2. Write source to temp file and encrypt with AES-256-CBC. @@ -522,7 +520,10 @@ class CodePushClient { // --- HTTP helpers --- - Future> _get(String path, {String? token}) async { + Future> _get( + String path, { + String? token, + }) async { final uri = Uri.parse('$_serverUrl$path'); final request = await _http.getUrl(uri); if (token != null) { @@ -603,8 +604,7 @@ class CodePushClient { } Future> _parseResponse( - HttpClientResponse response, - ) async { + HttpClientResponse response) async { final body = await response.transform(utf8.decoder).join(); final statusCode = response.statusCode; if (body.isEmpty) { diff --git a/lib/src/shared/constants.dart b/lib/src/shared/constants.dart index b1a5cf7..33f0e8f 100644 --- a/lib/src/shared/constants.dart +++ b/lib/src/shared/constants.dart @@ -26,7 +26,7 @@ $env:PATH = "{{path}}\cache\dart-sdk\bin;$env:PATH" '''; - // Engine Constants +// Engine Constants // Engine lives inside the Flutter contributor checkout static const engineInstallPath = '$flutterCompileInstallPath/engine'; static const depotToolsInstallPath = '$baseCliPath/depot_tools'; @@ -62,7 +62,7 @@ solutions = [ static const engineUpstreamSSH = 'git@github.com:flutter/flutter.git'; static const engineUpstreamHTTPS = 'https://github.com/flutter/flutter.git'; - // DevTools Constants +// DevTools Constants static const devToolsInstallPath = '$baseCliPath/devtools'; static const devToolsPATHExport = r''' @@ -289,11 +289,15 @@ enum RunCommandKey { flutterCompile('flutter_path'), // key for the path to the flutter compile devTools('devtools_path'), // key for the path to the devtools engine('engine_path'), // key for the path to the engine - depotTools('depot_tools_path') // key for the path to depot_tools + depotTools('depot_tools_path'), // key for the path to depot_tools + ; final String key; const RunCommandKey(this.key); } -enum FlutterMode { normal, compiled } +enum FlutterMode { + normal, + compiled, +} diff --git a/lib/src/shared/functions.dart b/lib/src/shared/functions.dart index ddc38b9..653305a 100644 --- a/lib/src/shared/functions.dart +++ b/lib/src/shared/functions.dart @@ -55,10 +55,8 @@ class F { } if (await rcConfigFile.exists()) { - final persistedPath = await readValueForKeyFromRcConfig( - rcConfigFile, - key.key, - ); + final persistedPath = + await readValueForKeyFromRcConfig(rcConfigFile, key.key); if (persistedPath != null) { return persistedPath; } @@ -190,9 +188,8 @@ class F { if (extractedBlocks.isEmpty) { // No legacy blocks found — just ensure source line. await ensureSourceLineInShellRc(); - final hadSourceLine = Constants.platformSourceLinePattern.hasMatch( - rcContents, - ); + final hadSourceLine = + Constants.platformSourceLinePattern.hasMatch(rcContents); return MigrateResult( blocksMoved: 0, sourceLineAdded: !hadSourceLine, @@ -222,9 +219,8 @@ class F { await envFile.writeAsString(envContents); // Strip blocks from the shell RC and add source line. - final hadSourceLine = Constants.platformSourceLinePattern.hasMatch( - rcContents, - ); + final hadSourceLine = + Constants.platformSourceLinePattern.hasMatch(rcContents); await ensureSourceLineInShellRc(); // Rewrite the SDK manager block with the new guarded template so @@ -259,8 +255,8 @@ class F { final shellConfig = shell.contains('bash') ? '.bashrc' : shell.contains('zsh') - ? '.zshrc' - : '.profile'; + ? '.zshrc' + : '.profile'; return '$home/$shellConfig'; } @@ -345,12 +341,10 @@ class F { await runCommand('brew', ['install', '--cask', 'android-platform-tools']); } else if (os == 'linux') { await runCommand('sudo', ['apt-get', 'update']); - await runCommand('sudo', [ - 'apt-get', - 'install', - '-y', - 'android-tools-adb', - ]); + await runCommand( + 'sudo', + ['apt-get', 'install', '-y', 'android-tools-adb'], + ); } else if (os == 'windows') { logger.info( 'On Windows, install Android platform tools manually or via Android Studio.', @@ -460,9 +454,8 @@ class F { .platformFlutterCompilePATHExport .replaceAll('{{path}}', flutterCompilePath); - final isUsingCompiledVersion = _flutterCompileBlockPattern.hasMatch( - contents, - ); + final isUsingCompiledVersion = + _flutterCompileBlockPattern.hasMatch(contents); if (mode == FlutterMode.compiled && !isUsingCompiledVersion) { contents += flutterCompilePATHExport; @@ -509,9 +502,8 @@ class F { /// Flutter repo so `fcp switch` doesn't fail with a broken Dart SDK download. static void _ensureDartSdkForCompiled(String compiledFlutterPath) { try { - final targetDart = File( - '$compiledFlutterPath/bin/cache/dart-sdk/bin/dart', - ); + final targetDart = + File('$compiledFlutterPath/bin/cache/dart-sdk/bin/dart'); if (targetDart.existsSync()) return; // Already has a Dart SDK. // Find the source Dart SDK. @@ -539,9 +531,8 @@ class F { final engineStamp = File('$compiledFlutterPath/bin/cache/engine.stamp'); if (engineStamp.existsSync()) { final hash = engineStamp.readAsStringSync().trim(); - File( - '$compiledFlutterPath/bin/cache/engine-dart-sdk.stamp', - ).writeAsStringSync(hash); + File('$compiledFlutterPath/bin/cache/engine-dart-sdk.stamp') + .writeAsStringSync(hash); } } catch (_) { // Best effort — don't break switch if this fails. @@ -578,7 +569,10 @@ class F { } } try { - await runCommand('git', ['clone', url, directory]); + await runCommand( + 'git', + ['clone', url, directory], + ); } catch (e) { // Clean up partial clone on failure if (dir.existsSync()) { @@ -600,9 +594,8 @@ class F { for (var line in lines) { final colonIndex = line.indexOf(':'); if (colonIndex != -1) { - keyValuePairs[line.substring(0, colonIndex)] = line.substring( - colonIndex + 1, - ); + keyValuePairs[line.substring(0, colonIndex)] = + line.substring(colonIndex + 1); } } } @@ -637,9 +630,8 @@ class F { static String sdkPubCachePath(String sdkPath) => '$sdkPath/.pub-cache'; - static Map sdkEnvironment(String sdkPath) => { - 'PUB_CACHE': sdkPubCachePath(sdkPath), - }; + static Map sdkEnvironment(String sdkPath) => + {'PUB_CACHE': sdkPubCachePath(sdkPath)}; static String sdkVersionPath(String version) { final home = homeDir(); diff --git a/lib/src/tui/tui_app.dart b/lib/src/tui/tui_app.dart index 4982c12..9a151e4 100644 --- a/lib/src/tui/tui_app.dart +++ b/lib/src/tui/tui_app.dart @@ -182,10 +182,8 @@ class TuiApp { await F.cloneRepository(Constants.flutterGitUrl, sdkPath); // Checkout the specific version - await Process.run('git', [ - 'checkout', - version, - ], workingDirectory: sdkPath); + await Process.run('git', ['checkout', version], + workingDirectory: sdkPath); _state.statusMessage = 'SDK "$version" installed.'; _state.sdkList = await gatherSdkList(); diff --git a/lib/src/tui/tui_renderer.dart b/lib/src/tui/tui_renderer.dart index 87ee7ce..453d0c4 100644 --- a/lib/src/tui/tui_renderer.dart +++ b/lib/src/tui/tui_renderer.dart @@ -58,7 +58,11 @@ class TuiRenderer { buf.writeln(parts.join(' ')); } - static void _renderContent(StringBuffer buf, TuiState state, int width) { + static void _renderContent( + StringBuffer buf, + TuiState state, + int width, + ) { if (state.loading) { buf.writeln(' Loading...'); return; @@ -115,8 +119,8 @@ class TuiRenderer { final icon = status == 'ok' ? '+' : status == 'not_configured' - ? '-' - : 'X'; + ? '-' + : 'X'; buf.writeln(' [$icon] $name: $status'); } } @@ -153,8 +157,8 @@ class TuiRenderer { final icon = status == 'ok' ? '+' : status == 'not_found' || status == 'not_configured' - ? '-' - : 'X'; + ? '-' + : 'X'; String displayName; if (category == 'engine_tools' && name == 'gclient') { @@ -183,7 +187,9 @@ class TuiRenderer { } if (state.currentTab == 0) { - buf.write(' [Enter] Set global [i] Install [r] Refresh [q] Quit'); + buf.write( + ' [Enter] Set global [i] Install [r] Refresh [q] Quit', + ); } else if (state.currentTab == 3) { buf.write(' [r] Refresh [q] Quit'); } else { diff --git a/test/ensure_build_test.dart b/test/ensure_build_test.dart index e390cb4..17ddd58 100644 --- a/test/ensure_build_test.dart +++ b/test/ensure_build_test.dart @@ -6,17 +6,14 @@ import 'package:test/test.dart'; void main() { test('packageVersion matches pubspec.yaml version', () { final pubspec = File('pubspec.yaml').readAsStringSync(); - final match = RegExp( - r'^version:\s*(\S+)', - multiLine: true, - ).firstMatch(pubspec); + final match = + RegExp(r'^version:\s*(\S+)', multiLine: true).firstMatch(pubspec); expect(match, isNotNull, reason: 'pubspec.yaml is missing a version: line'); final pubspecVersion = match!.group(1); expect( packageVersion, pubspecVersion, - reason: - 'lib/src/version.dart is out of sync with pubspec.yaml. ' + reason: 'lib/src/version.dart is out of sync with pubspec.yaml. ' 'Bump both together when cutting a release.', ); }); diff --git a/test/helpers/test_helpers.dart b/test/helpers/test_helpers.dart index 3fa4e89..8762701 100644 --- a/test/helpers/test_helpers.dart +++ b/test/helpers/test_helpers.dart @@ -14,19 +14,21 @@ class MockProgress extends Mock implements Progress {} Logger logger, PubUpdater pubUpdater, FlutterCompileCommandRunner commandRunner, -}) -createTestCommandRunner() { +}) createTestCommandRunner() { final logger = MockLogger(); final pubUpdater = MockPubUpdater(); - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); + when(() => pubUpdater.getLatestVersion(any())) + .thenAnswer((_) async => packageVersion); final commandRunner = FlutterCompileCommandRunner( logger: logger, pubUpdater: pubUpdater, ); - return (logger: logger, pubUpdater: pubUpdater, commandRunner: commandRunner); + return ( + logger: logger, + pubUpdater: pubUpdater, + commandRunner: commandRunner, + ); } diff --git a/test/src/command_runner_test.dart b/test/src/command_runner_test.dart index d1b4894..0936e76 100644 --- a/test/src/command_runner_test.dart +++ b/test/src/command_runner_test.dart @@ -18,8 +18,7 @@ import '../helpers/test_helpers.dart'; // mismatch triggered the banner — including `local > pub.dev`). const latestVersion = '9999.0.0'; -final updatePrompt = - ''' +final updatePrompt = ''' ${lightYellow.wrap('Update available!')} ${lightCyan.wrap(packageVersion)} \u2192 ${lightCyan.wrap(latestVersion)} Run ${lightCyan.wrap('$executableName update')} to update'''; @@ -46,16 +45,19 @@ void main() { verify(() => logger.info(updatePrompt)).called(1); }); - test('Does not show update message when the shell calls the ' - 'completion command', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - - final result = await commandRunner.run(['completion']); - expect(result, equals(ExitCode.success.code)); - verifyNever(() => logger.info(updatePrompt)); - }); + test( + 'Does not show update message when the shell calls the ' + 'completion command', + () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + + final result = await commandRunner.run(['completion']); + expect(result, equals(ExitCode.success.code)); + verifyNever(() => logger.info(updatePrompt)); + }, + ); test('does not show update message when using update command', () async { when( @@ -89,14 +91,12 @@ void main() { verifyNever(() => logger.info(updatePrompt)); }); - test( - 'can be instantiated without an explicit analytics/logger instance', - () { - final commandRunner = FlutterCompileCommandRunner(); - expect(commandRunner, isNotNull); - expect(commandRunner, isA>()); - }, - ); + test('can be instantiated without an explicit analytics/logger instance', + () { + final commandRunner = FlutterCompileCommandRunner(); + expect(commandRunner, isNotNull); + expect(commandRunner, isA>()); + }); test('handles FormatException', () async { const exception = FormatException('oops!'); diff --git a/test/src/commands/config_command_test.dart b/test/src/commands/config_command_test.dart index d07fe6d..1354cc1 100644 --- a/test/src/commands/config_command_test.dart +++ b/test/src/commands/config_command_test.dart @@ -55,12 +55,9 @@ void main() { tearDown(tempHome.tearDown); test('config set writes value and config get reads it back', () async { - var result = await commandRunner.run([ - 'config', - 'set', - 'engine', - '/tmp/engine', - ]); + var result = await commandRunner.run( + ['config', 'set', 'engine', '/tmp/engine'], + ); expect(result, equals(ExitCode.success.code)); verify(() => logger.info('Set engine_path:/tmp/engine')).called(1); @@ -90,7 +87,9 @@ void main() { test('config list --json outputs JSON', () async { await commandRunner.run(['config', 'set', 'flutter', '/tmp/flutter']); await commandRunner.run(['config', 'list', '--json']); - verify(() => logger.info('{"flutter_path":"/tmp/flutter"}')).called(1); + verify( + () => logger.info('{"flutter_path":"/tmp/flutter"}'), + ).called(1); }); test('config set with no args returns usage', () async { diff --git a/test/src/commands/sdk_command_test.dart b/test/src/commands/sdk_command_test.dart index f26f620..4e0e9b1 100644 --- a/test/src/commands/sdk_command_test.dart +++ b/test/src/commands/sdk_command_test.dart @@ -74,7 +74,9 @@ void main() { test('sdk remove with no version arg returns usage exit code', () async { final result = await commandRunner.run(['sdk', 'remove']); expect(result, equals(ExitCode.usage.code)); - verify(() => logger.err('Please specify a version to remove.')).called(1); + verify( + () => logger.err('Please specify a version to remove.'), + ).called(1); }); }); } diff --git a/test/src/commands/sdk_exec_command_test.dart b/test/src/commands/sdk_exec_command_test.dart index 1e395cd..fb40349 100644 --- a/test/src/commands/sdk_exec_command_test.dart +++ b/test/src/commands/sdk_exec_command_test.dart @@ -29,7 +29,9 @@ void main() { test('no args returns usage exit code', () async { final result = await commandRunner.run(['sdk', 'exec']); expect(result, equals(ExitCode.usage.code)); - verify(() => logger.err('Please specify a command to run.')).called(1); + verify( + () => logger.err('Please specify a command to run.'), + ).called(1); }); test('no SDK configured returns usage exit code', () async { diff --git a/test/src/commands/sdk_global_command_test.dart b/test/src/commands/sdk_global_command_test.dart index 8141b47..165c713 100644 --- a/test/src/commands/sdk_global_command_test.dart +++ b/test/src/commands/sdk_global_command_test.dart @@ -33,11 +33,8 @@ void main() { }); test('non-installed version returns usage exit code', () async { - final result = await commandRunner.run([ - 'sdk', - 'global', - 'non_existent_version', - ]); + final result = + await commandRunner.run(['sdk', 'global', 'non_existent_version']); expect(result, equals(ExitCode.usage.code)); verify( () => logger.err( diff --git a/test/src/commands/sdk_install_test.dart b/test/src/commands/sdk_install_test.dart index 62b9946..d4cd04e 100644 --- a/test/src/commands/sdk_install_test.dart +++ b/test/src/commands/sdk_install_test.dart @@ -43,51 +43,45 @@ void main() { expect(F.isSdkInstalled('3.19.0'), isTrue); }); - test( - 'cloneRepository skips when valid git repo exists and force=false', - () async { - final dir = Directory('${tempHome.path}/repo'); - Directory('${dir.path}/.git').createSync(recursive: true); - File( - '${dir.path}/.git/HEAD', - ).writeAsStringSync('ref: refs/heads/main\n'); + test('cloneRepository skips when valid git repo exists and force=false', + () async { + final dir = Directory('${tempHome.path}/repo'); + Directory('${dir.path}/.git').createSync(recursive: true); + File('${dir.path}/.git/HEAD').writeAsStringSync('ref: refs/heads/main\n'); - await F.cloneRepository('https://example.com/repo.git', dir.path); - // Should have skipped — verify the info message - verify( - () => logger.info( - 'Directory ${dir.path} already exists. Skipping clone.', - ), - ).called(1); - }, - ); + await F.cloneRepository('https://example.com/repo.git', dir.path); + // Should have skipped — verify the info message + verify( + () => logger.info( + 'Directory ${dir.path} already exists. Skipping clone.', + ), + ).called(1); + }); - test( - 'cloneRepository cleans up invalid repo directory and re-clones', - () async { - final dir = Directory('${tempHome.path}/repo'); - dir.createSync(); - // No .git/HEAD — invalid repo + test('cloneRepository cleans up invalid repo directory and re-clones', + () async { + final dir = Directory('${tempHome.path}/repo'); + dir.createSync(); + // No .git/HEAD — invalid repo - // The actual clone will fail because the URL is fake, - // but we verify the cleanup-and-reclone path was taken - try { - await F.cloneRepository( - 'https://invalid.example.com/repo.git', - dir.path, - ); - } catch (_) { - // Expected: git clone fails for invalid URL - } + // The actual clone will fail because the URL is fake, + // but we verify the cleanup-and-reclone path was taken + try { + await F.cloneRepository( + 'https://invalid.example.com/repo.git', + dir.path, + ); + } catch (_) { + // Expected: git clone fails for invalid URL + } - // The invalid directory should have been cleaned up by the catch block - verify( - () => logger.info( - 'Directory ${dir.path} exists but is not a valid git repo. ' - 'Cleaning up and re-cloning...', - ), - ).called(1); - }, - ); + // The invalid directory should have been cleaned up by the catch block + verify( + () => logger.info( + 'Directory ${dir.path} exists but is not a valid git repo. ' + 'Cleaning up and re-cloning...', + ), + ).called(1); + }); }); } diff --git a/test/src/commands/sdk_use_command_test.dart b/test/src/commands/sdk_use_command_test.dart index f7161af..7b89e64 100644 --- a/test/src/commands/sdk_use_command_test.dart +++ b/test/src/commands/sdk_use_command_test.dart @@ -33,11 +33,8 @@ void main() { }); test('non-installed version returns usage exit code', () async { - final result = await commandRunner.run([ - 'sdk', - 'use', - 'non_existent_version', - ]); + final result = + await commandRunner.run(['sdk', 'use', 'non_existent_version']); expect(result, equals(ExitCode.usage.code)); verify( () => logger.err( diff --git a/test/src/commands/status_command_test.dart b/test/src/commands/status_command_test.dart index f54eb60..f82e2d3 100644 --- a/test/src/commands/status_command_test.dart +++ b/test/src/commands/status_command_test.dart @@ -79,7 +79,9 @@ void main() { final result = await commandRunner.run(['status']); expect(result, equals(ExitCode.success.code)); verify(() => logger.info('Engine path: $enginePath')).called(1); - verify(() => logger.info('Available builds: (none)')).called(1); + verify( + () => logger.info('Available builds: (none)'), + ).called(1); }); }); } diff --git a/test/src/commands/update_command_test.dart b/test/src/commands/update_command_test.dart index 2309054..e320504 100644 --- a/test/src/commands/update_command_test.dart +++ b/test/src/commands/update_command_test.dart @@ -52,43 +52,49 @@ void main() { expect(command, isNotNull); }); - test('handles pub latest version query errors', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }); - - test('handles pub update errors', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenThrow(Exception('oops')); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.software.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.err('Exception: oops')); - verify( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).called(1); - }); + test( + 'handles pub latest version query errors', + () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenThrow(Exception('oops')); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.software.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.err('Exception: oops')); + verifyNever( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ); + }, + ); + + test( + 'handles pub update errors', + () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + when( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).thenThrow(Exception('oops')); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.software.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.err('Exception: oops')); + verify( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).called(1); + }, + ); test('handles pub update process errors', () async { const error = 'Oh no! Installing this is not possible right now!'; @@ -117,48 +123,54 @@ void main() { ).called(1); }); - test('updates when newer version exists', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => latestVersion); - when( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ).thenAnswer( - (_) async => ProcessResult(0, ExitCode.success.code, null, null), - ); - when(() => logger.progress(any())).thenReturn(MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify(() => logger.progress('Checking for updates')).called(1); - verify(() => logger.progress('Updating to $latestVersion')).called(1); - verify( - () => pubUpdater.update( - packageName: packageName, - versionConstraint: latestVersion, - ), - ).called(1); - }); - - test('does not update when already on latest version', () async { - when( - () => pubUpdater.getLatestVersion(any()), - ).thenAnswer((_) async => packageVersion); - when(() => logger.progress(any())).thenReturn(MockProgress()); - final result = await commandRunner.run(['update']); - expect(result, equals(ExitCode.success.code)); - verify( - () => logger.info('CLI is already at the latest version.'), - ).called(1); - verifyNever(() => logger.progress('Updating to $latestVersion')); - verifyNever( - () => pubUpdater.update( - packageName: any(named: 'packageName'), - versionConstraint: any(named: 'versionConstraint'), - ), - ); - }); + test( + 'updates when newer version exists', + () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => latestVersion); + when( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ).thenAnswer( + (_) async => ProcessResult(0, ExitCode.success.code, null, null), + ); + when(() => logger.progress(any())).thenReturn(MockProgress()); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.success.code)); + verify(() => logger.progress('Checking for updates')).called(1); + verify(() => logger.progress('Updating to $latestVersion')).called(1); + verify( + () => pubUpdater.update( + packageName: packageName, + versionConstraint: latestVersion, + ), + ).called(1); + }, + ); + + test( + 'does not update when already on latest version', + () async { + when( + () => pubUpdater.getLatestVersion(any()), + ).thenAnswer((_) async => packageVersion); + when(() => logger.progress(any())).thenReturn(MockProgress()); + final result = await commandRunner.run(['update']); + expect(result, equals(ExitCode.success.code)); + verify( + () => logger.info('CLI is already at the latest version.'), + ).called(1); + verifyNever(() => logger.progress('Updating to $latestVersion')); + verifyNever( + () => pubUpdater.update( + packageName: any(named: 'packageName'), + versionConstraint: any(named: 'versionConstraint'), + ), + ); + }, + ); }); } diff --git a/test/src/daemon/daemon_peer_test.dart b/test/src/daemon/daemon_peer_test.dart index 3964616..42050ce 100644 --- a/test/src/daemon/daemon_peer_test.dart +++ b/test/src/daemon/daemon_peer_test.dart @@ -28,8 +28,10 @@ void main() { /// Returns (clientChannel, daemonChannel) where: /// - clientChannel is used by the test to send/receive JSON-RPC /// - daemonChannel is used by the DaemonPeer - ({StreamChannel client, StreamChannel daemon}) - createChannelPair() { + ({ + StreamChannel client, + StreamChannel daemon, + }) createChannelPair() { final clientToServer = StreamController(); final serverToClient = StreamController(); @@ -48,14 +50,21 @@ void main() { test('version returns current package version', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); // Start peer in background unawaited(peer.start()); // Skip the daemon.connected notification // Read until we get a proper response - final request = {'jsonrpc': '2.0', 'method': 'version', 'id': 1}; + final request = { + 'jsonrpc': '2.0', + 'method': 'version', + 'id': 1, + }; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -67,17 +76,28 @@ void main() { } // Shutdown - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); test('sdk.list returns list shape', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); unawaited(peer.start()); - final request = {'jsonrpc': '2.0', 'method': 'sdk.list', 'id': 1}; + final request = { + 'jsonrpc': '2.0', + 'method': 'sdk.list', + 'id': 1, + }; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -88,17 +108,28 @@ void main() { } } - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); test('sdk.global.get returns null with no config', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); unawaited(peer.start()); - final request = {'jsonrpc': '2.0', 'method': 'sdk.global.get', 'id': 1}; + final request = { + 'jsonrpc': '2.0', + 'method': 'sdk.global.get', + 'id': 1, + }; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -109,17 +140,28 @@ void main() { } } - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); test('config.list returns empty map with no config', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); unawaited(peer.start()); - final request = {'jsonrpc': '2.0', 'method': 'config.list', 'id': 1}; + final request = { + 'jsonrpc': '2.0', + 'method': 'config.list', + 'id': 1, + }; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -131,13 +173,20 @@ void main() { } } - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); test('config.set writes to rc file', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); unawaited(peer.start()); @@ -160,17 +209,28 @@ void main() { } } - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); test('shutdown closes peer', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); final peerFuture = peer.start(); - final request = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 1}; + final request = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 1, + }; channels.client.sink.add(json.encode(request)); await for (final line in channels.client.stream) { @@ -187,7 +247,10 @@ void main() { test('invalid method returns JSON-RPC error', () async { final channels = createChannelPair(); - final peer = DaemonPeer(logger: logger, channel: channels.daemon); + final peer = DaemonPeer( + logger: logger, + channel: channels.daemon, + ); unawaited(peer.start()); @@ -206,7 +269,11 @@ void main() { } } - final shutdownReq = {'jsonrpc': '2.0', 'method': 'shutdown', 'id': 2}; + final shutdownReq = { + 'jsonrpc': '2.0', + 'method': 'shutdown', + 'id': 2, + }; channels.client.sink.add(json.encode(shutdownReq)); }); }); diff --git a/test/src/daemon/file_watcher_test.dart b/test/src/daemon/file_watcher_test.dart index 1d08369..d90a07f 100644 --- a/test/src/daemon/file_watcher_test.dart +++ b/test/src/daemon/file_watcher_test.dart @@ -34,9 +34,8 @@ void main() { watchedFile.writeAsStringSync('changed'); - final result = await completer.future.timeout( - const Duration(seconds: 15), - ); + final result = + await completer.future.timeout(const Duration(seconds: 15)); expect(result, equals(watchedFile.path)); watcher.stop(); diff --git a/test/src/shared/codepush_build_service_test.dart b/test/src/shared/codepush_build_service_test.dart index 4dec4e2..45c803f 100644 --- a/test/src/shared/codepush_build_service_test.dart +++ b/test/src/shared/codepush_build_service_test.dart @@ -45,10 +45,8 @@ void main() { }); group('validatePayloadMagic', () { - List mk(List header, [int tailLen = 32]) => [ - ...header, - ...List.filled(tailLen, 0x00), - ]; + List mk(List header, [int tailLen = 32]) => + [...header, ...List.filled(tailLen, 0x00)]; const iosHeader = [0x33, 0x43, 0x42, 0x44]; const macHeaderLe = [0xCF, 0xFA, 0xED, 0xFE]; @@ -103,10 +101,10 @@ void main() { }); test('rejects short payloads', () { - final err = CodePushBuildService.validatePayloadMagic([ - 0x90, - 0xAB, - ], 'ios'); + final err = CodePushBuildService.validatePayloadMagic( + [0x90, 0xAB], + 'ios', + ); expect(err, isNotNull); expect(err, contains('too small')); }); @@ -114,8 +112,7 @@ void main() { group('parseFlutterVersionOutput', () { test('extracts stable channel version', () { - const output = - 'Flutter 3.41.2 • channel stable • https://github.com/' + const output = 'Flutter 3.41.2 • channel stable • https://github.com/' 'flutter/flutter.git\n' 'Framework • revision abc123 (3 days ago)\n'; expect( @@ -137,28 +134,31 @@ void main() { }); test('returns null when first line does not start with Flutter', () { - const output = - 'Downloading Flutter SDK...\n' + const output = 'Downloading Flutter SDK...\n' 'Flutter 3.41.2 • channel stable\n'; - expect(CodePushBuildService.parseFlutterVersionOutput(output), isNull); + expect( + CodePushBuildService.parseFlutterVersionOutput(output), + isNull, + ); }); test('tolerates trailing whitespace on the first line', () { const output = 'Flutter 3.5.0 \n'; - expect(CodePushBuildService.parseFlutterVersionOutput(output), '3.5.0'); + expect( + CodePushBuildService.parseFlutterVersionOutput(output), + '3.5.0', + ); }); }); group('resolveFlutterVersion', () { - test( - 'explicit value short-circuits detection and stored config', - () async { - final resolved = await service.resolveFlutterVersion( - explicit: '3.29.1', - ); - expect(resolved, '3.29.1'); - }, - ); + test('explicit value short-circuits detection and stored config', + () async { + final resolved = await service.resolveFlutterVersion( + explicit: '3.29.1', + ); + expect(resolved, '3.29.1'); + }); test('empty explicit is treated as absent and falls through', () async { // We cannot assert the downstream result without mocking @@ -171,19 +171,21 @@ void main() { group('finalizeBuild', () { test( - 'ios is a no-op because the patched engine must be present at build time', - () async { - final result = await service.finalizeBuild( - buildPlatform: 'ios', - flutterVersion: '3.41.2', - artifactManager: CodePushArtifactManager(logger: logger), - ); + 'ios is a no-op because the patched engine must be present at build time', + () async { + final result = await service.finalizeBuild( + buildPlatform: 'ios', + flutterVersion: '3.41.2', + artifactManager: CodePushArtifactManager(logger: logger), + ); - expect(result.success, isTrue); - expect(result.message, 'iOS build already finalized during prepare.'); - expect(result.command, isNull); - }, - ); + expect(result.success, isTrue); + expect( + result.message, + 'iOS build already finalized during prepare.', + ); + expect(result.command, isNull); + }); }); }); diff --git a/test/src/shared/constants_test.dart b/test/src/shared/constants_test.dart index a547df3..10eb254 100644 --- a/test/src/shared/constants_test.dart +++ b/test/src/shared/constants_test.dart @@ -103,7 +103,10 @@ void main() { Constants.platformSdkPATHExport, equals(Constants.sdkPATHExport), ); - expect(Constants.platformRestartShell, equals(Constants.restartShell)); + expect( + Constants.platformRestartShell, + equals(Constants.restartShell), + ); } }); }); @@ -117,10 +120,8 @@ void main() { }); test('FlutterMode has normal and compiled', () { - expect( - FlutterMode.values, - containsAll([FlutterMode.normal, FlutterMode.compiled]), - ); + expect(FlutterMode.values, + containsAll([FlutterMode.normal, FlutterMode.compiled])); }); }); diff --git a/test/src/shared/functions_test.dart b/test/src/shared/functions_test.dart index e762eab..312ce43 100644 --- a/test/src/shared/functions_test.dart +++ b/test/src/shared/functions_test.dart @@ -76,9 +76,8 @@ void main() { test('returns true for directory with .git/HEAD', () { Directory('${tempDir.path}/.git').createSync(); - File( - '${tempDir.path}/.git/HEAD', - ).writeAsStringSync('ref: refs/heads/main\n'); + File('${tempDir.path}/.git/HEAD') + .writeAsStringSync('ref: refs/heads/main\n'); expect(F.isValidGitRepo(tempDir.path), isTrue); }); }); diff --git a/test/src/shared/ios_baseline_plist_test.dart b/test/src/shared/ios_baseline_plist_test.dart index 5cbc4a1..2389554 100644 --- a/test/src/shared/ios_baseline_plist_test.dart +++ b/test/src/shared/ios_baseline_plist_test.dart @@ -9,11 +9,9 @@ void main() { final baselineId = generateBaselineId(); expect( baselineId, - matches( - RegExp( - r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', - ), - ), + matches(RegExp( + r'^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$', + )), ); }); @@ -47,10 +45,9 @@ void main() { ); }); - test( - 'inserts a new FCPBaselineId key and can restore original content', - () { - const originalContent = ''' + test('inserts a new FCPBaselineId key and can restore original content', + () { + const originalContent = ''' @@ -59,25 +56,24 @@ void main() { '''; - File(plistPath).writeAsStringSync(originalContent); + File(plistPath).writeAsStringSync(originalContent); - final original = writeBaselineIdToIosInfoPlist( - '12345678-1234-4234-8234-123456789abc', - plistPath: plistPath, - ); + final original = writeBaselineIdToIosInfoPlist( + '12345678-1234-4234-8234-123456789abc', + plistPath: plistPath, + ); - expect(original, originalContent); - final updated = File(plistPath).readAsStringSync(); - expect(updated, contains('FCPBaselineId')); - expect( - updated, - contains('12345678-1234-4234-8234-123456789abc'), - ); + expect(original, originalContent); + final updated = File(plistPath).readAsStringSync(); + expect(updated, contains('FCPBaselineId')); + expect( + updated, + contains('12345678-1234-4234-8234-123456789abc'), + ); - restoreIosInfoPlist(original!, plistPath: plistPath); - expect(File(plistPath).readAsStringSync(), originalContent); - }, - ); + restoreIosInfoPlist(original!, plistPath: plistPath); + expect(File(plistPath).readAsStringSync(), originalContent); + }); test('replaces an existing FCPBaselineId value', () { const originalContent = ''' diff --git a/test/src/shared/ios_patch_entry_target_test.dart b/test/src/shared/ios_patch_entry_target_test.dart index ba07e46..371882e 100644 --- a/test/src/shared/ios_patch_entry_target_test.dart +++ b/test/src/shared/ios_patch_entry_target_test.dart @@ -47,25 +47,23 @@ Object? codePushPatch() => 1; ); }); - test( - 'findCodePushPatchSourceCandidates ignores invocation-only wrappers', - () { - File('lib/code_push_local_patch.dart').writeAsStringSync(''' + test('findCodePushPatchSourceCandidates ignores invocation-only wrappers', + () { + File('lib/code_push_local_patch.dart').writeAsStringSync(''' import 'screens/home_screen.dart'; @pragma('dyn-module:entry-point') Object? main() => codePushPatch(); '''); - File('lib/screens/home_screen.dart').writeAsStringSync(''' + File('lib/screens/home_screen.dart').writeAsStringSync(''' Object? codePushPatch() => 1; '''); - expect( - findCodePushPatchSourceCandidates(), - equals(['lib/screens/home_screen.dart']), - ); - }, - ); + expect( + findCodePushPatchSourceCandidates(), + equals(['lib/screens/home_screen.dart']), + ); + }); test('findCodePushPatchSourceCandidates returns sorted matches', () { File('lib/b.dart').writeAsStringSync('Object? codePushPatch() => 1;'); From 77c7c133d49f9fe85a6e56ca987594e80675dc2a Mon Sep 17 00:00:00 2001 From: fonkamloic Date: Fri, 12 Jun 2026 00:39:38 -0400 Subject: [PATCH 7/7] style: format the five files CI flags (Dart 3.12.2 with resolved package config) --- .../codepush_commands/_codepush_login.dart | 6 ++-- .../codepush_commands/_codepush_patch.dart | 18 ++++------ lib/src/shared/codepush_artifact_manager.dart | 34 +++++++++---------- lib/src/shared/codepush_build_service.dart | 22 +++++------- .../shared/codepush_archive_service_test.dart | 28 ++++++--------- 5 files changed, 44 insertions(+), 64 deletions(-) diff --git a/lib/src/commands/codepush_commands/_codepush_login.dart b/lib/src/commands/codepush_commands/_codepush_login.dart index cf8be60..7dfa9e6 100644 --- a/lib/src/commands/codepush_commands/_codepush_login.dart +++ b/lib/src/commands/codepush_commands/_codepush_login.dart @@ -15,8 +15,7 @@ class CodePushLoginSubCommand extends Command { ) ..addOption( 'server', - help: - 'Code push server URL. Defaults to the value stored in ' + help: 'Code push server URL. Defaults to the value stored in ' '~/.flutter_compilerc (set by previous login or ' '`fcp codepush config --server`), or ' '${Constants.codePushDefaultServer} if unset.', @@ -32,8 +31,7 @@ class CodePushLoginSubCommand extends Command { @override Future run() async { - final serverUrl = - (argResults?['server'] as String?) ?? + final serverUrl = (argResults?['server'] as String?) ?? await CodePushClient.getServerUrl(); // If --api-key provided, use the old direct login flow. diff --git a/lib/src/commands/codepush_commands/_codepush_patch.dart b/lib/src/commands/codepush_commands/_codepush_patch.dart index e922881..9e94bf4 100644 --- a/lib/src/commands/codepush_commands/_codepush_patch.dart +++ b/lib/src/commands/codepush_commands/_codepush_patch.dart @@ -30,8 +30,7 @@ class CodePushPatchSubCommand extends Command { ) ..addMultiOption( 'dart-define', - help: - 'Additional --dart-define values to forward to flutter build ' + help: 'Additional --dart-define values to forward to flutter build ' 'when --build is used. Repeat for multiple values.', ) ..addOption( @@ -59,8 +58,7 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'flutter-version', - help: - 'Flutter SDK version this patch was built with (e.g., 3.41.2). ' + help: 'Flutter SDK version this patch was built with (e.g., 3.41.2). ' 'Auto-detected from "flutter --version" if not specified, ' 'then falls back to codepush_engine_flutter_version in ' '~/.flutter_compilerc. Required by the finalize step to locate ' @@ -68,8 +66,7 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'package-prefix', - help: - 'iOS only. Package URI prefix identifying the user\'s ' + help: 'iOS only. Package URI prefix identifying the user\'s ' 'app code (e.g. `package:fcptest/`). Only libraries ' 'whose URIs start with this prefix get compiled into the ' 'patch; everything else (Flutter framework, pub deps) ' @@ -78,22 +75,19 @@ class CodePushPatchSubCommand extends Command { ) ..addOption( 'patch-entry-file', - help: - 'iOS only. Path to the Dart file under `lib/` that defines ' + help: 'iOS only. Path to the Dart file under `lib/` that defines ' '`codePushPatch()`. If omitted, flutter_compile scans `lib/` ' 'and requires exactly one matching source file.', ) ..addFlag( 'swap-mode', - help: - 'iOS only. Alternative patch generation mode that ' + help: 'iOS only. Alternative patch generation mode that ' 'enables runtime function replacement. Default: off.', negatable: false, ) ..addMultiOption( 'include-uri', - help: - 'iOS only. Additional library URI to include in the ' + help: 'iOS only. Additional library URI to include in the ' 'bytecode module (repeatable). For patch-side helper ' 'libraries not discovered automatically.', ); diff --git a/lib/src/shared/codepush_artifact_manager.dart b/lib/src/shared/codepush_artifact_manager.dart index 8f3eb2c..160d855 100644 --- a/lib/src/shared/codepush_artifact_manager.dart +++ b/lib/src/shared/codepush_artifact_manager.dart @@ -16,12 +16,11 @@ class CodePushArtifactManager { String? baseUrl, String? cacheRoot, HttpClient Function()? httpClientFactory, - }) : _logger = logger, - _baseUrl = baseUrl ?? _defaultArtifactBucketBase, - _cacheRoot = - cacheRoot ?? - '${Platform.environment['HOME'] ?? '/tmp'}/.flutter_compile/cache/${Constants.codePushCacheDir}', - _httpClientFactory = httpClientFactory ?? HttpClient.new; + }) : _logger = logger, + _baseUrl = baseUrl ?? _defaultArtifactBucketBase, + _cacheRoot = cacheRoot ?? + '${Platform.environment['HOME'] ?? '/tmp'}/.flutter_compile/cache/${Constants.codePushCacheDir}', + _httpClientFactory = httpClientFactory ?? HttpClient.new; /// Artifact server base URL for engine binaries. static const String _defaultArtifactBucketBase = @@ -79,10 +78,10 @@ class CodePushArtifactManager { final os = Platform.isMacOS ? 'darwin' : Platform.isLinux - ? 'linux' - : Platform.isWindows - ? 'windows' - : 'unknown'; + ? 'linux' + : Platform.isWindows + ? 'windows' + : 'unknown'; final arch = Platform.version.contains('arm64') ? 'arm64' : 'x64'; return '$os-$arch'; } @@ -209,8 +208,8 @@ class CodePushArtifactManager { } const iosTarget = 'ios-arm64'; - final overrideDirPath = Platform.environment['FCP_CODEPUSH_IOS_ENGINE_DIR'] - ?.trim(); + final overrideDirPath = + Platform.environment['FCP_CODEPUSH_IOS_ENGINE_DIR']?.trim(); final usingLocalOverride = overrideDirPath != null && overrideDirPath.isNotEmpty; final iosDir = Directory(platformDir(flutterVersion, iosTarget)); @@ -518,12 +517,11 @@ class CodePushArtifactManager { .whereType() .where((d) => File('${d.path}/.stamp').existsSync()) .map((d) { - final name = d.uri.pathSegments.where((s) => s.isNotEmpty).last; - return name.startsWith('flutter-') - ? name.substring('flutter-'.length) - : name; - }) - .toList() + final name = d.uri.pathSegments.where((s) => s.isNotEmpty).last; + return name.startsWith('flutter-') + ? name.substring('flutter-'.length) + : name; + }).toList() ..sort(); } diff --git a/lib/src/shared/codepush_build_service.dart b/lib/src/shared/codepush_build_service.dart index 00c0638..892b21e 100644 --- a/lib/src/shared/codepush_build_service.dart +++ b/lib/src/shared/codepush_build_service.dart @@ -206,8 +206,7 @@ class CodePushBuildService { if (flutterRoot != null) { final engineCache = '$flutterRoot/bin/cache/artifacts/engine'; sdkGenSnapshotPath = '$engineCache/ios-release/gen_snapshot_arm64'; - sdkFrameworkPath = - '$engineCache/ios-release/' + sdkFrameworkPath = '$engineCache/ios-release/' 'Flutter.xcframework/ios-arm64/Flutter.framework/Flutter'; expectedGenSnapshotSha = _sha256OfFile(File(sdkGenSnapshotPath)); @@ -561,11 +560,11 @@ class CodePushBuildService { _logger.err('Flutter version is required to prepare an iOS fcp build.'); return false; } - final overlaysInstalled = await artifactManager - .installOverlaysIntoFlutterSdk( - flutterVersion: flutterVersion, - platform: buildPlatform, - ); + final overlaysInstalled = + await artifactManager.installOverlaysIntoFlutterSdk( + flutterVersion: flutterVersion, + platform: buildPlatform, + ); if (!overlaysInstalled) { _logger.err( 'Failed to install the iOS fcp engine overlays into the active Flutter SDK.', @@ -609,8 +608,7 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: - 'Build tool not available. ' + message: 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } @@ -660,8 +658,7 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: - 'Build tool not available. ' + message: 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } @@ -716,8 +713,7 @@ class CodePushBuildService { if (tool == null) { return const BuildStepResult( success: false, - message: - 'Build tool not available. ' + message: 'Build tool not available. ' 'Run "fcp codepush setup" first to download it.', ); } diff --git a/test/src/shared/codepush_archive_service_test.dart b/test/src/shared/codepush_archive_service_test.dart index 5599261..54a3d6c 100644 --- a/test/src/shared/codepush_archive_service_test.dart +++ b/test/src/shared/codepush_archive_service_test.dart @@ -105,11 +105,9 @@ void main() { isTrue, ); - final manifest = - jsonDecode( - File('${releaseDir.path}/manifest.json').readAsStringSync(), - ) - as Map; + final manifest = jsonDecode( + File('${releaseDir.path}/manifest.json').readAsStringSync(), + ) as Map; expect(manifest['archive_format_version'], 1); expect(manifest['release_id'], 'rel-2'); expect(manifest['baseline_id'], 'base-2'); @@ -138,11 +136,9 @@ void main() { Directory('${releaseDir.path}/Runner.app.dSYM').existsSync(), isTrue, ); - final manifest = - jsonDecode( - File('${releaseDir.path}/manifest.json').readAsStringSync(), - ) - as Map; + final manifest = jsonDecode( + File('${releaseDir.path}/manifest.json').readAsStringSync(), + ) as Map; expect(manifest['has_dsym'], isTrue); }); @@ -253,13 +249,11 @@ void main() { fcpVersion: '0.0.0', ); - final manifest = - jsonDecode( - File( - '${projectDir.path}/.fcp-archive/rel-9/manifest.json', - ).readAsStringSync(), - ) - as Map; + final manifest = jsonDecode( + File( + '${projectDir.path}/.fcp-archive/rel-9/manifest.json', + ).readAsStringSync(), + ) as Map; // Pre-computed sha256 of the literal bytes. expect( manifest['framework_sha256'],