blob: b5a73e9460321fea93c7bd15d2807d40571a618e [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:logging/logging.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 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';
/// 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,
/// 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 go through the notarization
/// process.
///
/// 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
/// '--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.
/// 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.
///
/// Note: this tool uses Apple developer identity from keychain named build.keychain.
/// You can create/delete build.keychain by following the steps below:
/// /usr/bin/security create-keychain -p '' build.keychain # for create
/// /usr/bin/security delete-keychain build.keychain # for delete
///
/// 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
/// --input-zip-file-path=/a/input.zip
/// --output-zip-file-path=/b/output.zip
/// ```
Future<void> main(List<String> args) async {
Logger.root.onRecord.listen((LogRecord record) {
stdout.writeln(record.toString());
});
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(
kInputZipPathOption,
help: 'File path to the unsigned artifact zip file.',
)
..addOption(
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>.',
)
..addOption(
kCodesignAppstoreIDOption,
help:
'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>.',
)
..addFlag(
kDryrunFlag,
defaultsTo: true,
help: 'whether we are going to skip the notarization process.',
);
final ArgResults argResults = parser.parse(args);
const Platform platform = LocalPlatform();
final String codesignCertName = getValueFromArgs(kCodesignCertNameOption, 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)!;
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();
return FileCodesignVisitor(
codesignCertName: codesignCertName,
fileSystem: fileSystem,
rootDirectory: rootDirectory,
appSpecificPasswordFilePath: appSpecificPasswordFilePath,
codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
codesignTeamIDFilePath: codesignTeamIDFilePath,
processManager: processManager,
dryrun: dryrun,
inputZipPath: inputZipPath,
outputZipPath: outputZipPath,
).validateAll();
}