Commit 3a37c36c authored by Jason Simmons's avatar Jason Simmons

Add flags for signing an APK with a custom keystore

Fixes #1010
parent 35b18c3d
......@@ -20,8 +20,12 @@ import 'start.dart';
const String _kDefaultAndroidManifestPath = 'apk/AndroidManifest.xml';
const String _kDefaultOutputPath = 'build/app.apk';
const String _kDefaultResourcesPath = 'apk/res';
const String _kKeystoreKeyName = "chromiumdebugkey";
const String _kKeystorePassword = "chromium";
// Alias of the key provided in the Chromium debug keystore
const String _kDebugKeystoreKeyAlias = "chromiumdebugkey";
// Password for the Chromium debug keystore
const String _kDebugKeystorePassword = "chromium";
const String _kAndroidPlatformVersion = '22';
const String _kBuildToolsVersion = '22.0.1';
......@@ -83,12 +87,13 @@ class _ApkBuilder {
runCheckedSync(packageArgs);
}
void sign(File keystore, String keystorePassword, String keyName, File outputApk) {
void sign(File keystore, String keystorePassword, String keyAlias, String keyPassword, File outputApk) {
runCheckedSync([_jarsigner,
'-keystore', keystore.path,
'-storepass', keystorePassword,
'-keypass', keyPassword,
outputApk.path,
keyName,
keyAlias,
]);
}
......@@ -103,7 +108,7 @@ class _ApkComponents {
File icuData;
File classesDex;
File libSkyShell;
File keystore;
File debugKeystore;
Directory resources;
}
......@@ -132,6 +137,18 @@ class ApkCommand extends FlutterCommand {
abbr: 'f',
defaultsTo: '',
help: 'Path to the FLX file. If this is not provided, an FLX will be built.');
argParser.addOption('keystore',
defaultsTo: '',
help: 'Path to the keystore used to sign the app.');
argParser.addOption('keystore-password',
defaultsTo: '',
help: 'Password used to access the keystore.');
argParser.addOption('keystore-key-alias',
defaultsTo: '',
help: 'Alias of the entry within the keystore.');
argParser.addOption('keystore-key-password',
defaultsTo: '',
help: 'Password for the entry within the keystore.');
}
Future<_ApkComponents> _findApkComponents(BuildConfiguration config) async {
......@@ -168,7 +185,7 @@ class ApkCommand extends FlutterCommand {
components.icuData = new File(artifactPaths[0]);
components.classesDex = new File(artifactPaths[1]);
components.libSkyShell = new File(artifactPaths[2]);
components.keystore = new File(artifactPaths[3]);
components.debugKeystore = new File(artifactPaths[3]);
components.resources = new Directory(argResults['resources']);
if (!components.resources.existsSync()) {
......@@ -188,7 +205,7 @@ class ApkCommand extends FlutterCommand {
return null;
}
for (File f in [components.manifest, components.icuData, components.classesDex,
components.libSkyShell, components.keystore]) {
components.libSkyShell, components.debugKeystore]) {
if (!f.existsSync()) {
logging.severe('Can not locate file: ${f.path}');
return null;
......@@ -213,7 +230,10 @@ class ApkCommand extends FlutterCommand {
File unalignedApk = new File('${tempDir.path}/app.apk.unaligned');
builder.package(unalignedApk, components.manifest, assetBuilder.directory,
artifactBuilder.directory, components.resources);
builder.sign(components.keystore, _kKeystorePassword, _kKeystoreKeyName, unalignedApk);
int signResult = _signApk(builder, components, unalignedApk);
if (signResult != 0)
return signResult;
File finalApk = new File(argResults['output-file']);
ensureDirectoryExists(finalApk.path);
......@@ -227,6 +247,36 @@ class ApkCommand extends FlutterCommand {
}
}
int _signApk(_ApkBuilder builder, _ApkComponents components, File apk) {
File keystore;
String keystorePassword;
String keyAlias;
String keyPassword;
if (argResults['keystore'].isEmpty) {
logging.warning('Signing the APK using the debug keystore');
keystore = components.debugKeystore;
keystorePassword = _kDebugKeystorePassword;
keyAlias = _kDebugKeystoreKeyAlias;
keyPassword = _kDebugKeystorePassword;
} else {
keystore = new File(argResults['keystore']);
keystorePassword = argResults['keystore-password'];
keyAlias = argResults['keystore-key-alias'];
if (keystorePassword.isEmpty || keyAlias.isEmpty) {
logging.severe('Must provide a keystore password and a key alias');
return 1;
}
keyPassword = argResults['keystore-key-password'];
if (keyPassword.isEmpty)
keyPassword = keystorePassword;
}
builder.sign(keystore, keystorePassword, keyAlias, keyPassword, apk);
return 0;
}
@override
Future<int> runInProject() async {
BuildConfiguration config = buildConfigurations.firstWhere(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment