[codesign + recipe] refactor gsutil and zip utilities (#2289)
* refactor gsutil
* revert pubspec lock
* christopher's feedbacks
* try finally
* try block scope
* simplify passwords
* simplify cleanup
diff --git a/codesign/bin/codesign.dart b/codesign/bin/codesign.dart
index 937ac45..7197fde 100644
--- a/codesign/bin/codesign.dart
+++ b/codesign/bin/codesign.dart
@@ -15,8 +15,8 @@
const String kHelpFlag = 'help';
const String kDryrunFlag = 'dryrun';
const String kCodesignCertNameOption = 'codesign-cert-name';
-const String kGcsDownloadPathOption = 'gcs-download-path';
-const String kGcsUploadPathOption = 'gcs-upload-path';
+const String kInputZipPathOption = 'input-zip-file-path';
+const String kOutputZipPathOption = 'output-zip-file-path';
const String kAppSpecificPasswordOption = 'app-specific-password-file-path';
const String kCodesignAppstoreIDOption = 'codesign-appstore-id-file-path';
const String kCodesignTeamIDOption = 'codesign-team-id-file-path';
@@ -24,20 +24,20 @@
/// Perform Mac code signing based on file paths.
///
/// By default, if a user does not specify a dryrun flag, or selects dryrun
-/// mode by providing the `--dryrun` flag, then [kDryrunFlag] is set to true.
-/// In this case, code signed artifacts are not uploaded back to google cloud storage.
+/// mode by providing the `--dryrun` flag, then [kDryrunFlag] is set to true,
+/// a quick sanity check is performed and the notarization process is skipped.
/// On the other hand, if a user provides the flag `--no-dryrun`, [kDryrunFlag]
-/// will be set to false, and code signed artifacts will be uploaded back to
-/// google cloud storage.
+/// will be set to false, and code signed artifacts will go through the notarization
+/// process.
///
-/// For [kGcsDownloadPathOption] and [kGcsUploadPathOption], they are required parameter to specify the google cloud bucket paths.
-/// [kGcsDownloadPathOption] is the google cloud bucket prefix to download the remote artifacts,
-/// [kGcsUploadPathOption] is the cloud bucket prefix to upload codesigned artifact to.
+/// For [kInputZipPathOption] and [kOutputZipPathOption], they are required parameter to specify the
+/// input and output locations.
+/// The codesign app will take the zip file located at the input location [kInputZipPathOption], and
+/// put codesigned zip at [kOutputZipPathOption]. The work of downloading and uploading the zip
+/// artifacts is delegated to recipe.
/// For example, supply
-/// '--gcs-download-path=gs://flutter_infra_release/ios-usb-dependencies/unsigned/libimobiledevice/<commit>/libimobiledevice.zip',
-/// and code sign app will download the artifact at
-/// 'flutter_infra_release/ios-usb-dependencies/unsigned/libimobiledevice/<commit>/libimobiledevice.zip'
-/// on google cloud storage
+/// '--input-zip-file-path=/tmp/input.zip',
+/// and code sign app will code sign the artifacts located at /tmp/input.zip.
///
/// For [kAppSpecificPasswordOption], [kCodesignAppstoreIDOption] and [kCodesignTeamIDOption],
/// they are file paths of the password files in the file system.
@@ -59,8 +59,8 @@
/// --codesign-team-id-file-path=/a/b/c.txt
/// --codesign-appstore-id-file-path=/a/b/b.txt
/// --app-specific-password-file-path=/a/b/a.txt
-/// --gcs-download-path=gs://flutter_infra_release/flutter/<commit>/android-arm-profile/artifacts.zip
-/// --gcs-upload-path=gs://flutter_infra_release/flutter/<commit>/android-arm-profile/artifacts.zip
+/// --input-zip-file-path=/a/input.zip
+/// --output-zip-file-path=/b/output.zip
/// ```
Future<void> main(List<String> args) async {
final ArgParser parser = ArgParser();
@@ -81,36 +81,32 @@
'the name of the certificate for flutter, for example, is: FLUTTER.IO LLC',
)
..addOption(
- kGcsDownloadPathOption,
- help: 'The google cloud bucket path to download the artifact from\n'
- 'e.g. supply `--gcs-download-path=gs://flutter_infra_release/ios-usb-dependencies/unsigned/ios-deploy/<commit>/ios-deploy.zip`'
- ' if you would like to codesign ios-deploy.zip, which has a google cloud bucket path of flutter_infra_release/ios-usb-dependencies/unsigned/ios-deploy/<commit>/ios-deploy.zip to be downloaded from \n',
+ kInputZipPathOption,
+ help: 'File path to the unsigned artifact zip file.',
)
..addOption(
- kGcsUploadPathOption,
- help: 'The google cloud bucket path to upload the artifact to. \n'
- 'e.g. supply `--gcs-upload-path=gs://flutter_infra_release/ios-usb-dependencies/ios-deploy/<commit>/ios-deploy.zip`'
- ' if you would like to codesign ios-deploy.zip, which has a google cloud bucket path of flutter_infra_release/ios-usb-dependencies/ios-deploy/<commit>/ios-deploy.zip to be uploaded to',
+ kOutputZipPathOption,
+ help: 'File path to codesigned artifact zip file for output.',
)
..addOption(
kAppSpecificPasswordOption,
help:
- 'The file path of a password file in file system. The password file stores the sensitive password <APP-SPECIFIC-PASSWORD> \n',
+ 'The file path of a password file in file system. The password file stores the sensitive password <APP-SPECIFIC-PASSWORD>.',
)
..addOption(
kCodesignAppstoreIDOption,
help:
- 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_APPSTORE_ID> \n',
+ 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_APPSTORE_ID>.',
)
..addOption(
kCodesignTeamIDOption,
help:
- 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_TEAM_ID> \n',
+ 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_TEAM_ID>.',
)
..addFlag(
kDryrunFlag,
defaultsTo: true,
- help: 'whether we are going to upload the artifacts back to GCS for dryrun',
+ help: 'whether we are going to skip the notarization process.',
);
final ArgResults argResults = parser.parse(args);
@@ -118,8 +114,8 @@
const Platform platform = LocalPlatform();
final String codesignCertName = getValueFromArgs(kCodesignCertNameOption, argResults)!;
- final String gCloudDownloadPath = getValueFromArgs(kGcsDownloadPathOption, argResults)!;
- final String gCloudUploadPath = getValueFromArgs(kGcsUploadPathOption, argResults)!;
+ final String inputZipPath = getValueFromArgs(kInputZipPathOption, argResults)!;
+ final String outputZipPath = getValueFromArgs(kOutputZipPathOption, argResults)!;
final String appSpecificPasswordFilePath = getValueFromArgs(kAppSpecificPasswordOption, argResults)!;
final String codesignAppstoreIDFilePath = getValueFromArgs(kCodesignAppstoreIDOption, argResults)!;
final String codesignTeamIDFilePath = getValueFromArgs(kCodesignTeamIDOption, argResults)!;
@@ -136,10 +132,6 @@
const FileSystem fileSystem = LocalFileSystem();
final Directory rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
const ProcessManager processManager = LocalProcessManager();
- final GoogleCloudStorage googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
return FileCodesignVisitor(
codesignCertName: codesignCertName,
@@ -150,8 +142,7 @@
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
dryrun: dryrun,
- gcsDownloadPath: gCloudDownloadPath,
- gcsUploadPath: gCloudUploadPath,
- googleCloudStorage: googleCloudStorage,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
).validateAll();
}
diff --git a/codesign/lib/codesign.dart b/codesign/lib/codesign.dart
index b814211..54fe0c8 100644
--- a/codesign/lib/codesign.dart
+++ b/codesign/lib/codesign.dart
@@ -3,5 +3,4 @@
// found in the LICENSE file.
export 'src/file_codesign_visitor.dart';
-export 'src/google_cloud_storage.dart';
export 'src/utils.dart';
diff --git a/codesign/lib/src/file_codesign_visitor.dart b/codesign/lib/src/file_codesign_visitor.dart
index cacaea8..124f639 100644
--- a/codesign/lib/src/file_codesign_visitor.dart
+++ b/codesign/lib/src/file_codesign_visitor.dart
@@ -8,7 +8,6 @@
import 'package:file/file.dart';
import 'package:process/process.dart';
-import 'google_cloud_storage.dart';
import 'log.dart';
import 'utils.dart';
@@ -29,9 +28,8 @@
required this.fileSystem,
required this.rootDirectory,
required this.processManager,
- required this.gcsDownloadPath,
- required this.gcsUploadPath,
- required this.googleCloudStorage,
+ required this.inputZipPath,
+ required this.outputZipPath,
required this.appSpecificPasswordFilePath,
required this.codesignAppstoreIDFilePath,
required this.codesignTeamIDFilePath,
@@ -39,8 +37,6 @@
this.notarizationTimerDuration = const Duration(seconds: 5),
}) {
entitlementsFile = rootDirectory.childFile('Entitlements.plist')..writeAsStringSync(_entitlementsFileContents);
- remoteDownloadsDir = rootDirectory.childDirectory('downloads')..createSync();
- codesignedZipsDir = rootDirectory.childDirectory('codesigned_zips')..createSync();
}
/// Temp [Directory] to download/extract files to.
@@ -49,11 +45,10 @@
final Directory rootDirectory;
final FileSystem fileSystem;
final ProcessManager processManager;
- final GoogleCloudStorage googleCloudStorage;
final String codesignCertName;
- final String gcsDownloadPath;
- final String gcsUploadPath;
+ final String inputZipPath;
+ final String outputZipPath;
final String appSpecificPasswordFilePath;
final String codesignAppstoreIDFilePath;
final String codesignTeamIDFilePath;
@@ -78,8 +73,6 @@
};
late final File entitlementsFile;
- late final Directory remoteDownloadsDir;
- late final Directory codesignedZipsDir;
int _remoteDownloadIndex = 0;
int get remoteDownloadIndex => _remoteDownloadIndex++;
@@ -130,75 +123,40 @@
''';
/// Read a single line of password stored at [passwordFilePath].
- ///
- /// The password file should provide the password name and value, deliminated by a single colon.
- /// The content of a password file would look similar to:
- /// CODESIGN_APPSTORE_ID:123
- Future<void> readPassword(String passwordFilePath) async {
+ Future<String> readPassword(String passwordFilePath) async {
if (!(await fileSystem.file(passwordFilePath).exists())) {
throw CodesignException('$passwordFilePath not found \n'
'make sure you have provided codesign credentials in a file \n');
}
-
- final String passwordLine = await fileSystem.file(passwordFilePath).readAsString();
- final List<String> parsedPasswordLine = passwordLine.split(":");
- if (parsedPasswordLine.length != 2) {
- throw CodesignException('$passwordFilePath is not correctly formatted. \n'
- 'please double check formatting \n');
- }
- final String passwordName = parsedPasswordLine[0];
- final String passwordValue = parsedPasswordLine[1];
- if (!availablePasswords.containsKey(passwordName)) {
- throw CodesignException('$passwordName is not a password we can process. \n'
- 'please double check passwords.txt \n');
- }
- availablePasswords[passwordName] = passwordValue;
- return;
+ return (await fileSystem.file(passwordFilePath).readAsString());
}
/// The entrance point of examining and code signing an engine artifact.
Future<void> validateAll() async {
- for (String passwordFilePath in [
- codesignAppstoreIDFilePath,
- codesignTeamIDFilePath,
- appSpecificPasswordFilePath,
- ]) {
- await readPassword(passwordFilePath);
- }
- if (availablePasswords.containsValue('')) {
- throw CodesignException('certian passwords are missing. \n'
- 'make sure you have provided <CODESIGN_APPSTORE_ID>, <CODESIGN_TEAM_ID>, and <APP_SPECIFIC_PASSWORD>');
- }
- codesignAppstoreId = availablePasswords['CODESIGN_APPSTORE_ID']!;
- codesignTeamId = availablePasswords['CODESIGN_TEAM_ID']!;
- appSpecificPassword = availablePasswords['APP_SPECIFIC_PASSWORD']!;
+ codesignAppstoreId = await readPassword(codesignAppstoreIDFilePath);
+ codesignTeamId = await readPassword(codesignTeamIDFilePath);
+ appSpecificPassword = await readPassword(appSpecificPasswordFilePath);
await processRemoteZip();
+ log.info('Codesign completed. Codesigned zip is located at $outputZipPath.'
+ 'If you have uploaded the artifacts back to google cloud storage, please delete'
+ ' the folder $outputZipPath and $inputZipPath.');
if (dryrun) {
- log.info('code signing dry run has completed, If you intend to upload the artifacts back to'
- ' google cloud storage, please use the --dryrun=false flag to run code signing script.');
+ log.info('code signing dry run has completed, this is a quick sanity check without'
+ 'going through the notary service. To run the full codesign process, use --no-dryrun flag.');
}
- log.info('Codesigned all binaries in ${rootDirectory.path}');
-
- await rootDirectory.delete(recursive: true);
}
- /// Retrieve engine artifact from google cloud storage and kick start a
+ /// Process engine artifacts from [inputZipPath] and kick start a
/// recursive visit of its contents.
///
/// Invokes [visitDirectory] to recursively visit the contents of the remote
- /// zip. Also downloads, notarizes and uploads the engine artifact.
- Future<void> processRemoteZip() async {
- // Name of the downloaded artifact.
- // There won't be collisions since we are only signing one artifact at a time now
- const String localFilePath = 'remote_artifact.zip';
-
+ /// zip. Notarizes the engine artifact if [dryrun] is false.
+ /// Returns null as result if [dryrun] is true.
+ Future<String?> processRemoteZip() async {
// download the zip file
- final File originalFile = await googleCloudStorage.downloadEngineArtifact(
- from: gcsDownloadPath,
- destination: remoteDownloadsDir.childFile(localFilePath).path,
- );
+ final File originalFile = rootDirectory.fileSystem.file(inputZipPath);
// This is the starting directory of the unzipped artifact.
final Directory parentDirectory = rootDirectory.childDirectory('single_artifact');
@@ -218,24 +176,21 @@
// recursively visit extracted files
await visitDirectory(directory: parentDirectory, parentVirtualPath: "");
- final File codesignedFile = codesignedZipsDir.childFile(localFilePath);
-
await zip(
inputDir: parentDirectory,
- outputZip: codesignedFile,
+ outputZipPath: outputZipPath,
processManager: processManager,
);
- // `dryrun` flag defaults to true to prevent uploading artifacts back to google cloud.
- // This would help prevent https://github.com/flutter/flutter/issues/104387
- if (!dryrun) {
- await notarize(codesignedFile);
+ await parentDirectory.delete(recursive: true);
- await googleCloudStorage.uploadEngineArtifact(
- from: codesignedFile.path,
- destination: gcsUploadPath,
- );
+ // `dryrun` flag defaults to true to save time for a faster sanity check
+ if (!dryrun) {
+ await notarize(fileSystem.file(outputZipPath));
+
+ return outputZipPath;
}
+ return null;
}
/// Visit a [Directory] type while examining the file system extracted from an artifact.
@@ -301,9 +256,10 @@
await zipEntity.delete();
await zip(
inputDir: newDir,
- outputZip: zipEntity,
+ outputZipPath: zipEntity.absolute.path,
processManager: processManager,
);
+ await newDir.delete(recursive: true);
}
/// Visit and codesign a binary with / without entitlement.
diff --git a/codesign/lib/src/google_cloud_storage.dart b/codesign/lib/src/google_cloud_storage.dart
deleted file mode 100644
index fd0fe58..0000000
--- a/codesign/lib/src/google_cloud_storage.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 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:file/file.dart';
-import 'package:process/process.dart';
-
-import './utils.dart';
-
-/// A service to interact with google cloud storage through gsutil.
-class GoogleCloudStorage {
- GoogleCloudStorage({
- required this.processManager,
- required this.rootDirectory,
- });
-
- final ProcessManager processManager;
- final Directory rootDirectory;
-
- /// Method to upload code signed flutter engine artifact to google cloud bucket.
- Future<void> uploadEngineArtifact({
- required String from,
- required String destination,
- }) async {
- final ProcessResult result = await processManager.run(
- <String>['gsutil', 'cp', from, destination],
- );
-
- if (result.exitCode != 0) {
- throw CodesignException('Failed to upload $from to $destination');
- }
- }
-
- /// Method to download flutter engine artifact from google cloud bucket.
- Future<File> downloadEngineArtifact({
- required String from,
- required String destination,
- }) async {
- final ProcessResult result = await processManager.run(
- <String>['gsutil', 'cp', from, destination],
- );
- if (result.exitCode != 0) {
- throw CodesignException('Failed to download from $from');
- }
- return rootDirectory.fileSystem.file(destination);
- }
-}
diff --git a/codesign/lib/src/utils.dart b/codesign/lib/src/utils.dart
index 400ebce..ad56517 100644
--- a/codesign/lib/src/utils.dart
+++ b/codesign/lib/src/utils.dart
@@ -48,7 +48,7 @@
Future<void> zip({
required Directory inputDir,
- required FileSystemEntity outputZip,
+ required String outputZipPath,
required ProcessManager processManager,
}) async {
await processManager.run(
@@ -56,7 +56,7 @@
'zip',
'--symlinks',
'--recurse-paths',
- outputZip.absolute.path,
+ outputZipPath,
// use '.' so that the full absolute path is not encoded into the zip file
'.',
'--include',
diff --git a/codesign/test/file_codesign_visitor_test.dart b/codesign/test/file_codesign_visitor_test.dart
index b717b33..4a88bef 100644
--- a/codesign/test/file_codesign_visitor_test.dart
+++ b/codesign/test/file_codesign_visitor_test.dart
@@ -5,7 +5,6 @@
import 'dart:convert';
import 'package:codesign/codesign.dart' as cs;
-import 'package:codesign/src/google_cloud_storage.dart';
import 'package:codesign/src/log.dart';
import 'package:codesign/src/utils.dart';
import 'package:file/file.dart';
@@ -20,11 +19,12 @@
const String appSpecificPasswordFilePath = '/tmp/passwords.txt';
const String codesignAppstoreIDFilePath = '/tmp/appID.txt';
const String codesignTeamIDFilePath = '/tmp/teamID.txt';
+ const String inputZipPath = '/tmp/input.zip';
+ const String outputZipPath = '/tmp/output.zip';
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final List<LogRecord> records = <LogRecord>[];
late FakeProcessManager processManager;
- late GoogleCloudStorage googleCloudStorage;
late cs.FileCodesignVisitor codesignVisitor;
Directory rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
@@ -32,21 +32,16 @@
group('test reading in passwords: ', () {
setUp(() {
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- googleCloudStorage: googleCloudStorage,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
rootDirectory: rootDirectory,
- gcsDownloadPath: 'gs://flutter/$randomString/$randomString',
- gcsUploadPath: 'gs://flutter/$randomString/$randomString',
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
notarizationTimerDuration: const Duration(seconds: 0),
dryrun: false,
);
@@ -55,64 +50,10 @@
log.onRecord.listen((LogRecord record) => records.add(record));
});
- test('incorrectly formatted password file throws exception', () async {
- fileSystem.file(appSpecificPasswordFilePath)
- ..createSync(recursive: true)
- ..writeAsStringSync(
- 'file_a',
- mode: FileMode.write,
- encoding: utf8,
- );
-
+ test('lacking password file throws exception', () async {
expect(
() async {
await codesignVisitor.readPassword(appSpecificPasswordFilePath);
- fileSystem.file(appSpecificPasswordFilePath).deleteSync();
- },
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('unknown password name throws an exception', () async {
- fileSystem.file(codesignTeamIDFilePath)
- ..createSync(recursive: true, exclusive: true)
- ..writeAsStringSync(
- 'dart:dart',
- mode: FileMode.write,
- encoding: utf8,
- );
-
- expect(
- () async {
- await codesignVisitor.readPassword(codesignTeamIDFilePath);
- await fileSystem.file(codesignTeamIDFilePath).delete();
- },
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('lacking required passwords throws exception', () async {
- codesignVisitor.availablePasswords = {
- 'CODESIGN_APPSTORE_ID': '',
- 'CODESIGN_TEAM_ID': '',
- 'APP-SPECIFIC-PASSWORD': ''
- };
- fileSystem.file(codesignAppstoreIDFilePath)
- ..createSync(recursive: true)
- ..writeAsStringSync(
- 'CODESIGN_APPSTORE_ID:123',
- mode: FileMode.write,
- encoding: utf8,
- );
-
- expect(
- () async {
- await codesignVisitor.validateAll();
- await fileSystem.file(codesignAppstoreIDFilePath).delete();
},
throwsA(
isA<CodesignException>(),
@@ -124,7 +65,7 @@
fileSystem.file(appSpecificPasswordFilePath)
..createSync(recursive: true, exclusive: true)
..writeAsStringSync(
- 'APP_SPECIFIC_PASSWORD:123',
+ '123',
mode: FileMode.write,
encoding: utf8,
);
@@ -152,23 +93,18 @@
group('test google cloud storage and processRemoteZip workflow', () {
setUp(() {
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- googleCloudStorage: googleCloudStorage,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
rootDirectory: rootDirectory,
- gcsDownloadPath: 'gs://flutter/$randomString/$randomString',
- gcsUploadPath: 'gs://flutter/$randomString/$randomString',
notarizationTimerDuration: const Duration(seconds: 0),
dryrun: false,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
);
codesignVisitor.appSpecificPassword = randomString;
codesignVisitor.codesignAppstoreId = randomString;
@@ -178,143 +114,14 @@
log.onRecord.listen((LogRecord record) => records.add(record));
});
- test('download fails and upload succeeds throws exception', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- randomString,
- 'gs://flutter/$randomString/$randomString',
- ],
- exitCode: 0,
- ),
- ]);
- expect(
- () => googleCloudStorage.uploadEngineArtifact(
- from: randomString,
- destination: codesignVisitor.gcsUploadPath,
- ),
- returnsNormally,
- );
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- 'gs://flutter/$randomString/$randomString',
- randomString,
- ],
- exitCode: -1,
- ),
- ]);
- expect(
- () => googleCloudStorage.downloadEngineArtifact(
- destination: randomString,
- from: codesignVisitor.gcsDownloadPath,
- ),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('download succeeds and upload fails throws exception', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- 'gs://flutter/$randomString/$randomString',
- randomString,
- ],
- exitCode: 0,
- ),
- ]);
- expect(
- () => googleCloudStorage.downloadEngineArtifact(
- destination: randomString,
- from: codesignVisitor.gcsDownloadPath,
- ),
- returnsNormally,
- );
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- randomString,
- 'gs://flutter/$randomString/$randomString',
- ],
- exitCode: -1,
- ),
- ]);
- expect(
- () => googleCloudStorage.uploadEngineArtifact(
- from: randomString,
- destination: codesignVisitor.gcsUploadPath,
- ),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('download succeeds and upload succeeds returns normally', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- 'gs://flutter/$randomString/$randomString',
- randomString,
- ],
- exitCode: 0,
- ),
- ]);
- expect(
- () => googleCloudStorage.downloadEngineArtifact(
- destination: randomString,
- from: codesignVisitor.gcsDownloadPath,
- ),
- returnsNormally,
- );
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- randomString,
- 'gs://flutter/$randomString/$randomString',
- ],
- exitCode: 0,
- ),
- ]);
- expect(
- () => googleCloudStorage.uploadEngineArtifact(
- from: randomString,
- destination: codesignVisitor.gcsUploadPath,
- ),
- returnsNormally,
- );
- });
-
test('procesRemotezip triggers correct workflow', () async {
final String zipFileName = '${rootDirectory.path}/remote_zip_4/folder_1/zip_1';
fileSystem.file(zipFileName).createSync(recursive: true);
processManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
- 'gsutil',
- 'cp',
- 'gs://flutter/$randomString/$randomString',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
- ],
- ),
- FakeCommand(
- command: <String>[
'unzip',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
+ codesignVisitor.inputZipPath,
'-d',
'${rootDirectory.absolute.path}/single_artifact',
],
@@ -327,7 +134,7 @@
'zip',
'--symlinks',
'--recurse-paths',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
+ codesignVisitor.outputZipPath,
'.',
'--include',
'*',
@@ -338,7 +145,7 @@
'xcrun',
'notarytool',
'submit',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
+ codesignVisitor.outputZipPath,
'--apple-id',
randomString,
'--password',
@@ -363,14 +170,6 @@
],
stdout: 'status: Accepted',
),
- FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
- 'gs://flutter/$randomString/$randomString',
- ],
- ),
]);
await codesignVisitor.processRemoteZip();
@@ -381,7 +180,7 @@
expect(
messages,
contains(
- 'The downloaded file is unzipped from ${rootDirectory.absolute.path}/downloads/remote_artifact.zip to ${rootDirectory.path}/single_artifact',
+ 'The downloaded file is unzipped from ${codesignVisitor.inputZipPath} to ${rootDirectory.path}/single_artifact',
),
);
expect(
@@ -399,14 +198,12 @@
expect(
messages,
contains(
- 'uploading xcrun notarytool submit ${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip --apple-id $randomString --password $randomString --team-id $randomString',
+ 'uploading xcrun notarytool submit ${codesignVisitor.outputZipPath} --apple-id $randomString --password $randomString --team-id $randomString',
),
);
expect(
messages,
- contains(
- 'RequestUUID for ${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip is: $randomString',
- ),
+ contains('RequestUUID for ${codesignVisitor.outputZipPath} is: $randomString'),
);
expect(
messages,
@@ -416,7 +213,7 @@
);
expect(
messages,
- contains('successfully notarized ${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip'),
+ contains('successfully notarized ${codesignVisitor.outputZipPath}'),
);
});
});
@@ -424,21 +221,16 @@
group('visit directory/zip api calls: ', () {
setUp(() {
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- googleCloudStorage: googleCloudStorage,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
rootDirectory: rootDirectory,
- gcsDownloadPath: 'gs://flutter/$randomString/FILEPATH',
- gcsUploadPath: 'gs://flutter/$randomString/FILEPATH',
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
notarizationTimerDuration: Duration.zero,
);
codesignVisitor.appSpecificPassword = randomString;
@@ -714,15 +506,14 @@
test('visitBinary codesigns binary with / without entitlement', () async {
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- googleCloudStorage: googleCloudStorage,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
rootDirectory: rootDirectory,
- gcsDownloadPath: 'flutter/$randomString/FILEPATH',
- gcsUploadPath: 'flutter/$randomString/FILEPATH',
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
dryrun: false,
notarizationTimerDuration: const Duration(seconds: 0),
);
@@ -800,15 +591,10 @@
group('parse entitlement configs: ', () {
setUp(() {
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- gcsDownloadPath: 'flutter/$randomString/FILEPATH',
- gcsUploadPath: 'flutter/$randomString/FILEPATH',
- googleCloudStorage: googleCloudStorage,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
@@ -909,15 +695,10 @@
group('notarization tests: ', () {
setUp(() {
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- gcsDownloadPath: 'flutter/$randomString/FILEPATH',
- gcsUploadPath: 'flutter/$randomString/FILEPATH',
- googleCloudStorage: googleCloudStorage,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
@@ -1231,15 +1012,10 @@
setUp(() {
rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
processManager = FakeProcessManager.list(<FakeCommand>[]);
- googleCloudStorage = GoogleCloudStorage(
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- gcsDownloadPath: 'gs://ios-usb-dependencies/unsigned/libimobiledevice/$randomString/libimobiledevice.zip',
- gcsUploadPath: 'gs://ios-usb-dependencies/libimobiledevice/$randomString/libimobiledevice.zip',
- googleCloudStorage: googleCloudStorage,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
@@ -1256,29 +1032,21 @@
log.onRecord.listen((LogRecord record) => records.add(record));
fileSystem.file(codesignAppstoreIDFilePath)
..createSync(recursive: true)
- ..writeAsStringSync('CODESIGN_APPSTORE_ID:$randomString');
+ ..writeAsStringSync(randomString);
fileSystem.file(codesignTeamIDFilePath)
..createSync(recursive: true)
- ..writeAsStringSync('CODESIGN_TEAM_ID:$randomString');
+ ..writeAsStringSync(randomString);
fileSystem.file(appSpecificPasswordFilePath)
..createSync(recursive: true)
- ..writeAsStringSync('APP_SPECIFIC_PASSWORD:$randomString');
+ ..writeAsStringSync(randomString);
});
- test('codesign optional switches artifacts when dryrun is false', () async {
+ test('codesign optional switches artifacts when dryrun is true', () async {
processManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
- 'gsutil',
- 'cp',
- 'gs://ios-usb-dependencies/unsigned/libimobiledevice/abcd1234/libimobiledevice.zip',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
- ],
- ),
- FakeCommand(
- command: <String>[
'unzip',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
+ codesignVisitor.inputZipPath,
'-d',
'${rootDirectory.absolute.path}/single_artifact'
],
@@ -1291,7 +1059,7 @@
'zip',
'--symlinks',
'--recurse-paths',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
+ codesignVisitor.outputZipPath,
'.',
'--include',
'*'
@@ -1302,7 +1070,7 @@
'xcrun',
'notarytool',
'submit',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_zip',
+ codesignVisitor.outputZipPath,
'--apple-id',
randomString,
'--password',
@@ -1335,26 +1103,18 @@
.toList();
expect(
messages,
- contains('code signing dry run has completed, If you intend to upload the artifacts back to'
- ' google cloud storage, please use the --dryrun=false flag to run code signing script.'),
+ contains('code signing dry run has completed, this is a quick sanity check without'
+ 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'),
);
rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
});
- test('upload optional switch artifacts when dryrun is true', () async {
+ test('upload optional switch artifacts when dryrun is false', () async {
processManager.addCommands(<FakeCommand>[
FakeCommand(
command: <String>[
- 'gsutil',
- 'cp',
- 'gs://ios-usb-dependencies/unsigned/libimobiledevice/abcd1234/libimobiledevice.zip',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
- ],
- ),
- FakeCommand(
- command: <String>[
'unzip',
- '${rootDirectory.absolute.path}/downloads/remote_artifact.zip',
+ codesignVisitor.inputZipPath,
'-d',
'${rootDirectory.absolute.path}/single_artifact'
],
@@ -1367,7 +1127,7 @@
'zip',
'--symlinks',
'--recurse-paths',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
+ codesignVisitor.outputZipPath,
'.',
'--include',
'*'
@@ -1378,7 +1138,7 @@
'xcrun',
'notarytool',
'submit',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
+ codesignVisitor.outputZipPath,
'--apple-id',
randomString,
'--password',
@@ -1403,20 +1163,11 @@
],
stdout: 'status: Accepted',
),
- FakeCommand(
- command: <String>[
- 'gsutil',
- 'cp',
- '${rootDirectory.absolute.path}/codesigned_zips/remote_artifact.zip',
- 'gs://ios-usb-dependencies/libimobiledevice/$randomString/libimobiledevice.zip',
- ],
- ),
]);
codesignVisitor = cs.FileCodesignVisitor(
codesignCertName: randomString,
- gcsDownloadPath: 'gs://ios-usb-dependencies/unsigned/libimobiledevice/$randomString/libimobiledevice.zip',
- gcsUploadPath: 'gs://ios-usb-dependencies/libimobiledevice/$randomString/libimobiledevice.zip',
- googleCloudStorage: googleCloudStorage,
+ inputZipPath: inputZipPath,
+ outputZipPath: outputZipPath,
fileSystem: fileSystem,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
@@ -1437,14 +1188,16 @@
.toSet();
expect(
messages,
- isNot(
- contains('code signing dry run has completed, If you intend to upload the artifacts back to'
- ' google cloud storage, please use the --dryrun=false flag to run code signing script.'),
- ),
+ contains('Codesign completed. Codesigned zip is located at ${codesignVisitor.outputZipPath}.'
+ 'If you have uploaded the artifacts back to google cloud storage, please delete'
+ ' the folder ${codesignVisitor.outputZipPath} and ${codesignVisitor.inputZipPath}.'),
);
expect(
messages,
- contains('Codesigned all binaries in ${rootDirectory.path}'),
+ isNot(
+ contains('code signing dry run has completed, this is a quick sanity check without'
+ 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'),
+ ),
);
rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
});