feat(shorebird_cli): shorebird preview stream logs on MacOS (#2688)
This commit is contained in:
@@ -75,6 +75,7 @@ Command: shorebird ${args.join(' ')}
|
||||
javaRef,
|
||||
loggerRef,
|
||||
networkCheckerRef,
|
||||
openRef,
|
||||
osInterfaceRef,
|
||||
patchExecutableRef,
|
||||
patchDiffCheckerRef,
|
||||
|
||||
@@ -21,7 +21,6 @@ import 'package:shorebird_cli/src/logging/logging.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_command.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
@@ -339,9 +338,15 @@ class PreviewCommand extends ShorebirdCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(felangel): wrap `open` and stream logs.
|
||||
final proc = await process.start('open', ['-n', appDirectory.path]);
|
||||
return proc.exitCode;
|
||||
final logs = await open.newApplication(path: appDirectory.path);
|
||||
final completer = Completer<void>();
|
||||
|
||||
logs.listen(
|
||||
(log) => logger.info(utf8.decode(log)),
|
||||
onDone: completer.complete,
|
||||
);
|
||||
|
||||
return completer.future.then((_) => ExitCode.success.code);
|
||||
}
|
||||
|
||||
Future<int> installAndLaunchAndroid({
|
||||
|
||||
@@ -10,6 +10,7 @@ export 'gradlew.dart';
|
||||
export 'idevicesyslog.dart';
|
||||
export 'ios_deploy.dart';
|
||||
export 'java.dart';
|
||||
export 'open.dart';
|
||||
export 'patch_executable.dart';
|
||||
export 'shorebird_tools.dart';
|
||||
export 'xcodebuild.dart';
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
|
||||
/// A reference to a [Open] instance.
|
||||
final openRef = create(Open.new);
|
||||
|
||||
/// The [Open] instance available in the current zone.
|
||||
Open get open => read(openRef);
|
||||
|
||||
/// A wrapper around the `open` command.
|
||||
/// https://ss64.com/mac/open.html
|
||||
class Open {
|
||||
/// Opens a new application at the provided [path] and streams the stdout and
|
||||
/// stderr.
|
||||
Future<Stream<List<int>>> newApplication({required String path}) async {
|
||||
final tmp = Directory.systemTemp.createTempSync();
|
||||
final stdout = File(p.join(tmp.path, 'stdout.log'))..createSync();
|
||||
await process.start(
|
||||
'open',
|
||||
[
|
||||
'-n',
|
||||
path,
|
||||
'--stdout=${stdout.path}',
|
||||
'--stderr=${stdout.path}',
|
||||
],
|
||||
);
|
||||
|
||||
final stdoutProcess = await process.start(
|
||||
'tail',
|
||||
['-fn+1', stdout.path],
|
||||
);
|
||||
|
||||
return stdoutProcess.stdout;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import 'package:shorebird_cli/src/http_client/http_client.dart';
|
||||
import 'package:shorebird_cli/src/logging/logging.dart';
|
||||
import 'package:shorebird_cli/src/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -1518,8 +1517,7 @@ channel: ${DeploymentTrack.staging.channel}
|
||||
const releasePlatform = ReleasePlatform.macos;
|
||||
|
||||
late Ditto ditto;
|
||||
late ShorebirdProcess shorebirdProcess;
|
||||
late Process process;
|
||||
late Open open;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
return HttpOverrides.runZoned(
|
||||
@@ -1537,7 +1535,7 @@ channel: ${DeploymentTrack.staging.channel}
|
||||
httpClientRef.overrideWith(() => httpClient),
|
||||
loggerRef.overrideWith(() => logger),
|
||||
platformRef.overrideWith(() => platform),
|
||||
processRef.overrideWith(() => shorebirdProcess),
|
||||
openRef.overrideWith(() => open),
|
||||
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
|
||||
shorebirdValidatorRef.overrideWith(() => shorebirdValidator),
|
||||
},
|
||||
@@ -1547,8 +1545,7 @@ channel: ${DeploymentTrack.staging.channel}
|
||||
|
||||
setUp(() {
|
||||
ditto = MockDitto();
|
||||
shorebirdProcess = MockShorebirdProcess();
|
||||
process = MockProcess();
|
||||
open = MockOpen();
|
||||
|
||||
when(() => releaseArtifact.id).thenReturn(macosArtifactId);
|
||||
when(() => argResults['platform']).thenReturn(releasePlatform.name);
|
||||
@@ -1572,9 +1569,9 @@ channel: ${DeploymentTrack.staging.channel}
|
||||
});
|
||||
when(() => releaseArtifact.url).thenReturn(releaseArtifactUrl);
|
||||
when(() => platform.isMacOS).thenReturn(true);
|
||||
when(() => shorebirdProcess.start(any(), any())).thenAnswer(
|
||||
(_) async => process,
|
||||
);
|
||||
when(
|
||||
() => open.newApplication(path: any(named: 'path')),
|
||||
).thenAnswer((_) async => Stream.value(utf8.encode('hello world')));
|
||||
});
|
||||
|
||||
group('when querying for release artifact fails', () {
|
||||
@@ -1638,16 +1635,10 @@ channel: ${DeploymentTrack.staging.channel}
|
||||
});
|
||||
|
||||
group('when process completes with exit code 0', () {
|
||||
setUp(() {
|
||||
when(() => process.exitCode).thenAnswer((_) async => 0);
|
||||
});
|
||||
|
||||
test('completes successfully', () async {
|
||||
final result = await runWithOverrides(command.run);
|
||||
expect(result, equals(ExitCode.success.code));
|
||||
verify(
|
||||
() => shorebirdProcess.start('open', any()),
|
||||
).called(1);
|
||||
verify(() => logger.info('hello world')).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:scoped_deps/scoped_deps.dart';
|
||||
import 'package:shorebird_cli/src/executables/executables.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../mocks.dart';
|
||||
|
||||
void main() {
|
||||
group(Open, () {
|
||||
late Open open;
|
||||
late ShorebirdProcess process;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
return runScoped(
|
||||
body,
|
||||
values: {
|
||||
processRef.overrideWith(() => process),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
process = MockShorebirdProcess();
|
||||
open = Open();
|
||||
});
|
||||
|
||||
group('newApplication', () {
|
||||
test('executes correct command and streams logs', () async {
|
||||
final openProcess = MockProcess();
|
||||
final tailProcess = MockProcess();
|
||||
|
||||
when(() => process.start('open', any())).thenAnswer((_) async {
|
||||
return openProcess;
|
||||
});
|
||||
when(() => process.start('tail', any())).thenAnswer((_) async {
|
||||
return tailProcess;
|
||||
});
|
||||
|
||||
when(() => tailProcess.stdout).thenAnswer(
|
||||
(_) => Stream.fromIterable([utf8.encode('hello world') as List<int>]),
|
||||
);
|
||||
|
||||
final stream = await runWithOverrides(
|
||||
() => open.newApplication(path: 'test'),
|
||||
);
|
||||
|
||||
expect(stream, emits(utf8.encode('hello world')));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -117,6 +117,8 @@ class MockJwtPayload extends Mock implements JwtPayload {}
|
||||
|
||||
class MockNetworkChecker extends Mock implements NetworkChecker {}
|
||||
|
||||
class MockOpen extends Mock implements Open {}
|
||||
|
||||
class MockOperatingSystemInterface extends Mock
|
||||
implements OperatingSystemInterface {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user