add test to verify binaries are signed on release branches (#50074)

diff --git a/.cirrus.yml b/.cirrus.yml
index 5436a5d..6f444cf 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -652,6 +652,15 @@
         - 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/dev/bots/codesign.dart b/dev/bots/codesign.dart
new file mode 100644
index 0000000..129d5ab
--- /dev/null
+++ b/dev/bots/codesign.dart
@@ -0,0 +1,67 @@
+// 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 cacheDirectory {
+  final String flutterRepoRoot = path.normalize(path.join(path.dirname(Platform.script.path), '..', '..'));
+  return path.normalize(path.join(flutterRepoRoot, 'bin', 'cache'));
+}
+
+bool isBinary(String filePath) {
+  final ProcessResult result = Process.runSync(
+    'file',
+    <String>[
+      '--mime-type',
+      '-b', // is binary
+      filePath,
+    ],
+  );
+  return (result.stdout as String).contains('application/x-mach-binary');
+}
+
+List<String> findBinaryPaths() {
+  final ProcessResult result = Process.runSync(
+    'find',
+    <String>[
+      cacheDirectory,
+      '-type',
+      'f',
+      '-perm',
+      '+111', // is executable
+    ],
+  );
+  final List<String> allFiles = (result.stdout as String).split('\n').where((String s) => s.isNotEmpty).toList();
+  return allFiles.where(isBinary).toList();
+}
+
+void main() {
+  final List<String> failures = <String>[];
+
+  for (final String binaryPath in findBinaryPaths()) {
+    print('Verifying the code signature of $binaryPath');
+    final ProcessResult result = Process.runSync(
+      'codesign',
+      <String>[
+        '-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.');
+}