blob: 053282c8e3002831f566ea2ff9bcb6bfaa4ef182 [file] [log] [blame]
// 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:args/args.dart';
import 'package:codesign/codesign.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
/// Definitions of variables are included in help texts below.
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 kAppSpecificPasswordOption = 'app-specific-password-file-path';
const String kCodesignAppstoreIDOption = 'codesign-appstore-id-file-path';
const String kCodesignTeamIDOption = 'codesign-team-id-file-path';
/// 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.
/// 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.
///
/// 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 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
///
/// For [kAppSpecificPasswordOption], [kCodesignAppstoreIDOption] and [kCodesignTeamIDOption],
/// they are file paths of the password files in the file system.
/// Each of the file paths stores a single line of sensitive password.
/// sensitive passwords include <CODESIGN_APPSTORE_ID>, <CODESIGN_TEAM_ID>, and <APP_SPECIFIC_PASSWORD>.
/// For example, if a user supplies --app-specific-password-file-path=/tmp/passwords.txt,
/// then we would be expecting a password file located at /tmp/passwords.txt.
/// The password file should contain the password name APP-SPECIFIC-PASSWORD and its value, deliminated by a single colon.
/// The content of a password file would look similar to:
/// APP-SPECIFIC-PASSWORD:789
///
/// [kCodesignCertNameOption] is public information. For codesigning flutter artifacts,
/// a user can provide values for this variable as shown in the example below.
///
/// Usage:
/// ```shell
/// dart run bin/codesign.dart --[no-]dryrun
/// --codesign-cert-name="FLUTTER.IO LLC"
/// --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
/// ```
Future<void> main(List<String> args) async {
final ArgParser parser = ArgParser();
parser
..addFlag(kHelpFlag, help: 'Prints usage info.', callback: (bool value) {
if (value) {
stdout.write('${parser.usage}\n');
exit(1);
}
})
..addOption(
kCodesignCertNameOption,
help: 'The name of the codesign certificate to be used when codesigning.'
'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',
)
..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',
)
..addOption(
kAppSpecificPasswordOption,
help:
'The file path of a password file in file system. The password file stores the sensitive password <APP-SPECIFIC-PASSWORD> \n',
)
..addOption(
kCodesignAppstoreIDOption,
help:
'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_APPSTORE_ID> \n',
)
..addOption(
kCodesignTeamIDOption,
help:
'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_TEAM_ID> \n',
)
..addFlag(
kDryrunFlag,
defaultsTo: true,
help: 'whether we are going to upload the artifacts back to GCS for dryrun',
);
final ArgResults argResults = parser.parse(args);
const Platform platform = LocalPlatform();
final String codesignCertName = getValueFromArgs(kCodesignCertNameOption, argResults)!;
final String gCloudDownloadPath = getValueFromArgs(kGcsDownloadPathOption, argResults)!;
final String gCloudUploadPath = getValueFromArgs(kGcsUploadPathOption, argResults)!;
final String appSpecificPasswordFilePath = getValueFromArgs(kAppSpecificPasswordOption, argResults)!;
final String codesignAppstoreIDFilePath = getValueFromArgs(kCodesignAppstoreIDOption, argResults)!;
final String codesignTeamIDFilePath = getValueFromArgs(kCodesignTeamIDOption, argResults)!;
final bool dryrun = argResults[kDryrunFlag] as bool;
if (!platform.isMacOS) {
throw CodesignException(
'Error! Expected operating system "macos", actual operating system is: '
'"${platform.operatingSystem}"',
);
}
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,
fileSystem: fileSystem,
rootDirectory: rootDirectory,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
dryrun: dryrun,
gcsDownloadPath: gCloudDownloadPath,
gcsUploadPath: gCloudUploadPath,
googleCloudStorage: googleCloudStorage,
).validateAll();
}