Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Xamarin.Android.Build.Tasks] Add EnvVar and File support for Signing APKs #3522

Merged
merged 1 commit into from
Aug 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Documentation/guides/BuildProcess.md
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,15 @@ server, the following MSBuild properties can be used:
the value entered when `keytool` asks **Enter key password for
$(AndroidSigningKeyAlias)**.

You can use the raw password here, however if you want to hide your password in logs
you can use a prefix of env: or file: to point it to an Environment variable or
a file. For example

```
env:<PasswordEnvironentVariable>
file:<PasswordFile>
```

- **AndroidSigningKeyStore** &ndash; Specifies the filename of the
keystore file created by `keytool`. This corresponds to the value
provided to the **keytool -keystore** option.
Expand All @@ -1082,6 +1091,15 @@ server, the following MSBuild properties can be used:
`keytool` when creating the keystore file and asked **Enter
keystore password:**.

You can use the raw password here, however if you want to hide your password in logs
you can use a prefix of env: or file: to point it to an Environment variable or
a file. For example

```
env:<PasswordEnvironentVariable>
file:<PasswordFile>
```

For example, consider the following `keytool` invocation:

```shell
Expand Down Expand Up @@ -1111,6 +1129,26 @@ To use the keystore generated above, use the property group:
</PropertyGroup>
```

To use an environment variable to store your password you can do the following

```xml
<PropertyGroup>
<AndroidSigningStorePass>env:SomeEnvironmentVariableWithThePassword</AndroidSigningStorePass>
<AndroidSigningKeyPass>env:SomeEnvironmentVariableWithThePassword</AndroidSigningKeyPass>
</PropertyGroup>
```

to use a file you can do the following

To use an environment variable to store your password you can do the following

```xml
<PropertyGroup>
<AndroidSigningStorePass>file:SomeFileWithThePassword</AndroidSigningStorePass>
<AndroidSigningKeyPass>file:SomeFileWithThePassword</AndroidSigningKeyPass>
</PropertyGroup>
```

<a name="Build_Actions" />

## Build Actions
Expand Down
34 changes: 32 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/AndroidApkSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,27 @@ public class AndroidApkSigner : JavaToolTask
[Required]
public string KeyAlias { get; set; }

/// <summary>
/// The Password for the Key.
/// You can use the raw password here, however if you want to hide your password in logs
/// you can use a preview of env: or file: to point it to an Environment variable or
/// a file.
///
/// env:<PasswordEnvironentVariable>
/// file:<PasswordFile>
/// </summary>
[Required]
public string KeyPass { get; set; }

/// <summary>
/// The Password for the Keystore.
/// You can use the raw password here, however if you want to hide your password in logs
/// you can use a preview of env: or file: to point it to an Environment variable or
/// a file.
///
/// env:<PasswordEnvironentVariable>
/// file:<PasswordFile>
/// </summary>
[Required]
public string StorePass { get; set; }

Expand All @@ -41,6 +59,18 @@ public override bool Execute ()
return base.Execute ();
}

void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value)
{
if (value.StartsWith ("env:", StringComparison.Ordinal)) {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", value);
}
else if (value.StartsWith ("file:", StringComparison.Ordinal)) {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} file:", value.Replace ("file:", string.Empty));
} else {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} pass:", value);
}
}

protected override string GenerateCommandLineCommands ()
{
var cmd = new CommandLineBuilder ();
Expand All @@ -59,9 +89,9 @@ protected override string GenerateCommandLineCommands ()
cmd.AppendSwitchIfNotNull ("-jar ", ApkSignerJar);
cmd.AppendSwitch ("sign");
cmd.AppendSwitchIfNotNull ("--ks ", KeyStore);
cmd.AppendSwitchIfNotNull ("--ks-pass pass:", StorePass);
AddStorePass (cmd, "--ks-pass", StorePass);
cmd.AppendSwitchIfNotNull ("--ks-key-alias ", KeyAlias);
cmd.AppendSwitchIfNotNull ("--key-pass pass:", KeyPass);
AddStorePass (cmd, "--key-pass", KeyPass);
cmd.AppendSwitchIfNotNull ("--min-sdk-version ", minSdk.ToString ());
cmd.AppendSwitchIfNotNull ("--max-sdk-version ", maxSdk.ToString ());

Expand Down
35 changes: 33 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/AndroidSignPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,27 @@ public class AndroidSignPackage : AndroidToolTask
[Required]
public string KeyAlias { get; set; }

/// <summary>
/// The Password for the Key.
/// You can use the raw password here, however if you want to hide your password in logs
/// you can use a preview of env: or file: to point it to an Environment variable or
/// a file.
///
/// env:<PasswordEnvironentVariable>
/// file:<PasswordFile>
/// </summary>
[Required]
public string KeyPass { get; set; }

/// <summary>
/// The Password for the Keystore.
/// You can use the raw password here, however if you want to hide your password in logs
/// you can use a preview of env: or file: to point it to an Environment variable or
/// a file.
///
/// env:<PasswordEnvironentVariable>
/// file:<PasswordFile>
/// </summary>
[Required]
public string StorePass { get; set; }

Expand All @@ -48,6 +66,19 @@ public class AndroidSignPackage : AndroidToolTask

protected override string DefaultErrorCode => "ANDJS0000";

void AddStorePass (CommandLineBuilder cmd, string cmdLineSwitch, string value)
{
string pass = value.Replace ("env:", string.Empty).Replace ("file:", string.Empty);
if (value.StartsWith ("env:", StringComparison.Ordinal)) {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:env ", pass);
}
else if (value.StartsWith ("file:", StringComparison.Ordinal)) {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch}:file ", pass);
} else {
cmd.AppendSwitchIfNotNull ($"{cmdLineSwitch} ", pass);
}
}

