fix(shorebird_cli): windows executable sanitization (#2988)

This commit is contained in:
Felix Angelov
2025-03-18 12:53:13 -05:00
committed by GitHub
parent 33140fb2cf
commit 00f9bc94d3
2 changed files with 71 additions and 5 deletions
@@ -6,6 +6,7 @@ import 'package:meta/meta.dart';
import 'package:scoped_deps/scoped_deps.dart';
import 'package:shorebird_cli/src/engine_config.dart';
import 'package:shorebird_cli/src/logging/logging.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
/// A reference to a [ShorebirdProcess] instance.
@@ -34,9 +35,6 @@ class ShorebirdProcess {
Map<String, String>? environment,
String? workingDirectory,
}) async {
logger.detail(
'''[Process.stream] $executable ${arguments.join(' ')}${workingDirectory == null ? '' : ' (in $workingDirectory)'}''',
);
final process = await start(
executable,
arguments,
@@ -178,9 +176,21 @@ class ShorebirdProcess {
required bool useVendedFlutter,
}) {
if (useVendedFlutter && executable == 'flutter') {
return shorebirdEnv.flutterBinaryFile.path;
return _sanitizeExecutablePath(shorebirdEnv.flutterBinaryFile.path);
}
return _sanitizeExecutablePath(executable);
}
/// Sanitizes the executable path on Windows.
/// https://github.com/dart-lang/sdk/issues/37751
String _sanitizeExecutablePath(String executable) {
if (executable.isEmpty) return executable;
if (!platform.isWindows) return executable;
if (executable.contains(' ') && !executable.contains('"')) {
// Use quoted strings to indicate where the file name ends and the arguments begin;
// otherwise, the file name is ambiguous.
return '"$executable"';
}
return executable;
}
@@ -1,12 +1,14 @@
// cspell:ignore asdfasdf
import 'dart:io';
import 'dart:io' hide Platform;
import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'package:scoped_deps/scoped_deps.dart';
import 'package:shorebird_cli/src/engine_config.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:test/test.dart';
@@ -21,6 +23,7 @@ void main() {
late EngineConfig engineConfig;
late ShorebirdLogger logger;
late Platform platform;
late ProcessWrapper processWrapper;
late Process startProcess;
late ShorebirdProcessResult runProcessResult;
@@ -33,6 +36,7 @@ void main() {
values: {
engineConfigRef.overrideWith(() => engineConfig),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
},
);
@@ -41,6 +45,7 @@ void main() {
setUp(() {
engineConfig = const EngineConfig.empty();
logger = MockShorebirdLogger();
platform = MockPlatform();
processWrapper = MockProcessWrapper();
runProcessResult = MockProcessResult();
startProcess = MockProcess();
@@ -58,6 +63,8 @@ void main() {
when(() => runProcessResult.exitCode).thenReturn(ExitCode.success.code);
when(() => logger.level).thenReturn(Level.info);
when(() => platform.isWindows).thenReturn(false);
});
test('ShorebirdProcessResult can be instantiated as a const', () {
@@ -94,6 +101,21 @@ void main() {
).called(1);
});
test('sanitizes executable on windows', () {
when(() => platform.isWindows).thenReturn(true);
const executable =
r'C:\Program Files\Android\Android Studio\jbr\bin\java.exe';
runWithOverrides(() => shorebirdProcess.run(executable, ['--version']));
verify(
() => processWrapper.run(
'"$executable"',
['--version'],
environment: any(named: 'environment'),
workingDirectory: any(named: 'workingDirectory'),
),
).called(1);
});
test('replaces "flutter" with our local flutter', () async {
await runWithOverrides(
() => shorebirdProcess.run('flutter', [
@@ -242,6 +264,23 @@ void main() {
).called(1);
});
test('sanitizes executable on windows', () {
when(() => platform.isWindows).thenReturn(true);
const executable =
r'C:\Program Files\Android\Android Studio\jbr\bin\java.exe';
runWithOverrides(
() => shorebirdProcess.runSync(executable, ['--version']),
);
verify(
() => processWrapper.runSync(
'"$executable"',
['--version'],
environment: any(named: 'environment'),
workingDirectory: any(named: 'workingDirectory'),
),
).called(1);
});
test('replaces "flutter" with our local flutter', () {
runWithOverrides(
() => shorebirdProcess.runSync('flutter', [
@@ -417,6 +456,23 @@ void main() {
).called(1);
});
test('sanitizes executable on windows', () {
when(() => platform.isWindows).thenReturn(true);
const executable =
r'C:\Program Files\Android\Android Studio\jbr\bin\java.exe';
runWithOverrides(
() => shorebirdProcess.start(executable, ['--version']),
);
verify(
() => processWrapper.start(
'"$executable"',
['--version'],
environment: any(named: 'environment'),
workingDirectory: any(named: 'workingDirectory'),
),
).called(1);
});
test('replaces "flutter" with our local flutter', () async {
await runWithOverrides(
() => shorebirdProcess.start('flutter', ['run']),