Disable sandboxing for macOS apps and tests in CI (#149618)
macOS 14 added new requirements that un-codesigned sandbox apps must be granted access when changed. Waiting for this UI caused macOS tests to fail on macOS 14 because the test runner forced codesigning off. Additionally, adding codesigning is not sufficient, since it must still be approved before codesigning is enough to pass the check. As a workaround, this PR disables sandboxing for macOS apps/tests in CI.  https://developer.apple.com/documentation/updates/security#June-2023) > App Sandbox now associates your macOS app with its sandbox container using its code signature. The operating system asks the person using your app to grant permission if it tries to access a sandbox container associated with a different app. For more information, see [Accessing files from the macOS App Sandbox](https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox). And that link explains why this is happening on a macOS 14 update: > In macOS 14 and later, the operating system uses your appâs code signature to associate it with its sandbox container. If your app tries to access the sandbox container owned by another app, the system asks the person using your app whether to grant access. If the person denies access and your app is already running, then it canât read or write the files in the other appâs sandbox container. If the person denies access while your app is launching and trying to enter the other appâs sandbox container, your app fails to launch. > > The operating system also tracks the association between an appâs code signing identity and its sandbox container for helper tools, including launch agents. If a person denies permission for a launch agent to enter its sandbox container and the app fails to start, launchd starts the launch agent again and the operating system re-requests access. Fixes https://github.com/flutter/flutter/issues/149268. Fixes framework part of https://github.com/flutter/flutter/issues/149264. Might fix packages issue: https://github.com/flutter/flutter/issues/149329. Verified framework tests: https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20plugin_test_macos/9/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20run_debug_test_macos/2/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20tool_integration_tests_4_4/2/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20integration_ui_test_test_macos/3/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac%20flavors_test_macos/3/overview https://ci.chromium.org/ui/p/flutter/builders/staging.shadow/Mac_benchmark%20complex_layout_scroll_perf_macos__timeline_summary/6/overview
This commit is contained in:
parent
f24bd99572
commit
529a4d2bac
@ -191,6 +191,13 @@ Future<bool> runXcodeTests({
|
|||||||
codeSignStyle = environment['FLUTTER_XCODE_CODE_SIGN_STYLE'];
|
codeSignStyle = environment['FLUTTER_XCODE_CODE_SIGN_STYLE'];
|
||||||
provisioningProfile = environment['FLUTTER_XCODE_PROVISIONING_PROFILE_SPECIFIER'];
|
provisioningProfile = environment['FLUTTER_XCODE_PROVISIONING_PROFILE_SPECIFIER'];
|
||||||
}
|
}
|
||||||
|
File? disabledSandboxEntitlementFile;
|
||||||
|
if (platformDirectory.endsWith('macos')) {
|
||||||
|
disabledSandboxEntitlementFile = _createDisabledSandboxEntitlementFile(
|
||||||
|
platformDirectory,
|
||||||
|
configuration,
|
||||||
|
);
|
||||||
|
}
|
||||||
final String resultBundleTemp = Directory.systemTemp.createTempSync('flutter_xcresult.').path;
|
final String resultBundleTemp = Directory.systemTemp.createTempSync('flutter_xcresult.').path;
|
||||||
final String resultBundlePath = path.join(resultBundleTemp, 'result');
|
final String resultBundlePath = path.join(resultBundleTemp, 'result');
|
||||||
final int testResultExit = await exec(
|
final int testResultExit = await exec(
|
||||||
@ -214,6 +221,8 @@ Future<bool> runXcodeTests({
|
|||||||
'CODE_SIGN_STYLE=$codeSignStyle',
|
'CODE_SIGN_STYLE=$codeSignStyle',
|
||||||
if (provisioningProfile != null)
|
if (provisioningProfile != null)
|
||||||
'PROVISIONING_PROFILE_SPECIFIER=$provisioningProfile',
|
'PROVISIONING_PROFILE_SPECIFIER=$provisioningProfile',
|
||||||
|
if (disabledSandboxEntitlementFile != null)
|
||||||
|
'CODE_SIGN_ENTITLEMENTS=${disabledSandboxEntitlementFile.path}',
|
||||||
],
|
],
|
||||||
workingDirectory: platformDirectory,
|
workingDirectory: platformDirectory,
|
||||||
canFail: true,
|
canFail: true,
|
||||||
@ -247,3 +256,55 @@ Future<bool> runXcodeTests({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds and copies macOS entitlements file. In the copy, disables sandboxing.
|
||||||
|
/// If entitlements file is not found, returns null.
|
||||||
|
///
|
||||||
|
/// As of macOS 14, testing a macOS sandbox app may prompt the user to grant
|
||||||
|
/// access to the app. To workaround this in CI, we create and use a entitlements
|
||||||
|
/// file with sandboxing disabled. See
|
||||||
|
/// https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox.
|
||||||
|
File? _createDisabledSandboxEntitlementFile(
|
||||||
|
String platformDirectory,
|
||||||
|
String configuration,
|
||||||
|
) {
|
||||||
|
String entitlementDefaultFileName;
|
||||||
|
if (configuration == 'Release') {
|
||||||
|
entitlementDefaultFileName = 'Release';
|
||||||
|
} else {
|
||||||
|
entitlementDefaultFileName = 'DebugProfile';
|
||||||
|
}
|
||||||
|
|
||||||
|
final String entitlementFilePath = path.join(
|
||||||
|
platformDirectory,
|
||||||
|
'Runner',
|
||||||
|
'$entitlementDefaultFileName.entitlements',
|
||||||
|
);
|
||||||
|
final File entitlementFile = File(entitlementFilePath);
|
||||||
|
|
||||||
|
if (!entitlementFile.existsSync()) {
|
||||||
|
print('Unable to find entitlements file at ${entitlementFile.path}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String originalEntitlementFileContents =
|
||||||
|
entitlementFile.readAsStringSync();
|
||||||
|
final String tempEntitlementPath = Directory.systemTemp
|
||||||
|
.createTempSync('flutter_disable_sandbox_entitlement.')
|
||||||
|
.path;
|
||||||
|
final File disabledSandboxEntitlementFile = File(path.join(
|
||||||
|
tempEntitlementPath,
|
||||||
|
'${entitlementDefaultFileName}WithDisabledSandboxing.entitlements',
|
||||||
|
));
|
||||||
|
disabledSandboxEntitlementFile.createSync(recursive: true);
|
||||||
|
disabledSandboxEntitlementFile.writeAsStringSync(
|
||||||
|
originalEntitlementFileContents.replaceAll(
|
||||||
|
RegExp(r'<key>com\.apple\.security\.app-sandbox<\/key>[\S\s]*?<true\/>'),
|
||||||
|
'''
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>''',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return disabledSandboxEntitlementFile;
|
||||||
|
}
|
||||||
|
@ -72,6 +72,7 @@ class BuildMacosCommand extends BuildSubCommand {
|
|||||||
flutterUsage: globals.flutterUsage,
|
flutterUsage: globals.flutterUsage,
|
||||||
analytics: analytics,
|
analytics: analytics,
|
||||||
),
|
),
|
||||||
|
usingCISystem: usingCISystem,
|
||||||
);
|
);
|
||||||
return FlutterCommandResult.success();
|
return FlutterCommandResult.success();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import 'convert.dart';
|
|||||||
import 'devfs.dart';
|
import 'devfs.dart';
|
||||||
import 'device.dart';
|
import 'device.dart';
|
||||||
import 'device_port_forwarder.dart';
|
import 'device_port_forwarder.dart';
|
||||||
|
import 'globals.dart' as globals;
|
||||||
|
import 'macos/macos_device.dart';
|
||||||
import 'protocol_discovery.dart';
|
import 'protocol_discovery.dart';
|
||||||
|
|
||||||
/// A partial implementation of Device for desktop-class devices to inherit
|
/// A partial implementation of Device for desktop-class devices to inherit
|
||||||
@ -119,6 +121,7 @@ abstract class DesktopDevice extends Device {
|
|||||||
await buildForDevice(
|
await buildForDevice(
|
||||||
buildInfo: debuggingOptions.buildInfo,
|
buildInfo: debuggingOptions.buildInfo,
|
||||||
mainPath: mainPath,
|
mainPath: mainPath,
|
||||||
|
usingCISystem: debuggingOptions.usingCISystem,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,8 +162,39 @@ abstract class DesktopDevice extends Device {
|
|||||||
logger: _logger,
|
logger: _logger,
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
|
Timer? timer;
|
||||||
|
if (this is MacOSDevice) {
|
||||||
|
if (await globals.isRunningOnBot) {
|
||||||
|
const int defaultTimeout = 5;
|
||||||
|
timer = Timer(const Duration(minutes: defaultTimeout), () {
|
||||||
|
// As of macOS 14, if sandboxing is enabled and the app is not codesigned,
|
||||||
|
// a dialog will prompt the user to allow the app to run. This will
|
||||||
|
// cause tests in CI to hang. In CI, we workaround this by setting
|
||||||
|
// the CODE_SIGN_ENTITLEMENTS build setting to a version with
|
||||||
|
// sandboxing disabled.
|
||||||
|
final String sandboxingMessage;
|
||||||
|
if (debuggingOptions.usingCISystem) {
|
||||||
|
sandboxingMessage = 'Ensure sandboxing is disabled by checking '
|
||||||
|
'the set CODE_SIGN_ENTITLEMENTS.';
|
||||||
|
} else {
|
||||||
|
sandboxingMessage = 'Consider codesigning your app or disabling '
|
||||||
|
'sandboxing. Flutter will attempt to disable sandboxing if '
|
||||||
|
'the `--ci` flag is provided.';
|
||||||
|
}
|
||||||
|
_logger.printError(
|
||||||
|
'The Dart VM Service was not discovered after $defaultTimeout '
|
||||||
|
'minutes. If the app has sandboxing enabled and is not '
|
||||||
|
'codesigned or codesigning changed, this may be caused by a '
|
||||||
|
'system prompt asking for access. $sandboxingMessage\n'
|
||||||
|
'See https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox '
|
||||||
|
'for more information.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Uri? vmServiceUri = await vmServiceDiscovery.uri;
|
final Uri? vmServiceUri = await vmServiceDiscovery.uri;
|
||||||
if (vmServiceUri != null) {
|
if (vmServiceUri != null) {
|
||||||
|
timer?.cancel();
|
||||||
onAttached(package, buildInfo, process);
|
onAttached(package, buildInfo, process);
|
||||||
return LaunchResult.succeeded(vmServiceUri: vmServiceUri);
|
return LaunchResult.succeeded(vmServiceUri: vmServiceUri);
|
||||||
}
|
}
|
||||||
@ -199,6 +233,7 @@ abstract class DesktopDevice extends Device {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
required BuildInfo buildInfo,
|
required BuildInfo buildInfo,
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
|
bool usingCISystem = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Returns the path to the executable to run for [package] on this device for
|
/// Returns the path to the executable to run for [package] on this device for
|
||||||
|
@ -62,6 +62,7 @@ class LinuxDevice extends DesktopDevice {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
required BuildInfo buildInfo,
|
required BuildInfo buildInfo,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
await buildLinux(
|
await buildLinux(
|
||||||
FlutterProject.current().linux,
|
FlutterProject.current().linux,
|
||||||
|
@ -65,6 +65,7 @@ Future<void> buildMacOS({
|
|||||||
required bool verboseLogging,
|
required bool verboseLogging,
|
||||||
bool configOnly = false,
|
bool configOnly = false,
|
||||||
SizeAnalyzer? sizeAnalyzer,
|
SizeAnalyzer? sizeAnalyzer,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
final Directory? xcodeWorkspace = flutterProject.macos.xcodeWorkspace;
|
final Directory? xcodeWorkspace = flutterProject.macos.xcodeWorkspace;
|
||||||
if (xcodeWorkspace == null) {
|
if (xcodeWorkspace == null) {
|
||||||
@ -153,6 +154,19 @@ Future<void> buildMacOS({
|
|||||||
'Building macOS application...',
|
'Building macOS application...',
|
||||||
);
|
);
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
|
File? disabledSandboxEntitlementFile;
|
||||||
|
if (usingCISystem) {
|
||||||
|
disabledSandboxEntitlementFile = _createDisabledSandboxEntitlementFile(
|
||||||
|
flutterProject.macos,
|
||||||
|
configuration,
|
||||||
|
);
|
||||||
|
if (disabledSandboxEntitlementFile != null) {
|
||||||
|
globals.logger.printStatus(
|
||||||
|
'Detected macOS app running in CI, turning off sandboxing.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await globals.processUtils.stream(<String>[
|
result = await globals.processUtils.stream(<String>[
|
||||||
'/usr/bin/env',
|
'/usr/bin/env',
|
||||||
@ -170,6 +184,8 @@ Future<void> buildMacOS({
|
|||||||
else
|
else
|
||||||
'-quiet',
|
'-quiet',
|
||||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||||
|
if (disabledSandboxEntitlementFile != null)
|
||||||
|
'CODE_SIGN_ENTITLEMENTS=${disabledSandboxEntitlementFile.path}',
|
||||||
...environmentVariablesAsXcodeBuildSettings(globals.platform),
|
...environmentVariablesAsXcodeBuildSettings(globals.platform),
|
||||||
],
|
],
|
||||||
trace: true,
|
trace: true,
|
||||||
@ -271,3 +287,52 @@ Future<void> _writeCodeSizeAnalysis(BuildInfo buildInfo, SizeAnalyzer? sizeAnaly
|
|||||||
'dart devtools --appSizeBase=$relativeAppSizePath'
|
'dart devtools --appSizeBase=$relativeAppSizePath'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Finds and copies macOS entitlements file. In the copy, disables sandboxing.
|
||||||
|
/// If entitlements file is not found, returns null.
|
||||||
|
///
|
||||||
|
/// As of macOS 14, running a macOS sandbox app may prompt the user to grant
|
||||||
|
/// access to the app. To workaround this in CI, we create and use a entitlements
|
||||||
|
/// file with sandboxing disabled. See
|
||||||
|
/// https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox.
|
||||||
|
File? _createDisabledSandboxEntitlementFile(
|
||||||
|
MacOSProject macos,
|
||||||
|
String configuration,
|
||||||
|
) {
|
||||||
|
String entitlementDefaultFileName;
|
||||||
|
if (configuration == 'Release') {
|
||||||
|
entitlementDefaultFileName = 'Release';
|
||||||
|
} else {
|
||||||
|
entitlementDefaultFileName = 'DebugProfile';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vashworth): Once https://github.com/flutter/flutter/issues/146204 is
|
||||||
|
// fixed, it would be better to get the path to the entitlement file from the
|
||||||
|
// project's build settings (CODE_SIGN_ENTITLEMENTS).
|
||||||
|
final File entitlementFile = macos.hostAppRoot
|
||||||
|
.childDirectory('Runner')
|
||||||
|
.childFile('$entitlementDefaultFileName.entitlements');
|
||||||
|
|
||||||
|
if (!entitlementFile.existsSync()) {
|
||||||
|
globals.logger.printTrace(
|
||||||
|
'Unable to find entitlements file at ${entitlementFile.path}');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String entitlementFileContents = entitlementFile.readAsStringSync();
|
||||||
|
final File disabledSandboxEntitlementFile = globals.fs.systemTempDirectory
|
||||||
|
.createTempSync('flutter_disable_sandbox_entitlement.')
|
||||||
|
.childFile(
|
||||||
|
'${entitlementDefaultFileName}WithDisabledSandboxing.entitlements',
|
||||||
|
);
|
||||||
|
disabledSandboxEntitlementFile.createSync(recursive: true);
|
||||||
|
disabledSandboxEntitlementFile.writeAsStringSync(
|
||||||
|
entitlementFileContents.replaceAll(
|
||||||
|
RegExp(r'<key>com\.apple\.security\.app-sandbox<\/key>[\S\s]*?<true\/>'),
|
||||||
|
'''
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>''',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return disabledSandboxEntitlementFile;
|
||||||
|
}
|
||||||
|
@ -70,12 +70,14 @@ class MacOSDevice extends DesktopDevice {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
required BuildInfo buildInfo,
|
required BuildInfo buildInfo,
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
await buildMacOS(
|
await buildMacOS(
|
||||||
flutterProject: FlutterProject.current(),
|
flutterProject: FlutterProject.current(),
|
||||||
buildInfo: buildInfo,
|
buildInfo: buildInfo,
|
||||||
targetOverride: mainPath,
|
targetOverride: mainPath,
|
||||||
verboseLogging: _logger.isVerbose,
|
verboseLogging: _logger.isVerbose,
|
||||||
|
usingCISystem: usingCISystem,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,7 @@ class MacOSDesignedForIPadDevice extends DesktopDevice {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
required BuildInfo buildInfo,
|
required BuildInfo buildInfo,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
// Only attaching to a running app launched from Xcode is supported.
|
// Only attaching to a running app launched from Xcode is supported.
|
||||||
throw UnimplementedError('Building for "$name" is not supported.');
|
throw UnimplementedError('Building for "$name" is not supported.');
|
||||||
|
@ -376,7 +376,16 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
String? get packagesPath => stringArg(FlutterGlobalOptions.kPackagesOption, global: true);
|
String? get packagesPath => stringArg(FlutterGlobalOptions.kPackagesOption, global: true);
|
||||||
|
|
||||||
/// Whether flutter is being run from our CI.
|
/// Whether flutter is being run from our CI.
|
||||||
bool get usingCISystem => boolArg(FlutterGlobalOptions.kContinuousIntegrationFlag, global: true);
|
///
|
||||||
|
/// This is true if `--ci` is passed to the command or if environment
|
||||||
|
/// variable `LUCI_CI` is `True`.
|
||||||
|
bool get usingCISystem {
|
||||||
|
return boolArg(
|
||||||
|
FlutterGlobalOptions.kContinuousIntegrationFlag,
|
||||||
|
global: true,
|
||||||
|
) ||
|
||||||
|
globals.platform.environment['LUCI_CI'] == 'True';
|
||||||
|
}
|
||||||
|
|
||||||
String? get debugLogsDirectoryPath => stringArg(FlutterGlobalOptions.kDebugLogsDirectoryFlag, global: true);
|
String? get debugLogsDirectoryPath => stringArg(FlutterGlobalOptions.kDebugLogsDirectoryFlag, global: true);
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ class WindowsDevice extends DesktopDevice {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
required BuildInfo buildInfo,
|
required BuildInfo buildInfo,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
await buildWindows(
|
await buildWindows(
|
||||||
FlutterProject.current().windows,
|
FlutterProject.current().windows,
|
||||||
|
@ -6,6 +6,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:file_testing/file_testing.dart';
|
||||||
import 'package:flutter_tools/src/artifacts.dart';
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
@ -109,7 +110,12 @@ void main() {
|
|||||||
|
|
||||||
// Creates a FakeCommand for the xcodebuild call to build the app
|
// Creates a FakeCommand for the xcodebuild call to build the app
|
||||||
// in the given configuration.
|
// in the given configuration.
|
||||||
FakeCommand setUpFakeXcodeBuildHandler(String configuration, { bool verbose = false, void Function(List<String> command)? onRun }) {
|
FakeCommand setUpFakeXcodeBuildHandler(
|
||||||
|
String configuration, {
|
||||||
|
bool verbose = false,
|
||||||
|
void Function(List<String> command)? onRun,
|
||||||
|
List<String>? additionalCommandArguements,
|
||||||
|
}) {
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
||||||
final Directory flutterBuildDir = fileSystem.directory(getMacOSBuildDirectory());
|
final Directory flutterBuildDir = fileSystem.directory(getMacOSBuildDirectory());
|
||||||
return FakeCommand(
|
return FakeCommand(
|
||||||
@ -129,6 +135,8 @@ void main() {
|
|||||||
else
|
else
|
||||||
'-quiet',
|
'-quiet',
|
||||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||||
|
if (additionalCommandArguements != null)
|
||||||
|
...additionalCommandArguements,
|
||||||
],
|
],
|
||||||
stdout: '''
|
stdout: '''
|
||||||
STDOUT STUFF
|
STDOUT STUFF
|
||||||
@ -706,4 +714,136 @@ STDERR STUFF
|
|||||||
Usage: () => usage,
|
Usage: () => usage,
|
||||||
Analytics: () => fakeAnalytics,
|
Analytics: () => fakeAnalytics,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('macOS build overrides CODE_SIGN_ENTITLEMENTS when in CI if entitlement file exists (debug)', () async {
|
||||||
|
final BuildCommand command = BuildCommand(
|
||||||
|
artifacts: artifacts,
|
||||||
|
androidSdk: FakeAndroidSdk(),
|
||||||
|
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
processUtils: processUtils,
|
||||||
|
osUtils: FakeOperatingSystemUtils(),
|
||||||
|
);
|
||||||
|
createMinimalMockProjectFiles();
|
||||||
|
|
||||||
|
final File entitlementFile = fileSystem.file(fileSystem.path.join('macos', 'Runner', 'DebugProfile.entitlements'));
|
||||||
|
entitlementFile.createSync(recursive: true);
|
||||||
|
entitlementFile.writeAsStringSync('''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
|
''');
|
||||||
|
|
||||||
|
await createTestCommandRunner(command).run(
|
||||||
|
const <String>['build', 'macos', '--debug', '--no-pub']
|
||||||
|
);
|
||||||
|
|
||||||
|
final File tempEntitlementFile = fileSystem.systemTempDirectory.childFile('flutter_disable_sandbox_entitlement.rand0/DebugProfileWithDisabledSandboxing.entitlements');
|
||||||
|
expect(tempEntitlementFile, exists);
|
||||||
|
expect(tempEntitlementFile.readAsStringSync(), '''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
|
''');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||||
|
setUpFakeXcodeBuildHandler(
|
||||||
|
'Debug',
|
||||||
|
additionalCommandArguements: <String>[
|
||||||
|
'CODE_SIGN_ENTITLEMENTS=/.tmp_rand0/flutter_disable_sandbox_entitlement.rand0/DebugProfileWithDisabledSandboxing.entitlements',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
Platform: () => FakePlatform(
|
||||||
|
operatingSystem: 'macos',
|
||||||
|
environment: <String, String>{
|
||||||
|
'FLUTTER_ROOT': '/',
|
||||||
|
'HOME': '/',
|
||||||
|
'LUCI_CI': 'True'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('macOS build overrides CODE_SIGN_ENTITLEMENTS when in CI if entitlement file exists (release)', () async {
|
||||||
|
final BuildCommand command = BuildCommand(
|
||||||
|
artifacts: artifacts,
|
||||||
|
androidSdk: FakeAndroidSdk(),
|
||||||
|
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
logger: logger,
|
||||||
|
processUtils: processUtils,
|
||||||
|
osUtils: FakeOperatingSystemUtils(),
|
||||||
|
);
|
||||||
|
createMinimalMockProjectFiles();
|
||||||
|
|
||||||
|
final File entitlementFile = fileSystem.file(
|
||||||
|
fileSystem.path.join('macos', 'Runner', 'Release.entitlements'),
|
||||||
|
);
|
||||||
|
entitlementFile.createSync(recursive: true);
|
||||||
|
entitlementFile.writeAsStringSync('''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
|
''');
|
||||||
|
|
||||||
|
await createTestCommandRunner(command).run(
|
||||||
|
const <String>['build', 'macos', '--release', '--no-pub']
|
||||||
|
);
|
||||||
|
|
||||||
|
final File tempEntitlementFile = fileSystem.systemTempDirectory.childFile(
|
||||||
|
'flutter_disable_sandbox_entitlement.rand0/ReleaseWithDisabledSandboxing.entitlements',
|
||||||
|
);
|
||||||
|
expect(tempEntitlementFile, exists);
|
||||||
|
expect(tempEntitlementFile.readAsStringSync(), '''
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.app-sandbox</key>
|
||||||
|
<false/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
|
||||||
|
''');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||||
|
setUpFakeXcodeBuildHandler(
|
||||||
|
'Release',
|
||||||
|
additionalCommandArguements: <String>[
|
||||||
|
'CODE_SIGN_ENTITLEMENTS=/.tmp_rand0/flutter_disable_sandbox_entitlement.rand0/ReleaseWithDisabledSandboxing.entitlements',
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
Platform: () => FakePlatform(
|
||||||
|
operatingSystem: 'macos',
|
||||||
|
environment: <String, String>{
|
||||||
|
'FLUTTER_ROOT': '/',
|
||||||
|
'HOME': '/',
|
||||||
|
'LUCI_CI': 'True'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/common.dart';
|
|||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/base/terminal.dart';
|
import 'package:flutter_tools/src/base/terminal.dart';
|
||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
@ -1201,6 +1202,26 @@ void main() {
|
|||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('usingCISystem can also be set by environment LUCI_CI', () async {
|
||||||
|
final RunCommand command = RunCommand();
|
||||||
|
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
||||||
|
'run',
|
||||||
|
]), throwsToolExit());
|
||||||
|
|
||||||
|
final DebuggingOptions options = await command.createDebuggingOptions(false);
|
||||||
|
|
||||||
|
expect(options.usingCISystem, true);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
||||||
|
FileSystem: () => MemoryFileSystem.test(),
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
Platform: () => FakePlatform(
|
||||||
|
environment: <String, String>{
|
||||||
|
'LUCI_CI': 'True'
|
||||||
|
}
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('wasm mode selects skwasm renderer by default', () async {
|
testUsingContext('wasm mode selects skwasm renderer by default', () async {
|
||||||
final RunCommand command = RunCommand();
|
final RunCommand command = RunCommand();
|
||||||
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
await expectLater(() => createTestCommandRunner(command).run(<String>[
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:fake_async/fake_async.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/application_package.dart';
|
import 'package:flutter_tools/src/application_package.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
@ -14,12 +15,13 @@ import 'package:flutter_tools/src/desktop_device.dart';
|
|||||||
import 'package:flutter_tools/src/devfs.dart';
|
import 'package:flutter_tools/src/devfs.dart';
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
||||||
|
import 'package:flutter_tools/src/macos/macos_device.dart';
|
||||||
import 'package:flutter_tools/src/project.dart';
|
import 'package:flutter_tools/src/project.dart';
|
||||||
|
|
||||||
import 'package:test/fake.dart';
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
import '../src/fake_process_manager.dart';
|
import '../src/context.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('Basic info', () {
|
group('Basic info', () {
|
||||||
@ -364,6 +366,33 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('macOS devices print warning if Dart VM not found within timeframe in CI', () async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
final FakeMacOSDevice device = FakeMacOSDevice(
|
||||||
|
fileSystem: MemoryFileSystem.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
operatingSystemUtils: FakeOperatingSystemUtils(),
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
final FakeApplicationPackage package = FakeApplicationPackage();
|
||||||
|
|
||||||
|
FakeAsync().run((FakeAsync fakeAsync) {
|
||||||
|
device.startApp(
|
||||||
|
package,
|
||||||
|
prebuiltApplication: true,
|
||||||
|
debuggingOptions: DebuggingOptions.enabled(
|
||||||
|
BuildInfo.debug,
|
||||||
|
enableImpeller: ImpellerStatus.disabled,
|
||||||
|
dartEntrypointArgs: <String>[],
|
||||||
|
usingCISystem: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
fakeAsync.flushTimers();
|
||||||
|
expect(logger.errorText, contains('Ensure sandboxing is disabled by checking the set CODE_SIGN_ENTITLEMENTS'));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FakeDesktopDevice setUpDesktopDevice({
|
FakeDesktopDevice setUpDesktopDevice({
|
||||||
@ -424,6 +453,7 @@ class FakeDesktopDevice extends DesktopDevice {
|
|||||||
Future<void> buildForDevice({
|
Future<void> buildForDevice({
|
||||||
String? mainPath,
|
String? mainPath,
|
||||||
BuildInfo? buildInfo,
|
BuildInfo? buildInfo,
|
||||||
|
bool usingCISystem = false,
|
||||||
}) async {
|
}) async {
|
||||||
lastBuiltMainPath = mainPath;
|
lastBuiltMainPath = mainPath;
|
||||||
lastBuildInfo = buildInfo;
|
lastBuildInfo = buildInfo;
|
||||||
@ -444,3 +474,38 @@ class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
|
|||||||
@override
|
@override
|
||||||
String get name => 'Example';
|
String get name => 'Example';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeMacOSDevice extends MacOSDevice {
|
||||||
|
FakeMacOSDevice({
|
||||||
|
required super.processManager,
|
||||||
|
required super.logger,
|
||||||
|
required super.fileSystem,
|
||||||
|
required super.operatingSystemUtils,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => 'dummy';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.tester;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isSupported() => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isSupportedForProject(FlutterProject flutterProject) => true;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> buildForDevice({
|
||||||
|
String? mainPath,
|
||||||
|
BuildInfo? buildInfo,
|
||||||
|
bool usingCISystem = false,
|
||||||
|
}) async {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy implementation that just returns the build mode name.
|
||||||
|
@override
|
||||||
|
String? executablePathForDevice(ApplicationPackage package, BuildInfo buildInfo) {
|
||||||
|
return buildInfo.mode.cliName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user