diff --git a/.cirrus.yml b/.cirrus.yml index 415a388ad0d..67b1242f9f4 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -19,6 +19,7 @@ environment: # TODO(amirha): remove once we've migrated to newer Gradle CIRRUS_CHANGE_MESSAGE: "" CIRRUS_COMMIT_MESSAGE: "" + FLUTTER_TESTS_PIN_HASH: 'f73542b1dcf6866948ca13d4f61c3557c7c3d4ad' # LINUX SHARDS task: @@ -283,6 +284,8 @@ task: script: - rm -rf bin/cache/pkg/tests - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests + # Hotfix only, this pins the flutter/tests repo + - (cd bin/cache/pkg/tests ; git checkout "$FLUTTER_TESTS_PIN_HASH") - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test - name: firebase_test_lab_tests-linux # linux-only @@ -425,6 +428,8 @@ task: script: - CMD /S /C "IF EXIST "bin\cache\pkg\tests\" RMDIR /S /Q bin\cache\pkg\tests" - git clone https://github.com/flutter/tests.git bin\cache\pkg\tests + # Hotfix only, this pins the flutter/tests repo + - CMD /S /C "CD bin\cache\pkg\tests & git checkout "%FLUTTER_TESTS_PIN_HASH%"" - dart --enable-asserts dev\customer_testing\run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test # MACOS SHARDS @@ -548,6 +553,8 @@ task: - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - rm -rf bin/cache/pkg/tests - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests + # Hotfix only, this pins the flutter/tests repo + - (cd bin/cache/pkg/tests ; git checkout "$FLUTTER_TESTS_PIN_HASH") - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test - name: deploy_gallery-macos # linux- and macos- only @@ -577,6 +584,15 @@ task: - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 - ./dev/bots/deploy_gallery.sh + - name: verify_binaries_codesigned-macos # macos-only + # TODO(fujino): remove this `only_if` after https://github.com/flutter/flutter/issues/44372 + only_if: "$CIRRUS_BRANCH == 'dev' || $CIRRUS_BRANCH == 'beta' || $CIRRUS_BRANCH == 'stable' || $CIRRUS_BRANCH =~ '.*hotfix.*'" + depends_on: + - analyze-linux + script: + - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976 + - dart --enable-asserts ./dev/bots/codesign.dart + docker_builder: # Only build a new docker image when we tag a release (for dev, beta, or # stable). Note: tagging a commit and pushing to a release branch are diff --git a/bin/internal/engine.version b/bin/internal/engine.version index ea11d6179fb..1cc5f910143 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -a67792536ca236a971d0efbcfd7af4efb8f6c119 +e1e6ced81d029258d449bdec2ba3cddca9c2ca0c diff --git a/dev/bots/codesign.dart b/dev/bots/codesign.dart new file mode 100644 index 00000000000..0a90b627ed7 --- /dev/null +++ b/dev/bots/codesign.dart @@ -0,0 +1,105 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; +import 'package:path/path.dart' as path; + +String get repoRoot => path.normalize(path.join(path.dirname(Platform.script.toFilePath()), '..', '..')); +String get cacheDirectory => path.normalize(path.join(repoRoot, 'bin', 'cache')); + +/// Check mime-type of file at [filePath] to determine if it is binary +bool isBinary(String filePath) { + final ProcessResult result = Process.runSync( + 'file', + [ + '--mime-type', + '-b', // is binary + filePath, + ], + ); + return (result.stdout as String).contains('application/x-mach-binary'); +} + +/// Find every binary file in the given [rootDirectory] +List findBinaryPaths([String rootDirectory]) { + rootDirectory ??= cacheDirectory; + final ProcessResult result = Process.runSync( + 'find', + [ + rootDirectory, + '-type', + 'f', + '-perm', + '+111', // is executable + ], + ); + final List allFiles = (result.stdout as String).split('\n').where((String s) => s.isNotEmpty).toList(); + return allFiles.where(isBinary).toList(); +} + +/// Given the path to a stamp file, read the contents. +/// +/// Will throw if the file doesn't exist. +String readStamp(String filePath) { + final File file = File(filePath); + if (!file.existsSync()) { + throw 'Error! Stamp file $filePath does not exist!'; + } + return file.readAsStringSync().trim(); +} + +/// Return whether or not the flutter cache is up to date. +bool checkCacheIsCurrent() { + try { + final String dartSdkStamp = readStamp(path.join(cacheDirectory, 'engine-dart-sdk.stamp')); + final String engineVersion = readStamp(path.join(repoRoot, 'bin', 'internal', 'engine.version')); + return dartSdkStamp == engineVersion; + } catch (e) { + print(e); + return false; + } +} + +void main() { + final List failures = []; + + if (!Platform.isMacOS) { + print('Error! Expected operating system "macos", actual operating system ' + 'is: "${Platform.operatingSystem}"'); + exit(1); + } + + if (!checkCacheIsCurrent()) { + print( + 'Warning! Your cache is either not present or not matching your flutter\n' + 'version. Run a `flutter` command to update your cache, and re-try this\n' + 'test.'); + exit(1); + } + + for (final String binaryPath in findBinaryPaths(cacheDirectory)) { + print('Verifying the code signature of $binaryPath'); + final ProcessResult result = Process.runSync( + 'codesign', + [ + '-vvv', + binaryPath, + ], + ); + if (result.exitCode != 0) { + failures.add(binaryPath); + print('File "$binaryPath" does not appear to be codesigned.\n' + 'The `codesign` command failed with exit code ${result.exitCode}:\n' + '${result.stderr}\n'); + } + } + + if (failures.isNotEmpty) { + print('Found ${failures.length} unsigned binaries.'); + failures.forEach(print); + exit(1); + } + + print('Verified that binaries are codesigned.'); +} diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index d198087b451..0ac0916b955 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart @@ -12,8 +12,8 @@ import '../base/platform.dart'; import '../base/process.dart'; import '../ios/xcodeproj.dart'; -const int kXcodeRequiredVersionMajor = 10; -const int kXcodeRequiredVersionMinor = 2; +const int kXcodeRequiredVersionMajor = 11; +const int kXcodeRequiredVersionMinor = 0; Xcode get xcode => context.get(); diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart index e5256e544c3..12bbe1c6798 100644 --- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart @@ -79,8 +79,8 @@ void main() { testUsingOsxContext('majorVersion returns major version', () { when(mockProcessManager.runSync([xcodebuild, '-version'])) - .thenReturn(ProcessResult(1, 0, 'Xcode 10.3.3\nBuild version 8E3004b', '')); - expect(xcodeProjectInterpreter.majorVersion, 10); + .thenReturn(ProcessResult(1, 0, 'Xcode 11.4.3\nBuild version 11N111s', '')); + expect(xcodeProjectInterpreter.majorVersion, 11); }); testUsingOsxContext('majorVersion is null when version has unexpected format', () { diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart index ecbd244dca0..e379934fb9b 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart @@ -68,8 +68,9 @@ void main() { testUsingContext('xcodeVersionSatisfactory is true when version meets minimum', () { when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + expect(xcode.isVersionSatisfactory, isTrue); }, overrides: { XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, @@ -77,8 +78,9 @@ void main() { testUsingContext('xcodeVersionSatisfactory is true when major version exceeds minimum', () { when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(12); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + expect(xcode.isVersionSatisfactory, isTrue); }, overrides: { XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, @@ -86,7 +88,7 @@ void main() { testUsingContext('xcodeVersionSatisfactory is true when minor version exceeds minimum', () { when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(3); expect(xcode.isVersionSatisfactory, isTrue); }, overrides: { @@ -123,8 +125,8 @@ void main() { .thenReturn(ProcessResult(1, 127, '', 'ERROR')); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); }, overrides: { @@ -141,8 +143,9 @@ void main() { .thenReturn(ProcessResult(1, 0, xcodePath, '')); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); + expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); }, overrides: { XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, @@ -158,8 +161,9 @@ void main() { .thenReturn(ProcessResult(1, 0, xcodePath, '')); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); + when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); + expect(xcode.isInstalledAndMeetsVersionCheck, isTrue); }, overrides: { XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart index 5b3b915740b..357f5b42db0 100644 --- a/packages/flutter_tools/test/src/context.dart +++ b/packages/flutter_tools/test/src/context.dart @@ -349,13 +349,13 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter { bool get isInstalled => true; @override - String get versionText => 'Xcode 10.2'; + String get versionText => 'Xcode 11.0'; @override - int get majorVersion => 10; + int get majorVersion => 11; @override - int get minorVersion => 2; + int get minorVersion => 0; @override Future> getBuildSettings(