Skip to content

Commit

Permalink
Allow setting flutterSdkPath and dartSdkPath for the flutter and dart…
Browse files Browse the repository at this point in the history
… commands (#75)

Co-authored-by: Giuseppe Cianci <[email protected]>
  • Loading branch information
passsy and Giuspepe authored Oct 21, 2022
1 parent 6580965 commit 8a7103a
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 31 deletions.
54 changes: 51 additions & 3 deletions sidekick_core/lib/sidekick_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export 'package:sidekick_core/src/dart.dart';
export 'package:sidekick_core/src/dart_package.dart';
export 'package:sidekick_core/src/dart_runtime.dart';
export 'package:sidekick_core/src/file_util.dart';
export 'package:sidekick_core/src/flutter.dart';
export 'package:sidekick_core/src/flutterw.dart';
export 'package:sidekick_core/src/forward_command.dart';
export 'package:sidekick_core/src/git.dart';
Expand All @@ -34,13 +35,23 @@ export 'package:sidekick_core/src/sidekick_package.dart';
///
/// Set [name] to the name of your CLI entrypoint
///
/// [mainProjectPath], when set, links to the main package. For a flutter apps
/// it is the package that actually builds the flutter app.
/// Set [mainProjectPath] relative to the git repository root
/// [mainProjectPath] should be set when you have a package that you
/// consider the main package of the whole repository.
/// When your repository contains only one Flutter package in root set
/// `mainProjectPath = '.'`.
/// In a multi package repository you might use the same when the main package
/// is in root, or `mainProjectPath = 'packages/my_app'` when it is in a subfolder.
///
/// Set [flutterSdkPath] when you bind a flutter sdk to this project. This SDK
/// enables the [flutter] and [dart] commands.
/// [dartSdkPath] is inherited from [flutterSdkPath]. Set it only for pure dart
/// projects.
SidekickCommandRunner initializeSidekick({
required String name,
String? description,
String? mainProjectPath,
String? flutterSdkPath,
String? dartSdkPath,
}) {
DartPackage? mainProject;

Expand All @@ -57,6 +68,8 @@ SidekickCommandRunner initializeSidekick({
repository: repo,
mainProject: mainProject,
workingDirectory: Directory.current,
flutterSdk: flutterSdkPath == null ? null : Directory(flutterSdkPath),
dartSdk: dartSdkPath == null ? null : Directory(dartSdkPath),
);
return runner;
}
Expand All @@ -70,11 +83,15 @@ class SidekickCommandRunner<T> extends CommandRunner<T> {
required this.repository,
this.mainProject,
required this.workingDirectory,
this.flutterSdk,
this.dartSdk,
}) : super(cliName, description);

final Repository repository;
final DartPackage? mainProject;
final Directory workingDirectory;
final Directory? flutterSdk;
final Directory? dartSdk;

/// Mounts the sidekick related globals, returns a function to unmount them
/// and restore the previous globals
Expand All @@ -91,6 +108,9 @@ class SidekickCommandRunner<T> extends CommandRunner<T> {

@override
Future<T?> run(Iterable<String> args) async {
// a new command gets executes, reset whatever exitCode the previous command has set
exitCode = 0;

final unmount = mount();
final result = await super.run(args);
unmount();
Expand Down Expand Up @@ -149,3 +169,31 @@ DartPackage? get mainProject {
}
return _activeRunner?.mainProject;
}

/// Returns the path to he Flutter SDK sidekick should use for the [flutter] command
///
/// This variable is usually set to a pinned version of the Flutter SDK per project, i.e.
/// - https://github.com/passsy/flutter_wrapper
/// - https://github.com/fluttertools/fvm
Directory? get flutterSdk {
if (_activeRunner == null) {
error(
'You cannot access flutterSdk '
'outside of a Command executed with SidekickCommandRunner.',
);
}
return _activeRunner?.flutterSdk;
}

/// Returns the path to the Dart SDK sidekick should use for the [dart] command
///
/// Usually inherited from [flutterSdk] which ships with an embedded Dart SDK
Directory? get dartSdk {
if (_activeRunner == null) {
error(
'You cannot access dartSdk '
'outside of a Command executed with SidekickCommandRunner.',
);
}
return _activeRunner?.dartSdk;
}
3 changes: 1 addition & 2 deletions sidekick_core/lib/src/commands/dart_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class DartCommand extends ForwardCommand {

@override
Future<void> run() async {
final exitCode = dart(argResults!.arguments);
exit(exitCode);
exitCode = dart(argResults!.arguments);
}
}
22 changes: 14 additions & 8 deletions sidekick_core/lib/src/commands/flutter_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,21 @@ class FlutterCommand extends ForwardCommand {

@override
Future<void> run() async {
// TODO find pinned fvm flutter version
final args = argResults!.arguments;
try {
final exitCode = flutterw(argResults!.arguments);
exit(exitCode);
} on FlutterWrapperNotFoundException catch (_) {
printerr(
'Could not find a pinned flutter version associated with the project.\n'
'Please install https://github.com/passsy/flutter_wrapper, to pin a flutter version',
);
exitCode = flutter(args);
} on FlutterSdkNotSetException catch (original) {
// for backwards compatibility link to the previous required flutter_wrapper location
try {
// ignore: deprecated_member_use_from_same_package
exitCode = flutterw(args);
printerr("Sidekick Warning: ${original.message}");
// success with flutterw, immediately return
return;
} on FlutterWrapperNotFoundException catch (_) {
// rethrow original below
}
rethrow /* original */;
}
}
}
63 changes: 54 additions & 9 deletions sidekick_core/lib/src/dart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,52 @@ int dart(
Directory? workingDirectory,
dcli.Progress? progress,
}) {
// TODO find pinned fvm flutter version
final binDir = repository.root.directory('.flutter/bin/cache/dart-sdk/bin/');
bool flutterwLegacyMode = false;

Directory? sdk = dartSdk;
if (sdk == null) {
if (flutterSdk != null) {
final embeddedSdk = flutterSdk!.directory('bin/cache/dart-sdk');
if (!embeddedSdk.existsSync()) {
// Flutter SDK is not fully initialized, the Dart SDK not yet downloaded
// Execute flutter_tool to download the embedded dart runtime
flutter([], workingDirectory: workingDirectory);
}
if (embeddedSdk.existsSync()) {
sdk = embeddedSdk;
}
}
if (sdk == null) {
final flutterWrapperLocation = findFlutterwLocation();
if (flutterWrapperLocation != null) {
// flutter_wrapper is installed, going into legacy mode for those which have not set the flutterSdkPath
final embeddedSdk =
repository.root.directory('.flutter/bin/cache/dart-sdk');
if (!embeddedSdk.existsSync()) {
// Flutter SDK is not fully initialized, the Dart SDK not yet downloaded
// Execute flutter_tool to download the embedded dart runtime
// ignore: deprecated_member_use_from_same_package
flutterw([], workingDirectory: workingDirectory);
}
if (embeddedSdk.existsSync()) {
sdk = embeddedSdk;
flutterwLegacyMode = true;
}
}
}
}
if (sdk == null) {
throw DartSdkNotSetException();
}

final dart = () {
if (Platform.isWindows) {
return binDir.file('dart.exe');
return sdk!.file('bin/dart.exe');
} else {
return binDir.file('dart');
return sdk!.file('bin/dart');
}
}();

if (!dart.existsSync()) {
// run a flutterw command forcing flutter_tool to download the dart sdk
print("running flutterw to download dart");
flutterw([], workingDirectory: mainProject!.root);
}
final process = dcli.startFromArgs(
dart.path,
args,
Expand All @@ -33,5 +64,19 @@ int dart(
nothrow: true,
terminal: progress == null,
);

if (flutterwLegacyMode) {
printerr("Sidekick Warning: ${DartSdkNotSetException().message}");
}
return process.exitCode ?? -1;
}

/// The Dart SDK path is not set in [initializeSidekick] (param [dartSdk], neither is is the [flutterSdk])
class DartSdkNotSetException implements Exception {
final String message =
"No Dart SDK set. Please set it in `initializeSidekick(dartSdkPath: 'path/to/sdk')`";
@override
String toString() {
return "DartSdkNotSetException{message: $message}";
}
}
49 changes: 49 additions & 0 deletions sidekick_core/lib/src/flutter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:dcli/dcli.dart' as dcli;
import 'package:sidekick_core/sidekick_core.dart';

/// Executes Flutter command from Flutter SDK set in [flutterSdk]
int flutter(
List<String> args, {
Directory? workingDirectory,
dcli.Progress? progress,
}) {
final workingDir =
workingDirectory?.absolute ?? entryWorkingDirectory.absolute;

final sdk = flutterSdk;
if (sdk == null) {
throw FlutterSdkNotSetException();
}

if (Platform.isWindows) {
final process = dcli.startFromArgs(
'bash',
[sdk.file('bin/flutter.exe').path, ...args],
workingDirectory: workingDir.path,
nothrow: true,
progress: progress,
terminal: progress == null,
);
return process.exitCode ?? -1;
} else {
final process = dcli.startFromArgs(
sdk.file('bin/flutter').path,
args,
workingDirectory: workingDir.path,
nothrow: true,
progress: progress,
terminal: progress == null,
);
return process.exitCode ?? -1;
}
}

/// The Flutter SDK path is not set in [initializeSidekick] (param [flutterSdk])
class FlutterSdkNotSetException implements Exception {
final String message =
"No Flutter SDK set. Please set it in `initializeSidekick(flutterSdkPath: 'path/to/sdk')`";
@override
String toString() {
return "FlutterSdkNotSetException{message: $message}";
}
}
22 changes: 13 additions & 9 deletions sidekick_core/lib/src/flutterw.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import 'package:dcli/dcli.dart' as dcli;
import 'package:sidekick_core/sidekick_core.dart';

/// Returns the closes `flutterw` file, searching upwards from [mainProject] or [Repository.cliPackageDir] or [repository]
File? findFlutterwLocation() {
final searchStart =
mainProject?.root ?? Repository.cliPackageDir ?? repository.root;
final flutterwParent =
searchStart.findParent((dir) => dir.file('flutterw').existsSync());
final flutterw = flutterwParent?.file('flutterw');
return flutterw;
}

/// Executes Flutter CLI (flutter_tool) via flutter_wrapper
///
/// https://github.com/passsy/flutter_wrapper
@Deprecated('Use flutter() instead')
int flutterw(
List<String> args, {
Directory? workingDirectory,
dcli.Progress? progress,
}) {
// find closest flutterw
final searchStart =
mainProject?.root ?? Repository.cliPackageDir ?? repository.root;
final flutterwParent =
searchStart.findParent((dir) => dir.file('flutterw').existsSync());
final flutterw = flutterwParent?.file('flutterw');

if (flutterw == null || !flutterw.existsSync()) {
final flutterw = findFlutterwLocation();
if (flutterw == null) {
throw FlutterWrapperNotFoundException();
}

final workingDir =
workingDirectory?.absolute ?? entryWorkingDirectory.absolute;

Expand Down
54 changes: 54 additions & 0 deletions sidekick_core/test/dart_command_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:sidekick_core/sidekick_core.dart';
import 'package:test/test.dart';

import 'fake_sdk.dart';
import 'init_test.dart';

void main() {
test('dart command works when dartSdkPath is set', () async {
await insideFakeSidekickProject((dir) async {
final runner = initializeSidekick(
name: 'dash',
dartSdkPath: fakeDartSdk().path,
);
runner.addCommand(DartCommand());
await runner.run(['dart']);
});
});

test('dart command fails when dartSdkPath is not set', () async {
await insideFakeSidekickProject((dir) async {
final runner = initializeSidekick(
name: 'dash',
// ignore: avoid_redundant_argument_values
dartSdkPath: null,
);
runner.addCommand(DartCommand());
expect(
() => runner.run(['dart']),
throwsA(isA<DartSdkNotSetException>()),
);
});
});

test('dart command links to embedded Dart SDK in Flutter SDK', () async {
await insideFakeSidekickProject((dir) async {
final runner = initializeSidekick(
name: 'dash',
flutterSdkPath: fakeFlutterSdk().path,
);
runner.addCommand(DartCommand());
await runner.run(['dart']);
});
});
}

Future<File> installFlutterWrapper(Directory directory) async {
writeAndRunShellScript(
r'sh -c "$(curl -fsSL https://raw.githubusercontent.com/passsy/flutter_wrapper/master/install.sh)"',
workingDirectory: directory,
);
final exe = directory.file('flutterw');
assert(exe.existsSync());
return exe;
}
Loading

0 comments on commit 8a7103a

Please sign in to comment.