Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(shorebird_cli): ShorebirdFlutterManager use Git wrapper #1050

Merged
merged 1 commit into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/shorebird_cli/bin/shorebird.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/command_runner.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/git.dart';
import 'package:shorebird_cli/src/gradlew.dart';
import 'package:shorebird_cli/src/ios_deploy.dart';
import 'package:shorebird_cli/src/java.dart';
Expand Down Expand Up @@ -37,6 +38,7 @@ Future<void> main(List<String> args) async {
codePushClientWrapperRef,
doctorRef,
engineConfigRef,
gitRef,
gradlewRef,
iosDeployRef,
javaRef,
Expand Down
7 changes: 7 additions & 0 deletions packages/shorebird_cli/lib/src/git.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import 'dart:io';

import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/process.dart';

/// A reference to a [Git] instance.
final gitRef = create(Git.new);

/// The [Git] instance available in the current zone.
Git get git => read(gitRef);

/// A wrapper around all git related functionality.
class Git {
static const executable = 'git';
Expand Down
52 changes: 11 additions & 41 deletions packages/shorebird_cli/lib/src/shorebird_flutter_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:io';

import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/git.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';

/// A reference to a [ShorebirdFlutterManager] instance.
Expand All @@ -28,51 +28,21 @@ class ShorebirdFlutterManager {
p.join(shorebirdEnv.flutterDirectory.parent.path, revision),
);
if (targetDirectory.existsSync()) return;
const executable = 'git';

// Clone the Shorebird Flutter repo into the target directory.
final cloneArgs = [
'clone',
'--filter=tree:0',
flutterGitUrl,
'--no-checkout',
targetDirectory.path,
];
final cloneResult = await process.run(
executable,
cloneArgs,
runInShell: true,
await git.clone(
url: flutterGitUrl,
outputDirectory: targetDirectory.path,
args: [
'--filter=tree:0',
'--no-checkout',
],
);
if (cloneResult.exitCode != 0) {
throw ProcessException(
executable,
cloneArgs,
'${cloneResult.stderr}',
cloneResult.exitCode,
);
}

// Checkout the correct revision.
final checkoutArgs = [
'-C',
targetDirectory.path,
'-c',
'advice.detachedHead=false',
'checkout',
revision,
];
final checkoutResult = await process.run(
executable,
checkoutArgs,
runInShell: true,
await git.checkout(
directory: targetDirectory.path,
revision: revision,
);
if (checkoutResult.exitCode != 0) {
throw ProcessException(
executable,
checkoutArgs,
'${checkoutResult.stderr}',
checkoutResult.exitCode,
);
}
}
}
126 changes: 52 additions & 74 deletions packages/shorebird_cli/test/src/shorebird_flutter_manager_test.dart
Original file line number Diff line number Diff line change
@@ -1,36 +1,30 @@
import 'dart:io';

import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/git.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_flutter_manager.dart';
import 'package:test/test.dart';

class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}

class _MockShorebirdProcess extends Mock implements ShorebirdProcess {}
class _MockGit extends Mock implements Git {}

class _MockShorebirdProcessResult extends Mock
implements ShorebirdProcessResult {}
class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}