protected override string GenerateCommandLineCommands ()
{
var fileName = Path.GetFileNameWithoutExtension (UnsignedApk);
Expand All @@ -57,8 +88,8 @@ protected override string GenerateCommandLineCommands ()
cmd.AppendSwitchIfNotNull ("-tsa ", TimestampAuthorityUrl);
cmd.AppendSwitchIfNotNull ("-tsacert ", TimestampAuthorityCertificateAlias);
cmd.AppendSwitchIfNotNull ("-keystore ", KeyStore);
cmd.AppendSwitchIfNotNull ("-storepass ", StorePass);
cmd.AppendSwitchIfNotNull ("-keypass ", KeyPass);
AddStorePass (cmd, "-storepass", StorePass);
AddStorePass (cmd, "-keypass", KeyPass);
cmd.AppendSwitchIfNotNull ("-digestalg ", DigestAlgorithm);
cmd.AppendSwitchIfNotNull ("-sigalg ", SigningAlgorithm);
cmd.AppendSwitchIfNotNull ("-signedjar ", Path.Combine (SignedApkDirectory, $"{fileName}{FileSuffix}{extension}" ));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1684,20 +1684,27 @@ public void BuildLibraryWhichUsesResources ([Values (false, true)] bool isReleas

#pragma warning disable 414
static object [] AndroidStoreKeyTests = new object [] {
// isRelease, AndroidKeyStore, ExpectedResult
new object[] { false , "False" , "debug.keystore"},
new object[] { true , "False" , "debug.keystore"},
new object[] { false , "True" , "-keystore test.keystore"},
new object[] { true , "True" , "-keystore test.keystore"},
new object[] { false , "" , "debug.keystore"},
new object[] { true , "" , "debug.keystore"},
// isRelease, AndroidKeyStore, password, ExpectedResult
new object[] { false , "False" , "android", "debug.keystore"},
new object[] { true , "False" , "android", "debug.keystore"},
new object[] { false , "True" , "android", "-keystore test.keystore"},
new object[] { true , "True" , "android", "-keystore test.keystore"},
new object[] { false , "" , "android", "debug.keystore"},
new object[] { true , "" , "android", "debug.keystore"},
new object[] { false , "True" , "env:android", "-keystore test.keystore"},
new object[] { true , "True" , "env:android", "-keystore test.keystore"},
new object[] { false , "True" , "file:android", "-keystore test.keystore"},
new object[] { true , "True" , "file:android", "-keystore test.keystore"},
};
#pragma warning restore 414

[Test]
[TestCaseSource (nameof (AndroidStoreKeyTests))]
public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string expected)
public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string password, string expected)
{
string path = Path.Combine ("temp", TestName);
string storepassfile = Path.Combine (Root, path, "storepass.txt");
string keypassfile = Path.Combine (Root, path, "keypass.txt");
byte [] data;
using (var stream = typeof (XamarinAndroidCommonProject).Assembly.GetManifestResourceStream ("Xamarin.ProjectTools.Resources.Base.test.keystore")) {
data = new byte [stream.Length];
Expand All @@ -1706,17 +1713,35 @@ public void TestAndroidStoreKey (bool isRelease, string androidKeyStore, string
var proj = new XamarinAndroidApplicationProject () {
IsRelease = isRelease
};
Dictionary<string, string> envVar = new Dictionary<string, string> ();
if (password.StartsWith ("env:", StringComparison.Ordinal)) {
envVar.Add ("_MYPASSWORD", password.Replace ("env:", string.Empty));
proj.SetProperty ("AndroidSigningStorePass", "env:_MYPASSWORD");
proj.SetProperty ("AndroidSigningKeyPass", "env:_MYPASSWORD");
} else if (password.StartsWith ("file:", StringComparison.Ordinal)) {
proj.SetProperty ("AndroidSigningStorePass", $"file:{storepassfile}");
proj.SetProperty ("AndroidSigningKeyPass", $"file:{keypassfile}");
} else {
proj.SetProperty ("AndroidSigningStorePass", password);
proj.SetProperty ("AndroidSigningKeyPass", password);
}
proj.SetProperty ("AndroidKeyStore", androidKeyStore);
proj.SetProperty ("AndroidSigningKeyStore", "test.keystore");
proj.SetProperty ("AndroidSigningStorePass", "android");
proj.SetProperty ("AndroidSigningKeyAlias", "mykey");
proj.SetProperty ("AndroidSigningKeyPass", "android");
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "test.keystore") {
BinaryContent = () => data
});
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName), false, false)) {
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "storepass.txt") {
TextContent = () => password.Replace ("file:", string.Empty),
Encoding = Encoding.ASCII,
});
proj.OtherBuildItems.Add (new BuildItem (BuildActions.None, "keypass.txt") {
TextContent = () => password.Replace ("file:", string.Empty),
Encoding = Encoding.ASCII,
});
using (var b = CreateApkBuilder (path, false, false)) {
b.Verbosity = LoggerVerbosity.Diagnostic;
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
Assert.IsTrue (b.Build (proj, environmentVariables: envVar), "Build should have succeeded.");
StringAssertEx.Contains (expected, b.LastBuildOutput,
"The Wrong keystore was used to sign the apk");
}
Expand Down