Skip to content

Commit

Permalink
Merge branch 'master' into feature/JENKINS-38731
Browse files Browse the repository at this point in the history
  • Loading branch information
orrc committed Dec 29, 2017
2 parents 11b97cb + 14adff8 commit a4c4faa
Show file tree
Hide file tree
Showing 12 changed files with 60 additions and 37 deletions.
2 changes: 2 additions & 0 deletions Jenkinsfile
@@ -0,0 +1,2 @@
// Build on ci.jenkins.io; see https://github.com/jenkins-infra/pipeline-library
buildPlugin()
14 changes: 6 additions & 8 deletions pom.xml
Expand Up @@ -4,17 +4,15 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.14</version>
<version>3.2</version>
<relativePath />
</parent>

<properties>
<jenkins.version>1.642.3</jenkins.version>
<jenkins.version>2.32</jenkins.version>
<java.level>7</java.level>
<jenkins-test-harness.version>2.15</jenkins-test-harness.version>
</properties>

<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>google-play-android-publisher</artifactId>
<version>1.6-SNAPSHOT</version>
<packaging>hpi</packaging>
Expand All @@ -41,25 +39,25 @@
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-androidpublisher</artifactId>
<version>v2-rev45-1.22.0</version>
<version>v2-rev47-1.22.0</version>
</dependency>

<dependency>
<groupId>net.dongliu</groupId>
<artifactId>apk-parser</artifactId>
<version>2.1.6</version>
<version>2.4.1</version>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>google-oauth-plugin</artifactId>
<version>0.3</version>
<version>0.5</version>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>token-macro</artifactId>
<version>1.10</version>
<version>1.11</version>
</dependency>

<dependency>
Expand Down
@@ -1,6 +1,7 @@
package org.jenkinsci.plugins.googleplayandroidpublisher;

import com.google.jenkins.plugins.credentials.oauth.GoogleRobotCredentials;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.AbortException;
import hudson.Extension;
import hudson.FilePath;
Expand Down Expand Up @@ -124,6 +125,7 @@ public String getRolloutPercentage() {
return fixEmptyAndTrim(rolloutPercentage);
}

