diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78dbe77e..cf7bbfed 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,14 +21,10 @@ concurrency: jobs: test: - name: Test on ${{ matrix.os }} + name: Test timeout-minutes: 30 - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - - steps: + runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v4 @@ -37,35 +33,57 @@ jobs: with: sdk-version: ${{ github.event.inputs.sdk-version }} - # - name: Install DCM - # uses: CQLabs/setup-dcm@v1 - # with: - # github_token: ${{ secrets.GITHUB_TOKEN }} - - # - name: Run DCM - # uses: CQLabs/dcm-action@v1 - # with: - # github_token: ${{ secrets.GITHUB_TOKEN }} - # folders: lib + - uses: invertase/github-action-dart-analyzer@v1 + with: + fatal-infos: false - - - name: Tests - uses: ./.github/actions/test - if: matrix.os != 'ubuntu-latest' - + - name: Install DCM + uses: CQLabs/setup-dcm@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + + - name: Run DCM + uses: CQLabs/dcm-action@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + ci_key: oss + email: oss + folders: . + fatal_style: true + fatal_performance: true + fatal_warnings: true - name: Install lcov - run: sudo apt-get install lcov - if: matrix.os == 'ubuntu-latest' + run: sudo apt-get install lcov - name: Run tests uses: ./.github/actions/test - if: matrix.os == 'ubuntu-latest' with: with-coverage: 'true' - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 - if: matrix.os == 'ubuntu-latest' env: - CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + + test-os: + name: Test on ${{ matrix.os }} + needs: test + timeout-minutes: 30 + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, windows-latest] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Prepare environment + uses: ./.github/actions/prepare + with: + sdk-version: ${{ github.event.inputs.sdk-version }} + + - name: Tests + uses: ./.github/actions/test \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 7bb30add..08f47515 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -25,15 +25,17 @@ dart_code_metrics: only-in-src: true prefer-match-file-name: false prefer-correct-callback-field-name: false - # Remove later when cleaning up - prefer-single-widget-per-file: false - # End of remove later match-getter-setter-field-names: false + avoid-duplicate-cascades: false prefer-dedicated-media-query-methods: false avoid-shadowing: false + avoid-duplicate-initializers: false enum-constants-ordering: false + avoid-accessing-collections-by-constant-index: false avoid-unsafe-collection-methods: false + move-variable-closer-to-its-usage: false prefer-prefixed-global-constants: false + avoid-nullable-interpolation: false avoid-returning-widgets: false arguments-ordering: child-last: true diff --git a/bin/compile.dart b/bin/compile.dart index c9637c1a..0073fb95 100644 --- a/bin/compile.dart +++ b/bin/compile.dart @@ -10,6 +10,7 @@ Future main() async { if (os != 'macos' && os != 'linux') { print('Unsupported OS. Only MacOS and Linux are supported.'); + return; } @@ -28,6 +29,7 @@ Future main() async { // Error checking for compile process if (compileResult.exitCode != 0) { print('Error occurred in compilation:\n ${compileResult.stderr}'); + return; } diff --git a/lib/src/commands/base_command.dart b/lib/src/commands/base_command.dart index f4f97706..ef25a0cc 100644 --- a/lib/src/commands/base_command.dart +++ b/lib/src/commands/base_command.dart @@ -16,10 +16,12 @@ abstract class BaseCommand extends Command { if (arg == 'null' || (arg == null || arg.isEmpty)) { return null; } + return arg; } /// Gets the parsed command-line option named [name] as `List`. + // ignore: prefer-correct-json-casts List stringsArg(String name) => argResults![name] as List; @override String get invocation => 'fvm $name'; diff --git a/lib/src/commands/config_command.dart b/lib/src/commands/config_command.dart index 049e729b..864a08d0 100644 --- a/lib/src/commands/config_command.dart +++ b/lib/src/commands/config_command.dart @@ -1,10 +1,10 @@ -import 'package:fvm/constants.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/services/config_repository.dart'; -import 'package:fvm/src/services/logger_service.dart'; import 'package:io/ansi.dart'; import 'package:io/io.dart'; +import '../models/config_model.dart'; +import '../services/config_repository.dart'; +import '../services/logger_service.dart'; +import '../utils/constants.dart'; import '../utils/context.dart'; import 'base_command.dart'; @@ -22,8 +22,8 @@ class ConfigCommand extends BaseCommand { argParser.addFlag( 'update-check', help: 'Checks if there is a new version of $kPackageName available.', - negatable: true, defaultsTo: true, + negatable: true, ); } @override diff --git a/lib/src/commands/dart_command.dart b/lib/src/commands/dart_command.dart index c6fb5dc1..e0ddd4a1 100644 --- a/lib/src/commands/dart_command.dart +++ b/lib/src/commands/dart_command.dart @@ -1,9 +1,10 @@ import 'package:args/args.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/utils/commands.dart'; +import '../models/cache_flutter_version_model.dart'; import '../services/logger_service.dart'; +import '../services/project_service.dart'; +import '../utils/commands.dart'; +import '../utils/constants.dart'; import '../workflows/ensure_cache.workflow.dart'; import 'base_command.dart'; @@ -40,6 +41,7 @@ class DartCommand extends BaseCommand { // Running null will default to dart version on path } final results = await runDart(args, version: cacheVersion); + return results.exitCode; } } diff --git a/lib/src/commands/doctor_command.dart b/lib/src/commands/doctor_command.dart index be817b9a..d50eb1de 100644 --- a/lib/src/commands/doctor_command.dart +++ b/lib/src/commands/doctor_command.dart @@ -2,16 +2,18 @@ import 'dart:convert'; import 'dart:io'; import 'package:dart_console/dart_console.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/exceptions.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/utils/console_utils.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/which.dart'; import 'package:io/io.dart'; import 'package:path/path.dart'; +import '../models/config_model.dart'; +import '../models/project_model.dart'; import '../services/logger_service.dart'; +import '../services/project_service.dart'; +import '../utils/console_utils.dart'; +import '../utils/constants.dart'; +import '../utils/context.dart'; +import '../utils/exceptions.dart'; +import '../utils/which.dart'; import 'base_command.dart'; /// Information about fvm environment @@ -38,9 +40,9 @@ class DoctorCommand extends BaseCommand { ['Is Flutter Project', project.isFlutter ? 'Yes' : 'No'], [ 'Dart Tool Generator Version', - project.dartToolGeneratorVersion ?? 'Not available', + project.dartToolGeneratorVersion ?? 'N/A', ], - ['Dart tool version', project.dartToolVersion ?? 'Not available'], + ['Dart tool version', project.dartToolVersion ?? 'N/A'], ['.gitignore Present', project.gitignoreFile.existsSync() ? 'Yes' : 'No'], ['Config Present', project.hasConfig ? 'Yes' : 'No'], ['Pinned Version', project.pinnedVersion ?? 'None'], @@ -68,10 +70,10 @@ class DoctorCommand extends BaseCommand { table.insertRow([kVsCode]); // Check for .vscode directory final vscodeDir = Directory(join(project.path, '.vscode')); - final settingsPath = join(vscodeDir.path, 'settings.json'); - final settingsFile = File(settingsPath); if (vscodeDir.existsSync()) { + final settingsPath = join(vscodeDir.path, 'settings.json'); + final settingsFile = File(settingsPath); if (settingsFile.existsSync()) { try { final settings = jsonDecode(settingsFile.readAsStringSync()); @@ -87,14 +89,17 @@ class DoctorCommand extends BaseCommand { table.insertRow( ['Matches pinned version:', sdkPath == relativeSymlinkPath], ); - } on FormatException { + } on FormatException catch (_, stackTrace) { logger ..err('Error parsing Vscode settings.json on ${settingsFile.path}') ..err( - 'Please use a tool like https://jsonformatter.curiousconcept.com to validate and fix it', + 'Please use a tool like https://jsonlint.com to validate and fix it', ); - throw AppException( - 'Could not get vscode settings, please check settings.json', + Error.throwWithStackTrace( + AppException( + 'Could not get vscode settings, please check settings.json', + ), + stackTrace, ); } } else { @@ -134,9 +139,9 @@ class DoctorCommand extends BaseCommand { final dartSdk = dartSdkFile.readAsStringSync(); final containsUserHome = dartSdk.contains(r'$USER_HOME$'); final containsProjectDir = dartSdk.contains(r'$PROJECT_DIR$'); - final containsSymLinkName = dartSdk.contains('.fvm/flutter_sdk'); if (!containsUserHome && containsProjectDir) { + final containsSymLinkName = dartSdk.contains('.fvm/flutter_sdk'); if (containsSymLinkName) { table.insertRow([ 'SDK Path', @@ -155,10 +160,7 @@ class DoctorCommand extends BaseCommand { ]); } } else { - table.insertRow([ - kIntelliJ, - 'No .idea folder found', - ]); + table.insertRow([kIntelliJ, 'No .idea folder found']); } logger.write(table.toString()); @@ -198,7 +200,6 @@ class DoctorCommand extends BaseCommand { logger.write(table.toString()); } - void printFVMDetails() {} @override Future run() async { final project = ProjectService.fromContext.findAncestor(); diff --git a/lib/src/commands/exec_command.dart b/lib/src/commands/exec_command.dart index 12f0a114..2b568d44 100644 --- a/lib/src/commands/exec_command.dart +++ b/lib/src/commands/exec_command.dart @@ -1,10 +1,11 @@ import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/fvm.dart'; +import '../models/cache_flutter_version_model.dart'; import '../services/logger_service.dart'; +import '../services/project_service.dart'; import '../utils/commands.dart'; +import '../utils/constants.dart'; import '../workflows/ensure_cache.workflow.dart'; import 'base_command.dart'; @@ -31,7 +32,7 @@ class ExecCommand extends BaseCommand { final cmd = argResults!.rest[0]; // Removes version from first arg - final execArgs = [...argResults!.rest]..removeAt(0); + final execArgs = [...?argResults?.rest]..removeAt(0); // If no version is provided try to use global CacheFlutterVersion? cacheVersion; diff --git a/lib/src/commands/flutter_command.dart b/lib/src/commands/flutter_command.dart index c4034761..f12a3557 100644 --- a/lib/src/commands/flutter_command.dart +++ b/lib/src/commands/flutter_command.dart @@ -1,10 +1,11 @@ import 'package:args/args.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/exceptions.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/services/logger_service.dart'; +import '../models/cache_flutter_version_model.dart'; +import '../services/logger_service.dart'; +import '../services/project_service.dart'; import '../utils/commands.dart'; +import '../utils/constants.dart'; +import '../utils/exceptions.dart'; import '../workflows/ensure_cache.workflow.dart'; import 'base_command.dart'; @@ -23,7 +24,7 @@ class FlutterCommand extends BaseCommand { @override Future run() async { final version = ProjectService.fromContext.findVersion(); - final args = [...argResults!.arguments]; + final args = [...?argResults?.arguments]; CacheFlutterVersion? cacheVersion; @@ -56,6 +57,7 @@ class FlutterCommand extends BaseCommand { // Running null will default to flutter version on paths } final results = await runFlutter(args, version: cacheVersion); + return results.exitCode; } } diff --git a/lib/src/commands/global_command.dart b/lib/src/commands/global_command.dart index f9344e37..2a50096d 100644 --- a/lib/src/commands/global_command.dart +++ b/lib/src/commands/global_command.dart @@ -1,12 +1,12 @@ -import 'package:fvm/constants.dart'; -import 'package:fvm/src/models/cache_flutter_version_model.dart'; -import 'package:fvm/src/services/global_version_service.dart'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/services/project_service.dart'; -import 'package:fvm/src/utils/console_utils.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/helpers.dart'; -import 'package:fvm/src/utils/which.dart'; +import '../utils/constants.dart'; +import '../models/cache_flutter_version_model.dart'; +import '../services/global_version_service.dart'; +import '../services/logger_service.dart'; +import '../services/project_service.dart'; +import '../utils/console_utils.dart'; +import '../utils/context.dart'; +import '../utils/helpers.dart'; +import '../utils/which.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:tint/tint.dart'; @@ -32,7 +32,7 @@ class GlobalCommand extends BaseCommand { // Show chooser if not version is provided if (argResults!.rest.isEmpty) { final versions = await CacheService.fromContext.getAllVersions(); - version = await cacheVersionSelector(versions); + version = cacheVersionSelector(versions); } // Get first arg if it was not empty @@ -97,6 +97,7 @@ class GlobalCommand extends BaseCommand { ) ..info('Run the command outside of the IDE to verify.'); } + return ExitCode.success.code; } diff --git a/lib/src/commands/install_command.dart b/lib/src/commands/install_command.dart index 505cb3bd..dc2427e7 100644 --- a/lib/src/commands/install_command.dart +++ b/lib/src/commands/install_command.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'package:fvm/src/workflows/setup_flutter.workflow.dart'; import 'package:io/io.dart'; -import '../../exceptions.dart'; import '../services/project_service.dart'; +import '../utils/exceptions.dart'; import '../workflows/ensure_cache.workflow.dart'; +import '../workflows/setup_flutter.workflow.dart'; import 'base_command.dart'; /// Installs Flutter SDK @@ -20,8 +20,8 @@ class InstallCommand extends BaseCommand { InstallCommand() { argParser.addFlag( 'setup', - help: 'Builds SDK after install after install', abbr: 's', + help: 'Builds SDK after install after install', defaultsTo: false, negatable: false, ); diff --git a/lib/src/commands/list_command.dart b/lib/src/commands/list_command.dart index 47b44945..fa91baa7 100644 --- a/lib/src/commands/list_command.dart +++ b/lib/src/commands/list_command.dart @@ -1,13 +1,13 @@ import 'package:dart_console/dart_console.dart'; -import 'package:fvm/src/services/global_version_service.dart'; -import 'package:fvm/src/services/releases_service/models/release.model.dart'; -import 'package:fvm/src/services/releases_service/releases_client.dart'; -import 'package:fvm/src/utils/helpers.dart'; import 'package:mason_logger/mason_logger.dart'; import '../services/cache_service.dart'; +import '../services/global_version_service.dart'; import '../services/logger_service.dart'; +import '../services/releases_service/models/release.model.dart'; +import '../services/releases_service/releases_client.dart'; import '../utils/context.dart'; +import '../utils/helpers.dart'; import 'base_command.dart'; /// List installed SDK Versions @@ -29,6 +29,7 @@ class ListCommand extends BaseCommand { logger ..info('No SDKs have been installed yet. Flutter. SDKs') ..info('installed outside of fvm will not be displayed.'); + return ExitCode.success.code; } @@ -78,8 +79,10 @@ class ListCommand extends BaseCommand { if (latestRelease.version != version.flutterSdkVersion) { return '$flutterSdkVersion $rightArrow ${(green.wrap(latestRelease.version))}'; } + return flutterSdkVersion; } + return flutterSdkVersion; } diff --git a/lib/src/commands/releases_command.dart b/lib/src/commands/releases_command.dart index 06b3fba7..62b7f915 100644 --- a/lib/src/commands/releases_command.dart +++ b/lib/src/commands/releases_command.dart @@ -22,10 +22,10 @@ class ReleasesCommand extends BaseCommand { ReleasesCommand() { argParser.addOption( 'channel', - help: 'Filter by channel name', - defaultsTo: 'stable', abbr: 'c', + help: 'Filter by channel name', allowed: ['stable', 'beta', 'dev', 'all'], + defaultsTo: 'stable', ); } @@ -45,6 +45,7 @@ class ReleasesCommand extends BaseCommand { if (channelName == allChannel) { return false; } + return release.channel.name != channelName; } diff --git a/lib/src/commands/remove_command.dart b/lib/src/commands/remove_command.dart index 2a031c96..764d2732 100644 --- a/lib/src/commands/remove_command.dart +++ b/lib/src/commands/remove_command.dart @@ -1,13 +1,13 @@ import 'dart:io'; -import 'package:fvm/constants.dart'; -import 'package:fvm/src/utils/context.dart'; import 'package:io/io.dart'; import '../models/flutter_version_model.dart'; import '../services/cache_service.dart'; import '../services/logger_service.dart'; import '../utils/console_utils.dart'; +import '../utils/constants.dart'; +import '../utils/context.dart'; import 'base_command.dart'; /// Removes Flutter SDK @@ -21,8 +21,8 @@ class RemoveCommand extends BaseCommand { RemoveCommand() { argParser.addFlag( 'all', - help: 'Removes all versions', abbr: 'a', + help: 'Removes all versions', negatable: false, ); } @@ -48,6 +48,7 @@ class RemoveCommand extends BaseCommand { ); } } + return ExitCode.success.code; } @@ -55,7 +56,7 @@ class RemoveCommand extends BaseCommand { if (argResults!.rest.isEmpty) { final versions = await CacheService.fromContext.getAllVersions(); - version = await cacheVersionSelector(versions); + version = cacheVersionSelector(versions); } // Assign if its empty version ??= argResults!.rest[0]; @@ -65,6 +66,7 @@ class RemoveCommand extends BaseCommand { // Check if version is installed if (cacheVersion == null) { logger.info('Flutter SDK: $validVersion is not installed'); + return ExitCode.success.code; } diff --git a/lib/src/commands/spawn_command.dart b/lib/src/commands/spawn_command.dart index 21388cd3..36477dc7 100644 --- a/lib/src/commands/spawn_command.dart +++ b/lib/src/commands/spawn_command.dart @@ -30,7 +30,7 @@ class SpawnCommand extends BaseCommand { final version = argResults!.rest[0]; // Removes version from first arg - final flutterArgs = [...argResults!.rest]..removeAt(0); + final flutterArgs = [...?argResults?.rest]..removeAt(0); // Will install version if not already instaled final cacheVersion = await ensureCacheWorkflow(version); @@ -38,6 +38,7 @@ class SpawnCommand extends BaseCommand { logger.info('Spawning version "$version"...'); final results = await runFlutter(flutterArgs, version: cacheVersion); + return results.exitCode; } } diff --git a/lib/src/commands/update_command.dart b/lib/src/commands/update_command.dart index 1f235177..d2c76e4d 100644 --- a/lib/src/commands/update_command.dart +++ b/lib/src/commands/update_command.dart @@ -4,8 +4,8 @@ import 'package:args/command_runner.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:pub_updater/pub_updater.dart'; -import '../../constants.dart'; import '../services/logger_service.dart'; +import '../utils/constants.dart'; import '../version.g.dart'; class UpdateCommand extends Command { diff --git a/lib/src/commands/use_command.dart b/lib/src/commands/use_command.dart index 77a252ac..cccc4afe 100644 --- a/lib/src/commands/use_command.dart +++ b/lib/src/commands/use_command.dart @@ -1,12 +1,14 @@ import 'package:args/command_runner.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/services/releases_service/releases_client.dart'; -import 'package:fvm/src/utils/helpers.dart'; -import 'package:fvm/src/workflows/ensure_cache.workflow.dart'; import 'package:io/io.dart'; +import '../services/cache_service.dart'; import '../services/logger_service.dart'; +import '../services/project_service.dart'; +import '../services/releases_service/models/channels.model.dart'; +import '../services/releases_service/releases_client.dart'; import '../utils/console_utils.dart'; +import '../utils/helpers.dart'; +import '../workflows/ensure_cache.workflow.dart'; import '../workflows/use_version.workflow.dart'; import 'base_command.dart'; @@ -24,15 +26,15 @@ class UseCommand extends BaseCommand { argParser ..addFlag( 'force', - help: 'Skips command guards that does Flutter project checks.', abbr: 'f', + help: 'Skips command guards that does Flutter project checks.', negatable: false, ) ..addFlag( 'pin', + abbr: 'p', help: '''If version provided is a channel. Will pin the latest release of the channel''', - abbr: 'p', negatable: false, ) ..addOption( @@ -43,8 +45,8 @@ class UseCommand extends BaseCommand { ) ..addFlag( 'skip-setup', - help: 'Skips Flutter setup after install', abbr: 's', + help: 'Skips Flutter setup after install', negatable: false, ); } @@ -64,7 +66,7 @@ class UseCommand extends BaseCommand { version = project.pinnedVersion?.name; final versions = await CacheService.fromContext.getAllVersions(); // If no config found, ask which version to select. - version ??= await cacheVersionSelector(versions); + version ??= cacheVersionSelector(versions); } // Get version from first arg @@ -115,8 +117,8 @@ class UseCommand extends BaseCommand { version: cacheVersion, project: project, force: forceOption, - flavor: flavorOption, skipSetup: skipSetup, + flavor: flavorOption, ); return ExitCode.success.code; diff --git a/lib/src/models/cache_flutter_version_model.dart b/lib/src/models/cache_flutter_version_model.dart index 91c971fc..2e90c755 100644 --- a/lib/src/models/cache_flutter_version_model.dart +++ b/lib/src/models/cache_flutter_version_model.dart @@ -1,13 +1,13 @@ import 'dart:io'; -import 'package:fvm/src/models/flutter_version_model.dart'; -import 'package:fvm/src/utils/commands.dart'; -import 'package:fvm/src/utils/compare_semver.dart'; -import 'package:fvm/src/utils/extensions.dart'; -import 'package:fvm/src/utils/helpers.dart'; +import 'flutter_version_model.dart'; +import '../utils/commands.dart'; +import '../utils/compare_semver.dart'; +import '../utils/extensions.dart'; +import '../utils/helpers.dart'; import 'package:path/path.dart'; -import '../../constants.dart'; +import '../utils/constants.dart'; /// Cache Version model class CacheFlutterVersion extends FlutterVersion { @@ -41,6 +41,7 @@ class CacheFlutterVersion extends FlutterVersion { /// Get old bin path /// Before version 1.17.5 dart path was bin/cache/dart-sdk/bin if (hasOldBinPath) return join(_dartSdkCache, 'bin'); + return binPath; } @@ -53,11 +54,13 @@ class CacheFlutterVersion extends FlutterVersion { /// Gets Flutter SDK version from CacheVersion sync String? get flutterSdkVersion { final versionFile = join(directory, 'version'); + return versionFile.file.read()?.trim(); } String? get dartSdkVersion { final versionFile = join(_dartSdkCache, 'version'); + return versionFile.file.read()?.trim(); } diff --git a/lib/src/models/config_model.dart b/lib/src/models/config_model.dart index f5a1559a..d958a9fe 100644 --- a/lib/src/models/config_model.dart +++ b/lib/src/models/config_model.dart @@ -4,10 +4,11 @@ import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/src/utils/change_case.dart'; -import 'package:fvm/src/utils/extensions.dart'; -import 'package:fvm/src/utils/pretty_json.dart'; + +import '../utils/change_case.dart'; +import '../utils/constants.dart'; +import '../utils/extensions.dart'; +import '../utils/pretty_json.dart'; class ConfigKeys { final String key; @@ -57,8 +58,8 @@ class ConfigKeys { ConfigKeys.useGitCache.paramKey, help: 'Enable/Disable git cache globally, which is used for faster version installs.', - negatable: true, defaultsTo: true, + negatable: true, ); }, ConfigKeys.gitCachePath.key: () { @@ -77,8 +78,8 @@ class ConfigKeys { argParser.addFlag( ConfigKeys.priviledgedAccess.paramKey, help: 'Enable/Disable priviledged access for FVM', - negatable: true, defaultsTo: true, + negatable: true, ); }, }; @@ -95,7 +96,7 @@ class ConfigKeys { String get propKey => _recase.camelCase; @override - operator ==(other) => other is ConfigKeys && other.key == key; + operator ==(Object other) => other is ConfigKeys && other.key == key; @override int get hashCode => key.hashCode; @@ -188,16 +189,17 @@ class AppConfig extends Config { factory AppConfig.fromMap(Map map) { final envConfig = Config.fromMap(map); + return AppConfig( - cachePath: envConfig.cachePath, - gitCachePath: envConfig.gitCachePath, - flutterUrl: envConfig.flutterUrl, - useGitCache: envConfig.useGitCache, - priviledgedAccess: envConfig.priviledgedAccess, disableUpdateCheck: map['disableUpdateCheck'] as bool?, lastUpdateCheck: map['lastUpdateCheck'] != null ? DateTime.parse(map['lastUpdateCheck'] as String) : null, + cachePath: envConfig.cachePath, + useGitCache: envConfig.useGitCache, + gitCachePath: envConfig.gitCachePath, + flutterUrl: envConfig.flutterUrl, + priviledgedAccess: envConfig.priviledgedAccess, ); } @@ -223,13 +225,13 @@ class AppConfig extends Config { bool? priviledgedAccess, }) { return AppConfig( + disableUpdateCheck: disableUpdateCheck ?? this.disableUpdateCheck, + lastUpdateCheck: lastUpdateCheck ?? this.lastUpdateCheck, cachePath: cachePath ?? this.cachePath, useGitCache: useGitCache ?? this.useGitCache, gitCachePath: gitCachePath ?? this.gitCachePath, flutterUrl: flutterUrl ?? this.flutterUrl, - disableUpdateCheck: disableUpdateCheck ?? this.disableUpdateCheck, priviledgedAccess: priviledgedAccess ?? this.priviledgedAccess, - lastUpdateCheck: lastUpdateCheck ?? this.lastUpdateCheck, ); } @@ -240,8 +242,8 @@ class AppConfig extends Config { gitCachePath: config?.gitCachePath, flutterUrl: config?.flutterUrl, disableUpdateCheck: config?.disableUpdateCheck, - priviledgedAccess: config?.priviledgedAccess, lastUpdateCheck: config?.lastUpdateCheck, + priviledgedAccess: config?.priviledgedAccess, ); } @@ -308,17 +310,18 @@ class ProjectConfig extends Config { /// Returns ConfigDto from a map factory ProjectConfig.fromMap(Map map) { final envConfig = Config.fromMap(map); + return ProjectConfig( cachePath: envConfig.cachePath, + useGitCache: envConfig.useGitCache, gitCachePath: envConfig.gitCachePath, flutterUrl: envConfig.flutterUrl, - useGitCache: envConfig.useGitCache, priviledgedAccess: envConfig.priviledgedAccess, - updateGitIgnore: map['updateGitIgnore'] as bool?, flutterSdkVersion: map['flutterSdkVersion'] ?? map['flutter'] as String?, + flavors: map['flavors'] != null ? Map.from(map['flavors'] as Map) : null, updateVscodeSettings: map['updateVscodeSettings'] as bool?, + updateGitIgnore: map['updateGitIgnore'] as bool?, runPubGetOnSdkChanges: map['runPubGetOnSdkChanges'] as bool?, - flavors: map['flavors'] != null ? Map.from(map['flavors'] as Map) : null, ); } @@ -360,36 +363,37 @@ class ProjectConfig extends Config { }) { // merge map and override the keys final mergedFlavors = { - if (this.flavors != null) ...this.flavors!, + if (this.flavors != null) ...?this.flavors, + // ignore: prefer-null-aware-spread if (flavors != null) ...flavors, }; return ProjectConfig( - cachePath: cachePath ?? cachePath, + cachePath: cachePath ?? this.cachePath, + useGitCache: useGitCache ?? this.useGitCache, + gitCachePath: gitCachePath ?? this.gitCachePath, + flutterUrl: flutterUrl ?? this.flutterUrl, + priviledgedAccess: priviledgedAccess ?? this.priviledgedAccess, flutterSdkVersion: flutterSdkVersion ?? this.flutterSdkVersion, flavors: mergedFlavors, - priviledgedAccess: priviledgedAccess ?? this.priviledgedAccess, updateVscodeSettings: updateVscodeSettings ?? _updateVscodeSettings, - runPubGetOnSdkChanges: runPubGetOnSdkChanges ?? _runPubGetOnSdkChanges, updateGitIgnore: updateGitIgnore ?? _updateGitIgnore, - useGitCache: useGitCache ?? this.useGitCache, - gitCachePath: gitCachePath ?? this.gitCachePath, - flutterUrl: flutterUrl ?? this.flutterUrl, + runPubGetOnSdkChanges: runPubGetOnSdkChanges ?? _runPubGetOnSdkChanges, ); } ProjectConfig merge(ProjectConfig config) { return copyWith( cachePath: config.cachePath, - useGitCache: config.useGitCache, - gitCachePath: config.gitCachePath, - flutterUrl: config.flutterUrl, flutterSdkVersion: config.flutterSdkVersion, - priviledgedAccess: config.priviledgedAccess, - flavors: config.flavors, + useGitCache: config.useGitCache, updateVscodeSettings: config._updateVscodeSettings, updateGitIgnore: config._updateGitIgnore, runPubGetOnSdkChanges: config._runPubGetOnSdkChanges, + priviledgedAccess: config.priviledgedAccess, + gitCachePath: config.gitCachePath, + flutterUrl: config.flutterUrl, + flavors: config.flavors, ); } diff --git a/lib/src/models/flutter_version_model.dart b/lib/src/models/flutter_version_model.dart index ecf9a424..b906b4e8 100644 --- a/lib/src/models/flutter_version_model.dart +++ b/lib/src/models/flutter_version_model.dart @@ -1,8 +1,8 @@ -import 'package:fvm/src/utils/compare_semver.dart'; -import 'package:fvm/src/utils/extensions.dart'; -import 'package:fvm/src/utils/git_utils.dart'; +import '../utils/compare_semver.dart'; +import '../utils/extensions.dart'; +import '../utils/git_utils.dart'; -import '../../constants.dart'; +import '../utils/constants.dart'; import '../utils/helpers.dart'; /// Provides a structured way to handle Flutter SDK versions. @@ -121,6 +121,7 @@ class FlutterVersion { int compareTo(FlutterVersion other) { final otherVersion = assignVersionWeight(other.version); final versionWeight = assignVersionWeight(version); + return compareSemver(versionWeight, otherVersion); } diff --git a/lib/src/models/project_model.dart b/lib/src/models/project_model.dart index 3b5ad744..3bf796f6 100644 --- a/lib/src/models/project_model.dart +++ b/lib/src/models/project_model.dart @@ -5,7 +5,7 @@ import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; import 'package:pubspec/pubspec.dart'; -import '../../constants.dart'; +import '../utils/constants.dart'; import '../utils/extensions.dart'; import 'config_model.dart'; import 'flutter_version_model.dart'; @@ -55,7 +55,7 @@ class Project { ? PubSpec.fromYamlString(pubspecFile.readAsStringSync()) : null; - return Project(path: path, pubspec: pubspec, config: config); + return Project(config: config, path: path, pubspec: pubspec); } /// Retrieves the name of the project. @@ -69,6 +69,7 @@ class Project { if (sdkVersion != null) { return FlutterVersion.parse(sdkVersion); } + return null; } @@ -154,7 +155,7 @@ String? _dartToolGeneratorVersion(String projectPath) { return file.existsSync() ? (jsonDecode(file.readAsStringSync()) - as Map)['generatorVersion'] + as Map)['generatorVersion'] as String? : null; } diff --git a/lib/src/runner.dart b/lib/src/runner.dart index c9b45091..84f1a518 100644 --- a/lib/src/runner.dart +++ b/lib/src/runner.dart @@ -3,29 +3,29 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:fvm/constants.dart'; -import 'package:fvm/src/commands/global_command.dart'; -import 'package:fvm/src/commands/update_command.dart'; -import 'package:fvm/src/services/config_repository.dart'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/deprecation_util.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:pub_updater/pub_updater.dart'; import 'package:stack_trace/stack_trace.dart'; -import '../exceptions.dart'; import 'commands/config_command.dart'; import 'commands/dart_command.dart'; import 'commands/doctor_command.dart'; import 'commands/exec_command.dart'; import 'commands/flutter_command.dart'; +import 'commands/global_command.dart'; import 'commands/install_command.dart'; import 'commands/list_command.dart'; import 'commands/releases_command.dart'; import 'commands/remove_command.dart'; import 'commands/spawn_command.dart'; +import 'commands/update_command.dart'; import 'commands/use_command.dart'; +import 'services/config_repository.dart'; +import 'services/logger_service.dart'; +import 'utils/constants.dart'; +import 'utils/context.dart'; +import 'utils/deprecation_util.dart'; +import 'utils/exceptions.dart'; import 'version.g.dart'; /// Command Runner for FVM @@ -41,8 +41,8 @@ class FvmCommandRunner extends CommandRunner { ..addFlag( 'version', abbr: 'v', - negatable: false, help: 'Print the current version.', + negatable: false, ); addCommand(InstallCommand()); addCommand(UseCommand()); @@ -142,6 +142,7 @@ class FvmCommandRunner extends CommandRunner { "Try running with sudo or administrator priviledges.\n" "If you are on Windows, you can turn on developer mode: https://bit.ly/3vxRr2M", ); + return ExitCode.noPerm.code; } @@ -179,6 +180,7 @@ class FvmCommandRunner extends CommandRunner { ..err(err.toString()); _printTrace(stackTrace); + return ExitCode.unavailable.code; } finally { // Add spacer after the last line always @@ -193,9 +195,8 @@ class FvmCommandRunner extends CommandRunner { ..detail('') ..detail('Argument information:'); - final hasTopLevelOption = topLevelResults.options - .where((e) => topLevelResults.wasParsed(e)) - .isNotEmpty; + final hasTopLevelOption = + topLevelResults.options.any((e) => topLevelResults.wasParsed(e)); if (hasTopLevelOption) { logger.detail(' Top level options:'); @@ -212,9 +213,8 @@ class FvmCommandRunner extends CommandRunner { logger.detail('Command: ${commandResult.name}'); // Check if any command option was parsed - final hasCommandOption = commandResult.options - .where((e) => commandResult.wasParsed(e)) - .isNotEmpty; + final hasCommandOption = + commandResult.options.any((e) => commandResult.wasParsed(e)); if (hasCommandOption) { logger.detail(' Command options:'); diff --git a/lib/src/services/base_service.dart b/lib/src/services/base_service.dart index 95c04514..ea0b3012 100644 --- a/lib/src/services/base_service.dart +++ b/lib/src/services/base_service.dart @@ -1,4 +1,4 @@ -import 'package:fvm/src/utils/context.dart'; +import '../utils/context.dart'; abstract class ContextService { final FVMContext? _context; @@ -7,6 +7,7 @@ abstract class ContextService { /// Gets context, if no context is passed will get from scope FVMContext get context { if (_context == null) return ctx; + return _context!; } } diff --git a/lib/src/services/cache_service.dart b/lib/src/services/cache_service.dart index 6f970791..3c94b435 100644 --- a/lib/src/services/cache_service.dart +++ b/lib/src/services/cache_service.dart @@ -1,14 +1,14 @@ import 'dart:io'; -import 'package:fvm/exceptions.dart'; -import 'package:fvm/src/services/base_service.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/extensions.dart'; import 'package:io/io.dart'; import 'package:path/path.dart' as path; import '../models/cache_flutter_version_model.dart'; import '../models/flutter_version_model.dart'; +import '../utils/context.dart'; +import '../utils/exceptions.dart'; +import '../utils/extensions.dart'; +import 'base_service.dart'; enum CacheIntegrity { valid, @@ -34,6 +34,7 @@ class CacheService extends ContextService { if (version.isChannel) return true; // If sdkVersion is not available return true if (version.flutterSdkVersion == null) return true; + return version.flutterSdkVersion == version.version; } @@ -45,6 +46,7 @@ class CacheService extends ContextService { final versionDir = getVersionCacheDir(version.name); // Return null if version does not exist if (!versionDir.existsSync()) return null; + return CacheFlutterVersion(version, directory: versionDir.path); } diff --git a/lib/src/services/config_repository.dart b/lib/src/services/config_repository.dart index e04ce19d..15a146c0 100644 --- a/lib/src/services/config_repository.dart +++ b/lib/src/services/config_repository.dart @@ -1,9 +1,8 @@ import 'dart:io'; -import 'package:fvm/constants.dart'; -import 'package:fvm/src/utils/helpers.dart'; - -import '../../fvm.dart'; +import '../models/config_model.dart'; +import '../utils/constants.dart'; +import '../utils/helpers.dart'; const String flutterGitUrl = 'FLUTTER_GIT_URL'; @@ -14,6 +13,7 @@ class ConfigRepository { static AppConfig loadFile() { final appConfig = AppConfig.loadFromPath(_configPath); if (appConfig != null) return appConfig; + return AppConfig.empty(); } @@ -57,9 +57,9 @@ class ConfigRepository { for (final variable in ConfigKeys.values) { final value = environments[variable.envKey]; - final legacyFvmHome = environments['FVM_HOME']; if (variable == ConfigKeys.cachePath) { + final legacyFvmHome = environments['FVM_HOME']; cachePath = value ?? legacyFvmHome; break; } diff --git a/lib/src/services/flutter_service.dart b/lib/src/services/flutter_service.dart index ebab641e..5ebfb642 100644 --- a/lib/src/services/flutter_service.dart +++ b/lib/src/services/flutter_service.dart @@ -2,19 +2,20 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:fvm/src/services/base_service.dart'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/services/releases_service/releases_client.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/parsers/git_clone_update_printer.dart'; import 'package:git/git.dart'; import 'package:io/io.dart' as io; import 'package:mason_logger/mason_logger.dart'; -import '../../exceptions.dart'; -import '../../fvm.dart'; +import '../models/cache_flutter_version_model.dart'; import '../models/flutter_version_model.dart'; import '../utils/commands.dart'; +import '../utils/context.dart'; +import '../utils/exceptions.dart'; +import '../utils/parsers/git_clone_update_printer.dart'; +import 'base_service.dart'; +import 'cache_service.dart'; +import 'logger_service.dart'; +import 'releases_service/releases_client.dart'; /// Helpers and tools to interact with Flutter sdk class FlutterService extends ContextService { @@ -78,18 +79,20 @@ class FlutterService extends ContextService { ]; try { - final result = await runGit([ - 'clone', - '--progress', - ...cloneArgs, - context.flutterUrl, - versionDir.path, - ], echoOutput: !(context.isTest || !logger.isVerbose)); + final result = await runGit( + [ + 'clone', + '--progress', + ...cloneArgs, + context.flutterUrl, + versionDir.path, + ], + echoOutput: !(context.isTest || !logger.isVerbose), + ); final gitVersionDir = CacheService(context).getVersionCacheDir(version.name); final isGit = await GitDir.isGitDir(gitVersionDir.path); - if (!isGit) { throw AppException( 'Flutter SDK is not a valid git repository after clone. Please try again.', @@ -152,6 +155,7 @@ class FlutterService extends ContextService { if (commitSha == null) { return false; } + return commit.contains(commitSha); } @@ -164,7 +168,8 @@ class FlutterService extends ContextService { } final tags = await getTags(); - return tags.where((t) => t == tag).isNotEmpty; + + return tags.any((t) => t == tag); } Future> getTags() async { diff --git a/lib/src/services/global_version_service.dart b/lib/src/services/global_version_service.dart index f8b40ec8..a3bd6985 100644 --- a/lib/src/services/global_version_service.dart +++ b/lib/src/services/global_version_service.dart @@ -1,13 +1,14 @@ import 'dart:io'; -import 'package:fvm/src/models/cache_flutter_version_model.dart'; -import 'package:fvm/src/models/flutter_version_model.dart'; -import 'package:fvm/src/services/base_service.dart'; -import 'package:fvm/src/services/cache_service.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/extensions.dart'; import 'package:path/path.dart' as path; +import '../models/cache_flutter_version_model.dart'; +import '../models/flutter_version_model.dart'; +import '../utils/context.dart'; +import '../utils/extensions.dart'; +import 'base_service.dart'; +import 'cache_service.dart'; + class GlobalVersionService extends ContextService { const GlobalVersionService(super.context); @@ -27,6 +28,7 @@ class GlobalVersionService extends ContextService { final version = path.basename(_globalCacheLink.targetSync()); // Make sure its a valid version final validVersion = FlutterVersion.parse(version); + // Verify version is cached return CacheService(context).getVersion(validVersion); } @@ -34,12 +36,14 @@ class GlobalVersionService extends ContextService { /// Checks if a cached [version] is configured as global bool isGlobal(CacheFlutterVersion version) { if (!_globalCacheLink.existsSync()) return false; + return _globalCacheLink.targetSync() == version.directory; } /// Returns a global version name if exists String? getGlobalVersion() { if (!_globalCacheLink.existsSync()) return null; + // Get directory name return path.basename(_globalCacheLink.targetSync()); } diff --git a/lib/src/services/logger_service.dart b/lib/src/services/logger_service.dart index b17e093b..967ae2c8 100644 --- a/lib/src/services/logger_service.dart +++ b/lib/src/services/logger_service.dart @@ -1,12 +1,13 @@ import 'dart:async'; import 'package:dart_console/dart_console.dart'; -import 'package:fvm/src/services/base_service.dart'; -import 'package:fvm/src/utils/context.dart'; import 'package:interact/interact.dart' as interact; import 'package:mason_logger/mason_logger.dart'; import 'package:tint/tint.dart'; +import '../utils/context.dart'; +import 'base_service.dart'; + /// Sets default logger mode LoggerService get logger => getProvider(); @@ -58,6 +59,7 @@ class LoggerService extends ContextService { // Replace for a normal log logger.info(message); } + return progress; } diff --git a/lib/src/services/project_service.dart b/lib/src/services/project_service.dart index e730c9b1..5951a913 100644 --- a/lib/src/services/project_service.dart +++ b/lib/src/services/project_service.dart @@ -1,13 +1,14 @@ import 'dart:io'; -import 'package:fvm/src/models/config_model.dart'; -import 'package:fvm/src/models/project_model.dart'; -import 'package:fvm/src/services/base_service.dart'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/extensions.dart'; -import 'package:fvm/src/utils/pretty_json.dart'; import 'package:path/path.dart' as path; +import '../models/config_model.dart'; +import '../models/project_model.dart'; +import '../utils/context.dart'; +import '../utils/extensions.dart'; +import '../utils/pretty_json.dart'; +import 'base_service.dart'; + /// Flutter Project Services /// APIs for interacting with local Flutter projects /// @@ -57,6 +58,7 @@ class ProjectService extends ContextService { /// Returns the pinned Flutter SDK version for the project, or `null` if no version is configured. String? findVersion() { final project = findAncestor(); + return project.pinnedVersion?.name; } @@ -77,9 +79,9 @@ class ProjectService extends ContextService { final newConfig = project.config ?? ProjectConfig(); final config = newConfig.copyWith( - flavors: flavors, flutterSdkVersion: flutterSdkVersion, updateVscodeSettings: updateVscodeSettings, + flavors: flavors, ); // Update flavors diff --git a/lib/src/services/releases_service/models/flutter_releases.model.dart b/lib/src/services/releases_service/models/flutter_releases.model.dart index df01018a..b596d184 100644 --- a/lib/src/services/releases_service/models/flutter_releases.model.dart +++ b/lib/src/services/releases_service/models/flutter_releases.model.dart @@ -69,7 +69,7 @@ class Releases { Map toMap() => { 'base_url': baseUrl, 'channels': channels.toMap(), - 'releases': List.from(releases.map((x) => x.toMap())), + 'releases': List.from(releases.map((x) => x.toMap())), }; } @@ -79,7 +79,7 @@ class Releases { Releases _parseCurrentReleases(Map map) { final baseUrl = map['base_url'] as String; final currentRelease = map['current_release'] as Map; - final releasesJson = map['releases'] as List; + final releasesJson = map['releases'] as List; final systemArch = 'x64'; @@ -125,8 +125,8 @@ Releases _parseCurrentReleases(Map map) { final stableRelease = hashReleaseMap[stable]; final channels = Channels( - dev: devRelease!, beta: betaRelease!, + dev: devRelease!, stable: stableRelease!, ); @@ -134,7 +134,7 @@ Releases _parseCurrentReleases(Map map) { baseUrl: baseUrl, channels: channels, releases: releasesList, - versionReleaseMap: versionReleaseMap, hashReleaseMap: hashReleaseMap, + versionReleaseMap: versionReleaseMap, ); } diff --git a/lib/src/services/releases_service/models/release.model.dart b/lib/src/services/releases_service/models/release.model.dart index 9316f885..e41f0c6c 100644 --- a/lib/src/services/releases_service/models/release.model.dart +++ b/lib/src/services/releases_service/models/release.model.dart @@ -51,10 +51,10 @@ class Release { channel: FlutterChannel.fromName(map['channel'] as String), version: map['version'] as String, releaseDate: DateTime.parse(map['release_date'] as String), - dartSdkArch: map['dart_sdk_arch'] as String?, - dartSdkVersion: map['dart_sdk_version'] as String?, archive: map['archive'] as String, sha256: map['sha256'] as String, + dartSdkArch: map['dart_sdk_arch'] as String?, + dartSdkVersion: map['dart_sdk_version'] as String?, activeChannel: map['active_channel'] as bool? ?? false, ); diff --git a/lib/src/services/releases_service/releases_client.dart b/lib/src/services/releases_service/releases_client.dart index b980b7ea..9aa1568d 100644 --- a/lib/src/services/releases_service/releases_client.dart +++ b/lib/src/services/releases_service/releases_client.dart @@ -1,12 +1,11 @@ import 'dart:io'; -import 'package:fvm/src/services/releases_service/models/channels.model.dart'; -import 'package:fvm/src/services/releases_service/models/release.model.dart'; -import 'package:fvm/src/utils/http.dart'; - -import '../../../exceptions.dart'; +import '../../utils/exceptions.dart'; +import '../../utils/http.dart'; import '../logger_service.dart'; +import 'models/channels.model.dart'; import 'models/flutter_releases.model.dart'; +import 'models/release.model.dart'; final _envVars = Platform.environment; final _storageUrl = 'https://storage.googleapis.com'; @@ -46,9 +45,11 @@ class FlutterReleases { final response = await fetch(releasesUrl); _cacheReleasesRes = Releases.fromJson(response); + return await Future.value(_cacheReleasesRes); } on Exception catch (err) { logger.detail(err.toString()); + return _getFromFlutterUrl(platform); } } @@ -57,13 +58,17 @@ class FlutterReleases { try { final response = await fetch(getFlutterReleasesUrl(platform)); _cacheReleasesRes = Releases.fromJson(response); + return await Future.value(_cacheReleasesRes); - } on Exception { - throw AppException( - 'Failed to retrieve the Flutter SDK from: ${getFlutterReleasesUrl(platform)}\n' - 'Fvm will use the value set on ' - 'env FLUTTER_STORAGE_BASE_URL to check versions\n' - 'if you are located in China, please see this page: https://flutter.dev/community/china', + } on Exception catch (_, stackTrace) { + Error.throwWithStackTrace( + AppException( + 'Failed to retrieve the Flutter SDK from: ${getFlutterReleasesUrl(platform)}\n' + 'Fvm will use the value set on ' + 'env FLUTTER_STORAGE_BASE_URL to check versions\n' + 'if you are located in China, please see this page: https://flutter.dev/community/china', + ), + stackTrace, ); } } @@ -73,12 +78,14 @@ class FlutterReleases { FlutterChannel channel, ) async { final releases = await get(); + return releases.getLatestChannelRelease(channel.name); } /// Returns a [FlutterChannel] from a [version] static Future getReleaseFromVersion(String version) async { final releases = await get(); + return releases.getReleaseFromVersion(version); } } diff --git a/lib/src/utils/change_case.dart b/lib/src/utils/change_case.dart index 767ea8cc..b8e22fb0 100644 --- a/lib/src/utils/change_case.dart +++ b/lib/src/utils/change_case.dart @@ -58,6 +58,7 @@ class ChangeCase { String _upperCaseFirstLetter(String word) { if (word.isEmpty) return ''; + return word.capitalize; } @@ -79,6 +80,7 @@ class ChangeCase { extension on String { String get capitalize { if (isEmpty) return this; + return this[0].toUpperCase() + substring(1).toLowerCase(); } } diff --git a/lib/src/utils/cli_util.dart b/lib/src/utils/cli_util.dart index dc65f3ce..f839e903 100644 --- a/lib/src/utils/cli_util.dart +++ b/lib/src/utils/cli_util.dart @@ -6,9 +6,10 @@ import 'dart:io'; -import 'package:fvm/constants.dart'; import 'package:path/path.dart' as path; +import 'constants.dart'; + /// Return the path to the current Dart SDK. String getSdkPath() => path.dirname(path.dirname(Platform.resolvedExecutable)); @@ -33,6 +34,7 @@ String get _configHome { if (xdgConfigHome != null) { return xdgConfigHome; } + // XDG Base Directory Specification says to use $HOME/.config/ when // $XDG_CONFIG_HOME isn't defined. return path.join(kUserHome, '.config'); diff --git a/lib/src/utils/commands.dart b/lib/src/utils/commands.dart index f1410515..ac3c8319 100644 --- a/lib/src/utils/commands.dart +++ b/lib/src/utils/commands.dart @@ -1,10 +1,9 @@ import 'dart:io'; -import 'package:fvm/src/utils/context.dart'; -import 'package:fvm/src/utils/run_command.dart'; - -import '../../fvm.dart'; +import '../models/cache_flutter_version_model.dart'; +import 'context.dart'; import 'helpers.dart'; +import 'run_command.dart'; final _dartCmd = 'dart'; final _flutterCmd = 'flutter'; @@ -19,6 +18,7 @@ Future runFlutter( if (version == null) { return _runCmd(_flutterCmd, args: args); } + return _runOnVersion( _flutterCmd, version, @@ -38,6 +38,7 @@ Future runDart( if (version == null) { return _runCmd(_dartCmd, args: args); } + return _runOnVersion( _dartCmd, version, @@ -61,10 +62,7 @@ Future _runOnVersion( // Update environment final environment = updateEnvironmentVariables( - [ - version.binPath, - version.dartBinPath, - ], + [version.binPath, version.dartBinPath], ctx.environment, ); @@ -89,10 +87,7 @@ Future execCmd( var environment = ctx.environment; if (version != null) { environment = updateEnvironmentVariables( - [ - version.binPath, - version.dartBinPath, - ], + [version.binPath, version.dartBinPath], ctx.environment, ); } @@ -110,6 +105,7 @@ Future _runCmd( }) async { echoOutput ??= true; throwOnError ??= false; + return await runCommand( execPath, args: args, diff --git a/lib/src/utils/compare_semver.dart b/lib/src/utils/compare_semver.dart index dc846086..3d216753 100644 --- a/lib/src/utils/compare_semver.dart +++ b/lib/src/utils/compare_semver.dart @@ -9,5 +9,6 @@ int compareSemver(String version, String otherVersion) { // Use the built-in comparison if (ver1 < ver2) return -1; if (ver1 > ver2) return 1; + return 0; // versions are equal } diff --git a/lib/src/utils/console_utils.dart b/lib/src/utils/console_utils.dart index 12473d21..24762bc4 100644 --- a/lib/src/utils/console_utils.dart +++ b/lib/src/utils/console_utils.dart @@ -1,8 +1,8 @@ import 'package:dart_console/dart_console.dart'; -import 'package:fvm/src/models/cache_flutter_version_model.dart'; -import '../../exceptions.dart'; +import '../models/cache_flutter_version_model.dart'; import '../services/logger_service.dart'; +import 'exceptions.dart'; Table createTable([List columns = const []]) { final table = Table() @@ -14,6 +14,7 @@ Table createTable([List columns = const []]) { for (final column in columns) { table.insertColumn(header: column, alignment: TextAlignment.left); } + return table; } diff --git a/lib/constants.dart b/lib/src/utils/constants.dart similarity index 99% rename from lib/constants.dart rename to lib/src/utils/constants.dart index 2372c2a1..17b18430 100644 --- a/lib/constants.dart +++ b/lib/src/utils/constants.dart @@ -55,6 +55,7 @@ String get _configHome { if (appdata == null) { throw Exception('Environment variable %APPDATA% is not defined!'); } + return appdata; } @@ -67,6 +68,7 @@ String get _configHome { if (xdgConfigHome != null) { return xdgConfigHome; } + // XDG Base Directory Specification says to use $HOME/.config/ when // $XDG_CONFIG_HOME isn't defined. return join(kUserHome, '.config'); diff --git a/lib/src/utils/context.dart b/lib/src/utils/context.dart index e4e2426f..2b5ede1b 100644 --- a/lib/src/utils/context.dart +++ b/lib/src/utils/context.dart @@ -1,15 +1,17 @@ import 'dart:io'; -import 'package:fvm/src/services/config_repository.dart'; -import 'package:fvm/src/services/flutter_service.dart'; -import 'package:fvm/src/services/global_version_service.dart'; -import 'package:fvm/src/services/logger_service.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:path/path.dart'; import 'package:scope/scope.dart'; -import '../../constants.dart'; -import '../../fvm.dart'; +import '../models/config_model.dart'; +import '../services/cache_service.dart'; +import '../services/config_repository.dart'; +import '../services/flutter_service.dart'; +import '../services/global_version_service.dart'; +import '../services/logger_service.dart'; +import '../services/project_service.dart'; +import 'constants.dart'; final contextKey = ScopeKey(); @@ -17,6 +19,7 @@ final contextKey = ScopeKey(); /// /// Generators are allowed to return `null`, in which case the context will /// store the `null` value as the value for that type. +// ignore: avoid-dynamic typedef Generator = dynamic Function(FVMContext context); FVMContext get ctx => use(contextKey, withDefault: () => FVMContext.main); @@ -56,9 +59,10 @@ class FVMContext { // Load config from file in config path final projectConfig = ProjectConfig.loadFromPath(workingDirectory); final envConfig = ConfigRepository.loadEnv(); - var appConfig = ConfigRepository.loadFile(); - appConfig = appConfig.mergeConfig(envConfig).mergeConfig(projectConfig); + final appConfig = ConfigRepository.loadFile() + .mergeConfig(envConfig) + .mergeConfig(projectConfig); // Merge config from file with env config final config = appConfig.merge(configOverrides); @@ -69,7 +73,6 @@ class FVMContext { id: id ?? 'MAIN', workingDirectory: workingDirectory, config: config, - isTest: isTest, generators: { LoggerService: (context) => LoggerService( level: level, @@ -81,6 +84,7 @@ class FVMContext { GlobalVersionService: GlobalVersionService.new, ...overrides, }, + isTest: isTest, ); } @@ -106,6 +110,7 @@ class FVMContext { String get gitCachePath { // If git cache is not overriden use default based on fvmDir if (config.gitCachePath != null) return config.gitCachePath!; + return join(fvmDir, 'cache.git'); } @@ -140,6 +145,7 @@ class FVMContext { if (generators != null && generators!.containsKey(T)) { final generator = generators![T] as Generator; _dependencies[T] = generator(this); + return _dependencies[T]; } throw Exception('Generator for $T not found'); diff --git a/lib/src/utils/deprecation_util.dart b/lib/src/utils/deprecation_util.dart index 618ecb4e..dd0bfd37 100644 --- a/lib/src/utils/deprecation_util.dart +++ b/lib/src/utils/deprecation_util.dart @@ -1,14 +1,15 @@ import 'dart:convert'; import 'dart:io'; -import 'package:fvm/constants.dart'; -import 'package:fvm/fvm.dart'; -import 'package:fvm/src/services/config_repository.dart'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/utils/context.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:path/path.dart'; +import '../models/config_model.dart'; +import '../services/config_repository.dart'; +import '../services/logger_service.dart'; +import 'constants.dart'; +import 'context.dart'; + void deprecationWorkflow() { _warnDeprecatedEnvVars(); final fvmDir = ctx.fvmDir; @@ -87,6 +88,7 @@ void _warnDeprecatedEnvVars() { if (!confirmation) { exit(ExitCode.success.code); } + return; } @@ -95,5 +97,5 @@ void _warnDeprecatedEnvVars() { logger.info('Please use ${ConfigKeys.cachePath.envKey} instead'); } - if (flutterRoot == null || fvmHome == null) {} + // if (flutterRoot == null || fvmHome == null) {} } diff --git a/lib/exceptions.dart b/lib/src/utils/exceptions.dart similarity index 100% rename from lib/exceptions.dart rename to lib/src/utils/exceptions.dart diff --git a/lib/src/utils/extensions.dart b/lib/src/utils/extensions.dart index b8a4a737..5ac4989f 100644 --- a/lib/src/utils/extensions.dart +++ b/lib/src/utils/extensions.dart @@ -8,6 +8,7 @@ extension ListExtension on Iterable { return element; } } + return null; } } @@ -76,6 +77,7 @@ extension StringExtensions on String { if (isEmpty) return this; final firstChar = substring(0, 1).toUpperCase(); final remainingChars = substring(1); + return '$firstChar$remainingChars'; } } diff --git a/lib/src/utils/helpers.dart b/lib/src/utils/helpers.dart index 83fea55e..b72692dc 100644 --- a/lib/src/utils/helpers.dart +++ b/lib/src/utils/helpers.dart @@ -1,11 +1,11 @@ import 'dart:io'; import 'package:date_format/date_format.dart'; -import 'package:fvm/src/utils/git_utils.dart'; import 'package:pub_semver/pub_semver.dart'; -import '../../constants.dart'; import '../services/logger_service.dart'; +import 'constants.dart'; +import 'git_utils.dart'; /// Checks if [name] is a channel @@ -63,9 +63,11 @@ String assignVersionWeight(String version) { } try { + // ignore: avoid-unused-instances Version.parse(version); } on Exception { logger.warn('Version $version is not a valid semver'); + return '0.0.0'; } @@ -176,6 +178,7 @@ String extractDartVersionOutput(String input) { bool isValidGitUrl(String url) { try { final uri = Uri.parse(url); + return uri.scheme.isNotEmpty && (uri.host.isNotEmpty || uri.path.isNotEmpty) && uri.path.endsWith('.git'); diff --git a/lib/src/utils/parsers/git_clone_update_printer.dart b/lib/src/utils/parsers/git_clone_update_printer.dart index 1905fdfa..f6f758ab 100644 --- a/lib/src/utils/parsers/git_clone_update_printer.dart +++ b/lib/src/utils/parsers/git_clone_update_printer.dart @@ -1,10 +1,13 @@ +// ignore_for_file: avoid-unassigned-stream-subscriptions + import 'dart:convert'; import 'dart:io'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/utils/extensions.dart'; import 'package:mason_logger/mason_logger.dart'; +import '../../services/logger_service.dart'; +import '../extensions.dart'; + final regexes = { 'Enumerating objects:': RegExp(r'Enumerating objects: +(\d+)%'), 'Counting objects:': RegExp(r'Counting objects: +(\d+)%'), diff --git a/lib/src/utils/pretty_json.dart b/lib/src/utils/pretty_json.dart index 7b60721f..1e1536d7 100644 --- a/lib/src/utils/pretty_json.dart +++ b/lib/src/utils/pretty_json.dart @@ -1,9 +1,11 @@ import 'dart:convert'; /// Formats [json] +// ignore: avoid-dynamic String prettyJson(dynamic json) { var spaces = ' ' * 2; var encoder = JsonEncoder.withIndent(spaces); + return encoder.convert(json); } diff --git a/lib/src/utils/run_command.dart b/lib/src/utils/run_command.dart index 91531bb6..de5fddf3 100644 --- a/lib/src/utils/run_command.dart +++ b/lib/src/utils/run_command.dart @@ -1,7 +1,7 @@ import 'dart:io'; -import 'package:fvm/src/services/logger_service.dart'; -import 'package:fvm/src/utils/context.dart'; +import '../services/logger_service.dart'; +import 'context.dart'; Future runCommand( String command, { @@ -22,22 +22,23 @@ Future runCommand( processResult = await Process.run( command, args, + workingDirectory: workingDirectory, environment: environment, runInShell: true, - workingDirectory: workingDirectory, ); if (throwOnError) { _throwIfProcessFailed(processResult, command, args); } + return processResult; } final process = await Process.start( command, args, + workingDirectory: workingDirectory, environment: environment, runInShell: true, - workingDirectory: workingDirectory, mode: ProcessStartMode.inheritStdio, ); @@ -50,6 +51,7 @@ Future runCommand( if (throwOnError) { _throwIfProcessFailed(processResult, command, args); } + return processResult; } diff --git a/lib/src/utils/which.dart b/lib/src/utils/which.dart index d2f93bf0..4d2b516e 100644 --- a/lib/src/utils/which.dart +++ b/lib/src/utils/which.dart @@ -21,6 +21,7 @@ String? which(String command, {bool binDir = false}) { if (exec.existsSync()) { final exectPath = exec.absolute.path; + return binDir ? dirname(exectPath) : exectPath; } @@ -30,6 +31,7 @@ String? which(String command, {bool binDir = false}) { exec = File(winPath); if (exec.existsSync()) { final exectPath = exec.absolute.path; + return binDir ? dirname(exectPath) : exectPath; } } diff --git a/lib/src/workflows/ensure_cache.workflow.dart b/lib/src/workflows/ensure_cache.workflow.dart index cbea0290..e0909458 100644 --- a/lib/src/workflows/ensure_cache.workflow.dart +++ b/lib/src/workflows/ensure_cache.workflow.dart @@ -2,13 +2,13 @@ import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; -import '../../exceptions.dart'; import '../models/cache_flutter_version_model.dart'; import '../models/flutter_version_model.dart'; import '../services/cache_service.dart'; import '../services/flutter_service.dart'; import '../services/logger_service.dart'; import '../utils/context.dart'; +import '../utils/exceptions.dart'; import '../utils/helpers.dart'; /// Ensures that the specified Flutter SDK version is cached locally. diff --git a/lib/src/workflows/resolve_dependencies.workflow.dart b/lib/src/workflows/resolve_dependencies.workflow.dart index 425a2b31..39037df9 100644 --- a/lib/src/workflows/resolve_dependencies.workflow.dart +++ b/lib/src/workflows/resolve_dependencies.workflow.dart @@ -1,11 +1,12 @@ import 'dart:io'; -import 'package:fvm/exceptions.dart'; -import 'package:fvm/src/models/cache_flutter_version_model.dart'; -import 'package:fvm/src/models/project_model.dart'; -import 'package:fvm/src/services/logger_service.dart'; import 'package:mason_logger/mason_logger.dart'; +import '../models/cache_flutter_version_model.dart'; +import '../models/project_model.dart'; +import '../services/logger_service.dart'; +import '../utils/exceptions.dart'; + Future resolveDependenciesWorkflow( Project project, CacheFlutterVersion version, @@ -22,6 +23,7 @@ Future resolveDependenciesWorkflow( logger ..info('Skipping "pub get" because of config setting.') ..spacer; + return; } @@ -54,6 +56,7 @@ Future resolveDependenciesWorkflow( if (!confirmation) { throw AppException('Dependencies not resolved.'); } + return; } } @@ -93,6 +96,7 @@ void logDetails(CacheFlutterVersion version, Project project) { if (dartToolVersion == flutterSdkVersion) { logger.detail('✅ Dart tool version matches SDK version, skipping resolve.'); + return; } diff --git a/lib/src/workflows/setup_flutter.workflow.dart b/lib/src/workflows/setup_flutter.workflow.dart index e997180d..a61dc74c 100644 --- a/lib/src/workflows/setup_flutter.workflow.dart +++ b/lib/src/workflows/setup_flutter.workflow.dart @@ -1,5 +1,5 @@ -import 'package:fvm/src/models/cache_flutter_version_model.dart'; -import 'package:fvm/src/services/logger_service.dart'; +import '../models/cache_flutter_version_model.dart'; +import '../services/logger_service.dart'; Future setupFlutterWorkflow(CacheFlutterVersion version) async { logger diff --git a/lib/src/workflows/use_version.workflow.dart b/lib/src/workflows/use_version.workflow.dart index a1e50b0b..ad6ca9bc 100644 --- a/lib/src/workflows/use_version.workflow.dart +++ b/lib/src/workflows/use_version.workflow.dart @@ -7,13 +7,13 @@ import 'package:mason_logger/mason_logger.dart'; import 'package:path/path.dart'; import 'package:pub_semver/pub_semver.dart'; -import '../../constants.dart'; -import '../../exceptions.dart'; import '../models/cache_flutter_version_model.dart'; import '../models/project_model.dart'; import '../services/logger_service.dart'; import '../services/project_service.dart'; +import '../utils/constants.dart'; import '../utils/context.dart'; +import '../utils/exceptions.dart'; import '../utils/extensions.dart'; import '../utils/helpers.dart'; import '../utils/pretty_json.dart'; @@ -321,7 +321,6 @@ void _manageVscodeSettings(Project project) { from: ctx.workingDirectory, ); - Error.throwWithStackTrace( AppDetailedException( 'Error parsing $kVsCode settings at $relativePath', diff --git a/test/testing_helpers/prepare_test_environment.dart b/test/testing_helpers/prepare_test_environment.dart index 40cc34a1..36ff9684 100644 --- a/test/testing_helpers/prepare_test_environment.dart +++ b/test/testing_helpers/prepare_test_environment.dart @@ -1,6 +1,6 @@ import 'dart:io'; -import 'package:fvm/constants.dart'; +import 'package:fvm/src/utils/constants.dart'; import 'package:fvm/src/utils/context.dart'; import 'package:io/io.dart'; import 'package:path/path.dart';