void main() {
group(ShorebirdFlutterManager, () {
late Directory shorebirdRoot;
late Directory flutterDirectory;
late Git git;
late ShorebirdEnv shorebirdEnv;
late ShorebirdProcessResult cloneProcessResult;
late ShorebirdProcessResult checkoutProcessResult;
late ShorebirdProcess shorebirdProcess;
late ShorebirdFlutterManager shorebirdFlutterManager;

R runWithOverrides<R>(R Function() body) {
return runScoped(
body,
values: {
processRef.overrideWith(() => shorebirdProcess),
gitRef.overrideWith(() => git),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
},
);
Expand All @@ -39,34 +33,24 @@ void main() {
setUp(() {
shorebirdRoot = Directory.systemTemp.createTempSync();
flutterDirectory = Directory(p.join(shorebirdRoot.path, 'flutter'));
git = _MockGit();
shorebirdEnv = _MockShorebirdEnv();
cloneProcessResult = _MockShorebirdProcessResult();
checkoutProcessResult = _MockShorebirdProcessResult();
shorebirdProcess = _MockShorebirdProcess();
shorebirdFlutterManager = runWithOverrides(ShorebirdFlutterManager.new);

when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory);
when(
() => shorebirdProcess.run(
any(),
any(),
runInShell: any(named: 'runInShell'),
() => git.clone(
url: any(named: 'url'),
outputDirectory: any(named: 'outputDirectory'),
args: any(named: 'args'),
),
).thenAnswer((invocation) async {
final executable = invocation.positionalArguments[0] as String;
final args = invocation.positionalArguments[1] as List<String>;
if (executable == 'git' && args[0] == 'clone') {
return cloneProcessResult;
} else if (executable == 'git' && args[4] == 'checkout') {
return checkoutProcessResult;
} else {
throw UnimplementedError();
}
});
when(() => cloneProcessResult.exitCode).thenReturn(ExitCode.success.code);
).thenAnswer((_) async => {});
when(
() => checkoutProcessResult.exitCode,
).thenReturn(ExitCode.success.code);
() => git.checkout(
directory: any(named: 'directory'),
revision: any(named: 'revision'),
),
).thenAnswer((_) async => {});
when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory);
});

group('installRevision', () {
Expand All @@ -80,73 +64,67 @@ void main() {
() => shorebirdFlutterManager.installRevision(revision: revision),
);

verifyNever(() => shorebirdProcess.run(any(), any()));
verifyNever(
() => git.clone(
url: any(named: 'url'),
outputDirectory: any(named: 'outputDirectory'),
args: any(named: 'args'),
),
);
});

test('throws ProcessException if unable to clone', () async {
test('throws exception if unable to clone', () async {
final exception = Exception('oops');
when(
() => cloneProcessResult.exitCode,
).thenReturn(ExitCode.software.code);
() => git.clone(
url: any(named: 'url'),
outputDirectory: any(named: 'outputDirectory'),
args: any(named: 'args'),
),
).thenThrow(exception);

await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
),
throwsA(isA<ProcessException>()),
throwsA(exception),
);

verify(
() => shorebirdProcess.run(
'git',
[
'clone',
'--filter=tree:0',
ShorebirdFlutterManager.flutterGitUrl,
'--no-checkout',
p.join(flutterDirectory.parent.path, revision)
],
runInShell: true,
() => git.clone(
url: ShorebirdFlutterManager.flutterGitUrl,
outputDirectory: p.join(flutterDirectory.parent.path, revision),
args: ['--filter=tree:0', '--no-checkout'],
),
).called(1);
});

test('throws ProcessException if unable to checkout revision', () async {
test('throws exception if unable to checkout revision', () async {
final exception = Exception('oops');
when(
() => checkoutProcessResult.exitCode,
).thenReturn(ExitCode.software.code);
() => git.checkout(
directory: any(named: 'directory'),
revision: any(named: 'revision'),
),
).thenThrow(exception);

await expectLater(
runWithOverrides(
() => shorebirdFlutterManager.installRevision(revision: revision),
),
throwsA(isA<ProcessException>()),
throwsA(exception),
);

verify(
() => shorebirdProcess.run(
'git',
[
'clone',
'--filter=tree:0',
ShorebirdFlutterManager.flutterGitUrl,
'--no-checkout',
p.join(flutterDirectory.parent.path, revision)
],
runInShell: true,
() => git.clone(
url: ShorebirdFlutterManager.flutterGitUrl,
outputDirectory: p.join(flutterDirectory.parent.path, revision),
args: ['--filter=tree:0', '--no-checkout'],
),
).called(1);
verify(
() => shorebirdProcess.run(
'git',
[
'-C',
p.join(flutterDirectory.parent.path, revision),
'-c',
'advice.detachedHead=false',
'checkout',
revision,
],
runInShell: true,
() => git.checkout(
directory: p.join(flutterDirectory.parent.path, revision),
revision: revision,
),
).called(1);
});
Expand Down