@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
private double getRolloutPercentageValue() throws IOException, InterruptedException {
String pct = getRolloutPercentage();
if (pct != null) {
Expand All @@ -134,6 +136,7 @@ private double getRolloutPercentageValue() throws IOException, InterruptedExcept
return tryParseNumber(expand(pct), DEFAULT_PERCENTAGE).doubleValue();
}

@SuppressFBWarnings("EI_EXPOSE_REP")
public RecentChanges[] getRecentChangeList() {
return recentChangeList;
}
Expand Down Expand Up @@ -235,7 +238,7 @@ private boolean publishApk(@Nonnull Run<?, ?> run, @Nonnull FilePath workspace,
return false;
} catch (ParserException e) {
// Show a bit more information for APK parse exceptions
logger.println(String.format("File does not appear to be a valid APK: %s\n- %s",
logger.println(String.format("File does not appear to be a valid APK: %s%n- %s",
apk.getRemote(), e.getMessage()));
return false;
} catch (IOException e) {
Expand All @@ -248,7 +251,8 @@ private boolean publishApk(@Nonnull Run<?, ?> run, @Nonnull FilePath workspace,

// If there are multiple APKs, ensure that all have the same application ID
if (applicationIds.size() != 1) {
logger.println("Multiple APKs were found but they have inconsistent application IDs:");
logger.println(String.format("Multiple APKs matched the pattern '%s', " +
"but they have inconsistent application IDs:", filesPattern));
for (String id : applicationIds) {
logger.print("- ");
logger.println(id);
Expand Down Expand Up @@ -311,7 +315,7 @@ private boolean publishApk(@Nonnull Run<?, ?> run, @Nonnull FilePath workspace,
&& fileSet.getMainFile() == null) {
logger.println(String.format("Patch expansion file '%s' was provided, but no main expansion file " +
"was provided, and the option to reuse a pre-existing expansion file was " +
"disabled.\nGoogle Play requires that each APK with a patch file also has a main " +
"disabled.%nGoogle Play requires that each APK with a patch file also has a main " +
"file.", fileSet.getPatchFile().getName()));
return false;
}
Expand Down Expand Up @@ -378,7 +382,7 @@ public static class DescriptorImpl extends Descriptor<RecentChanges> {

@Override
public String getDisplayName() {
return null;
return "Recent changes";
}

public ComboBoxModel doFillLanguageItems() {
Expand Down
Expand Up @@ -73,7 +73,7 @@ protected boolean shouldReducingRolloutPercentageCauseFailure() {

protected Boolean execute() throws IOException, InterruptedException, UploadException {
// Open an edit via the Google Play API, thereby ensuring that our credentials etc. are working
logger.println(String.format("Authenticating to Google Play API...\n- Credential: %s\n- Application ID: %s",
logger.println(String.format("Authenticating to Google Play API...%n- Credential: %s%n- Application ID: %s%n",
getCredentialName(), applicationId));
createEdit(applicationId);

Expand Down
Expand Up @@ -9,20 +9,20 @@ public class Constants {
public static final String DEOBFUSCATION_FILE_TYPE_PROGUARD = "proguard";

/** File name pattern which expansion files must match. */
public static final Pattern OBB_FILE_REGEX =
static final Pattern OBB_FILE_REGEX =
Pattern.compile("^(main|patch)\\.([0-9]+)\\.([._a-z0-9]+)\\.obb$", Pattern.CASE_INSENSITIVE);

/** Expansion file type: main */
public static final String OBB_FILE_TYPE_MAIN = "main";
static final String OBB_FILE_TYPE_MAIN = "main";

/** Expansion file type: patch */
public static final String OBB_FILE_TYPE_PATCH = "patch";
static final String OBB_FILE_TYPE_PATCH = "patch";

/** Formatter that only displays decimal places when necessary. */
public static final DecimalFormat PERCENTAGE_FORMATTER = new DecimalFormat("#.#");
static final DecimalFormat PERCENTAGE_FORMATTER = new DecimalFormat("#.#");

/** Allowed percentage values when doing a staged rollout to production. */
public static final double[] ROLLOUT_PERCENTAGES = { 0.5, 1, 5, 10, 20, 50, 100 };
public static final double DEFAULT_PERCENTAGE = 100;
static final double[] ROLLOUT_PERCENTAGES = { 0.5, 1, 5, 10, 20, 50, 100 };
static final double DEFAULT_PERCENTAGE = 100;

}
Expand Up @@ -3,9 +3,11 @@
import com.google.jenkins.plugins.credentials.oauth.GoogleOAuth2ScopeRequirement;
import com.google.jenkins.plugins.credentials.oauth.GoogleRobotCredentials;
import com.google.jenkins.plugins.credentials.oauth.GoogleRobotPrivateKeyCredentials;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;

import static hudson.Util.fixEmptyAndTrim;
Expand All @@ -24,23 +26,24 @@ public CredentialsHandler(String googleCredentialsId) throws CredentialsExceptio
}

/** @return The Google API credentials configured for this job. */
@SuppressFBWarnings("VA_FORMAT_STRING_USES_NEWLINE")
public final GoogleRobotCredentials getServiceAccountCredentials() throws UploadException {
try {
GoogleOAuth2ScopeRequirement req = new AndroidPublisherScopeRequirement();
GoogleRobotCredentials credentials = GoogleRobotCredentials.getById(googleCredentialsId);
if (credentials == null) {
throw new CredentialsException(String.format("The Google Service Account credential '%s' "
+ "could not be found.\n\tIf you renamed the credential since configuring this job, you must "
+ "could not be found.%n\tIf you renamed the credential since configuring this job, you must "
+ "re-configure this job, choosing the new credential name", googleCredentialsId));
}
return credentials.forRemote(req);
} catch (GoogleRobotPrivateKeyCredentials.AccountIdNotSetException e) {
throw new CredentialsException(String.format("The Google Service Account credential '%s' "
+ "has not been configured correctly.\n\tUpdate the credential, ensuring that the required data "
+ "has not been configured correctly.%n\tUpdate the credential, ensuring that the required data "
+ "have been entered, then try again", googleCredentialsId));
} catch (GoogleRobotPrivateKeyCredentials.PrivateKeyNotSetException e) {
throw new CredentialsException(String.format("The Google Service Account credential '%s' "
+ "has not been configured correctly.\n\tUpdate the credential, ensuring that the required data "
+ "has not been configured correctly.%n\tUpdate the credential, ensuring that the required data "
+ "have been entered, then try again", googleCredentialsId));
} catch (NullPointerException e) {
// This should really be handled by the Google OAuth plugin
Expand All @@ -57,6 +60,10 @@ public final GoogleRobotCredentials getServiceAccountCredentials() throws Upload
}
throw new UploadException(e);
} catch (GeneralSecurityException e) {
if (ExceptionUtils.getRootCause(e) instanceof IOException) {
throw new UploadException("Failed to validate Google Service Account credential against the " +
"Google API servers. Check internet connectivity on the Jenkins server and try again.", e);
}
throw new UploadException(e);
}
}
Expand Down
@@ -0,0 +1,10 @@
package org.jenkinsci.plugins.googleplayandroidpublisher;

/** Thrown when there's a temporary problem with validating the Google Play credentials. */
public class EphemeralCredentialsException extends UploadException {

public EphemeralCredentialsException(String message) {
super(message);
}

}
@@ -1,5 +1,6 @@
package org.jenkinsci.plugins.googleplayandroidpublisher;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.model.AbstractProject;
import hudson.model.Describable;
import hudson.tasks.BuildStep;
Expand Down Expand Up @@ -51,6 +52,9 @@ public FormValidation doCheckGoogleCredentialsId(@QueryParameter String value) {
// Otherwise, attempt to load the given credential to see whether it has been set up correctly
try {
new CredentialsHandler(value).getServiceAccountCredentials();
} catch (EphemeralCredentialsException e) {
// Loading the credential (apparently) goes online, so we may get ephemeral connectivity problems
return FormValidation.warning(e.getMessage());
} catch (UploadException e) {
return FormValidation.error(e.getMessage());
}
Expand All @@ -73,6 +77,7 @@ public FormValidation doCheckTrackName(@QueryParameter String value) {
return FormValidation.ok();
}

@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public FormValidation doCheckRolloutPercentage(@QueryParameter String value) {
value = fixEmptyAndTrim(value);
if (value == null || value.matches(REGEX_VARIABLE)) {
Expand Down
@@ -1,6 +1,7 @@
package org.jenkinsci.plugins.googleplayandroidpublisher;

import com.google.jenkins.plugins.credentials.oauth.GoogleRobotCredentials;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.AbortException;
import hudson.EnvVars;
import hudson.Extension;
Expand Down Expand Up @@ -106,6 +107,7 @@ public String getRolloutPercentage() {
return fixEmptyAndTrim(rolloutPercentage);
}

@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
private double getRolloutPercentageValue() throws IOException, InterruptedException {
String pct = getRolloutPercentage();
if (pct != null) {
Expand Down Expand Up @@ -169,6 +171,7 @@ public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath workspace, @Nonnul
}
}

@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
private boolean assignApk(@Nonnull Run<?, ?> run, @Nonnull FilePath workspace, @Nonnull TaskListener listener)
throws IOException, InterruptedException {
final PrintStream logger = listener.getLogger();
Expand Down Expand Up @@ -232,7 +235,7 @@ private AppInfo getApplicationInfoForApks(FilePath workspace, PrintStream logger
} catch (ZipException e) {
throw new IOException(String.format("File does not appear to be a valid APK: %s", apk.getRemote()), e);
} catch (ParserException e) {
logger.println(String.format("File does not appear to be a valid APK: %s\n- %s",
logger.println(String.format("File does not appear to be a valid APK: %s%n- %s",
apk.getRemote(), e.getMessage()));
throw e;
}
Expand Down
Expand Up @@ -40,7 +40,7 @@ protected boolean shouldReducingRolloutPercentageCauseFailure() {

protected Boolean execute() throws IOException, InterruptedException, UploadException {
// Open an edit via the Google Play API, thereby ensuring that our credentials etc. are working
logger.println(String.format("Authenticating to Google Play API...\n- Credential: %s\n- Application ID: %s",
logger.println(String.format("Authenticating to Google Play API...%n- Credential: %s%n- Application ID: %s",
getCredentialName(), applicationId));
createEdit(applicationId);

Expand Down
Expand Up @@ -72,8 +72,8 @@ protected void assignApksToTrack(Collection<Integer> versionCodes, ReleaseTrack
trackToAssign.setUserFraction(rolloutFraction);

// Check whether we also need to override the desired rollout percentage
Double currentFraction = rolloutTrack == null ? rolloutFraction : rolloutTrack.getUserFraction();
if (currentFraction != null && currentFraction > rolloutFraction) {
double currentFraction = rolloutTrack == null ? rolloutFraction : rolloutTrack.getUserFraction();
if (currentFraction > rolloutFraction) {
if (shouldReducingRolloutPercentageCauseFailure()) {
throw new UploadException(String.format("Staged rollout percentage cannot be reduced from " +
"%s%% to the configured %s%%",
Expand Down Expand Up @@ -104,7 +104,7 @@ protected void assignApksToTrack(Collection<Integer> versionCodes, ReleaseTrack
}
Track updatedTrack =
editService.tracks().update(applicationId, editId, trackToAssign.getTrack(), trackToAssign).execute();
logger.println(String.format("The %s release track will now contain the APK(s): %s\n", track,
logger.println(String.format("The %s release track will now contain APK(s) with version code(s): %s%n", track,
join(updatedTrack.getVersionCodes(), ", ")));
}

Expand Down
Expand Up @@ -11,7 +11,7 @@
import hudson.remoting.VirtualChannel;
import jenkins.MasterToSlaveFileCallable;
import jenkins.model.Jenkins;
import net.dongliu.apk.parser.ApkParser;
import net.dongliu.apk.parser.ApkParsers;
import net.dongliu.apk.parser.bean.ApkMeta;
import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException;
import org.jenkinsci.plugins.tokenmacro.TokenMacro;
Expand All @@ -21,7 +21,6 @@
import java.security.GeneralSecurityException;

import static hudson.Util.fixEmptyAndTrim;
import static hudson.Util.replaceMacro;

public class Util {

Expand Down Expand Up @@ -56,7 +55,7 @@ public String invoke(File f, VirtualChannel channel) throws IOException, Interru
}

/** @return The version code of the given APK file. */
public static int getVersionCode(FilePath apk) throws IOException, InterruptedException {
static int getVersionCode(FilePath apk) throws IOException, InterruptedException {
return apk.act(new MasterToSlaveFileCallable<Integer>() {
public Integer invoke(File f, VirtualChannel channel) throws IOException, InterruptedException {
return getApkMetadata(f).getVersionCode().intValue();
Expand All @@ -65,13 +64,8 @@ public Integer invoke(File f, VirtualChannel channel) throws IOException, Interr
}

/** @return The application metadata of the given APK file. */
public static ApkMeta getApkMetadata(File apk) throws IOException, InterruptedException {
ApkParser apkParser = new ApkParser(apk);
try {
return apkParser.getApkMeta();
} finally {
apkParser.close();
}
static ApkMeta getApkMetadata(File apk) throws IOException {
return ApkParsers.getMetaInfo(apk);
}

/** @return The given value with variables expanded and trimmed; {@code null} if that results in an empty string. */
Expand Down

0 comments on commit a4c4faa

Please sign in to comment.