@@ -65,6 +65,7 @@ Future<void> buildMacOS({
6565 required bool verboseLogging,
6666 bool configOnly = false ,
6767 SizeAnalyzer ? sizeAnalyzer,
68+ bool usingCISystem = false ,
6869}) async {
6970 final Directory ? xcodeWorkspace = flutterProject.macos.xcodeWorkspace;
7071 if (xcodeWorkspace == null ) {
@@ -153,6 +154,19 @@ Future<void> buildMacOS({
153154 'Building macOS application...' ,
154155 );
155156 int result;
157+
158+ File ? disabledSandboxEntitlementFile;
159+ if (usingCISystem) {
160+ disabledSandboxEntitlementFile = _createDisabledSandboxEntitlementFile (
161+ flutterProject.macos,
162+ configuration,
163+ );
164+ if (disabledSandboxEntitlementFile != null ) {
165+ globals.logger.printStatus (
166+ 'Detected macOS app running in CI, turning off sandboxing.' );
167+ }
168+ }
169+
156170 try {
157171 result = await globals.processUtils.stream (< String > [
158172 '/usr/bin/env' ,
@@ -170,6 +184,8 @@ Future<void> buildMacOS({
170184 else
171185 '-quiet' ,
172186 'COMPILER_INDEX_STORE_ENABLE=NO' ,
187+ if (disabledSandboxEntitlementFile != null )
188+ 'CODE_SIGN_ENTITLEMENTS=${disabledSandboxEntitlementFile .path }' ,
173189 ...environmentVariablesAsXcodeBuildSettings (globals.platform),
174190 ],
175191 trace: true ,
@@ -271,3 +287,52 @@ Future<void> _writeCodeSizeAnalysis(BuildInfo buildInfo, SizeAnalyzer? sizeAnaly
271287 'dart devtools --appSizeBase=$relativeAppSizePath '
272288 );
273289}
290+
291+ /// Finds and copies macOS entitlements file. In the copy, disables sandboxing.
292+ /// If entitlements file is not found, returns null.
293+ ///
294+ /// As of macOS 14, running a macOS sandbox app may prompt the user to grant
295+ /// access to the app. To workaround this in CI, we create and use a entitlements
296+ /// file with sandboxing disabled. See
297+ /// https://developer.apple.com/documentation/security/app_sandbox/accessing_files_from_the_macos_app_sandbox.
298+ File ? _createDisabledSandboxEntitlementFile (
299+ MacOSProject macos,
300+ String configuration,
301+ ) {
302+ String entitlementDefaultFileName;
303+ if (configuration == 'Release' ) {
304+ entitlementDefaultFileName = 'Release' ;
305+ } else {
306+ entitlementDefaultFileName = 'DebugProfile' ;
307+ }
308+
309+ // TODO(vashworth): Once https://github.com/flutter/flutter/issues/146204 is
310+ // fixed, it would be better to get the path to the entitlement file from the
311+ // project's build settings (CODE_SIGN_ENTITLEMENTS).
312+ final File entitlementFile = macos.hostAppRoot
313+ .childDirectory ('Runner' )
314+ .childFile ('$entitlementDefaultFileName .entitlements' );
315+
316+ if (! entitlementFile.existsSync ()) {
317+ globals.logger.printTrace (
318+ 'Unable to find entitlements file at ${entitlementFile .path }' );
319+ return null ;
320+ }
321+
322+ final String entitlementFileContents = entitlementFile.readAsStringSync ();
323+ final File disabledSandboxEntitlementFile = globals.fs.systemTempDirectory
324+ .createTempSync ('flutter_disable_sandbox_entitlement.' )
325+ .childFile (
326+ '${entitlementDefaultFileName }WithDisabledSandboxing.entitlements' ,
327+ );
328+ disabledSandboxEntitlementFile.createSync (recursive: true );
329+ disabledSandboxEntitlementFile.writeAsStringSync (
330+ entitlementFileContents.replaceAll (
331+ RegExp (r'<key>com\.apple\.security\.app-sandbox<\/key>[\S\s]*?<true\/>' ),
332+ '''
333+ <key>com.apple.security.app-sandbox</key>
334+ <false/>''' ,
335+ ),
336+ );
337+ return disabledSandboxEntitlementFile;
338+ }
0 commit comments