package org.jfrog.hudson.BintrayPublish;
import com.google.common.collect.Lists;
import hudson.model.*;
import hudson.security.ACL;
import hudson.security.Permission;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.jfrog.build.api.release.BintrayUploadInfoOverride;
import org.jfrog.build.client.ArtifactoryVersion;
import org.jfrog.build.client.bintrayResponse.BintrayResponse;
import org.jfrog.build.extractor.clientConfiguration.client.ArtifactoryBuildInfoClient;
import org.jfrog.build.util.VersionException;
import org.jfrog.hudson.*;
import org.jfrog.hudson.util.BuildUniqueIdentifierHelper;
import org.jfrog.hudson.util.CredentialManager;
import org.jfrog.hudson.util.Credentials;
import org.jfrog.hudson.util.plugins.PluginsUtils;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
/**
* This action is added to a successful build in order to push built artifacts to Bintray
*
* @author Aviad Shikloshi
*/
public class BintrayPublishAction<C extends BuildInfoAwareConfigurator & DeployerOverrider> extends TaskAction implements BuildBadgeAction {
private final static String MINIMAL_SUPPORTED_VERSION = "3.5.3";
private final AbstractBuild build;
private final C configurator;
private boolean override;
private String subject;
private String repoName;
private String packageName;
private String versionName;
private String signMethod;
private List<String> licenses;
private String passphrase;
private String vcsUrl;
public BintrayPublishAction(AbstractBuild build, C configurator) {
this.build = build;
this.configurator = configurator;
}
public void doSubmit(StaplerRequest req, StaplerResponse resp) throws ServletException, IOException {
getACL().checkPermission(getPermission());
ArtifactoryServer artifactory = configurator.getArtifactoryServer();
resetFields();
req.bindParameters(this);
if (!override){
resetOverrideFields();
}
CredentialsConfig credentialsConfig = CredentialManager.getPreferredDeployer(configurator, configurator.getArtifactoryServer());
new PushToBintrayWorker(artifactory, credentialsConfig.getCredentials(build.getProject())).start();
resp.sendRedirect(".");
}
private void resetFields(){
resetOverrideFields();
resetQueryFields();
this.override = false;
}
private void resetOverrideFields(){
this.subject = null;
this.repoName = null;
this.packageName = null;
this.versionName = null;
this.licenses = Lists.newArrayList();
this.vcsUrl = null;
}
private void resetQueryFields() {
this.signMethod = null;
this.passphrase = null;
}
@SuppressWarnings({"UnusedDeclaration"})
public void doIndex(StaplerRequest req, StaplerResponse resp) throws IOException, ServletException {
req.getView(this, getCurrentAction()).forward(req, resp);
}
public boolean isOverride() {
return override;
}
public void setOverride(boolean override) {
this.override = override;
}
public boolean hasPushToBintrayPermission() {
return getACL().hasPermission(getPermission()) && PluginsUtils.isPushToBintrayEnabled();
}
public synchronized String getCurrentAction() {
return workerThread == null ? "form.jelly" : "progress.jelly";
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
if (StringUtils.isNotBlank(subject)) {
this.subject = subject.trim();
}
}
public String getRepoName() {
return repoName;
}
public void setRepoName(String repoName) {
if (StringUtils.isNotEmpty(repoName)) {
this.repoName = repoName.trim();
}
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
if (StringUtils.isNotEmpty(packageName)) {
this.packageName = packageName;
}
}
public String getVersionName() {
return versionName;
}
public void setVersionName(String versionName) {
if (StringUtils.isNotEmpty(versionName)) {
this.versionName = versionName;
}
}
public String getSignMethod() {
return signMethod;
}
public void setSignMethod(String signMethod) {
if (StringUtils.isNotEmpty(signMethod)) {
this.signMethod = signMethod;
}
}
public String getLicenses() {
return StringUtils.join(licenses, ",");
}
public void setLicenses(String licenses) {
if (StringUtils.isNotBlank(licenses)) {
this.licenses = Lists.newArrayList();
String[] licenseList = licenses.split(",");
for (String s : licenseList) {
s = s.trim();
if (!s.isEmpty()) {
this.licenses.add(s);
}
}
} else {
this.licenses = null;
}
}
public String getPassphrase() {
return passphrase;
}
public void setPassphrase(String passphrase) {
if (StringUtils.isNotEmpty(passphrase)) {
this.passphrase = passphrase.trim();
}
}
public String getVcsUrl() {
return vcsUrl;
}
public void setVcsUrl(String vcsUrl) {
if (StringUtils.isNotEmpty(vcsUrl)) {
this.vcsUrl = vcsUrl.trim();
}
}
public AbstractBuild getBuild() {
return build;
}
@Override
protected Permission getPermission() {
return ArtifactoryPlugin.PUSH_TO_BINTRAY;
}
@Override
protected ACL getACL() {
return build.getACL();
}
public String getIconFileName() {
return "/plugin/artifactory/images/bintray.png";
}
public String getDisplayName() {
return "Push to Bintray";
}
public String getUrlName() {
if (hasPushToBintrayPermission())
return "bintray";
else
return null;
}
// Check of the current Artifactory version supports "Push to Bintray" API
private boolean isValidArtifactoryVersion(ArtifactoryBuildInfoClient client) throws VersionException {
ArtifactoryVersion version = client.verifyCompatibleArtifactoryVersion();
return version.isAtLeast(new ArtifactoryVersion(MINIMAL_SUPPORTED_VERSION));
}
public final class PushToBintrayWorker extends TaskThread {
private final ArtifactoryServer artifactoryServer;
private final Credentials deployer;
public PushToBintrayWorker(ArtifactoryServer artifactoryServer, Credentials deployer) {
super(BintrayPublishAction.this, ListenerAndText.forMemory(null));
this.artifactoryServer = artifactoryServer;
this.deployer = deployer;
}
@Override
protected void perform(TaskListener listener) throws IOException {
long started = System.currentTimeMillis();
BintrayUploadInfoOverride uploadInfoOverride =
new BintrayUploadInfoOverride(subject, repoName, packageName, versionName, licenses, vcsUrl);
PrintStream logger = listener.getLogger();
ArtifactoryBuildInfoClient client =
artifactoryServer.createArtifactoryClient(deployer.getUsername(), deployer.getPassword(),
artifactoryServer.createProxyConfiguration(Jenkins.getInstance().proxy));
if (override && !uploadInfoOverride.isValid()) {
logger.print("Please fill in all mandatory fields when pushing to Bintray without descriptor file\n");
} else {
try {
logger.println("Publishing to Bintray...");
if (isValidArtifactoryVersion(client)) {
String buildName = configurator.isOverrideBuildName() ? configurator.getCustomBuildName() : BuildUniqueIdentifierHelper.getBuildName(build);
String buildNumber = BuildUniqueIdentifierHelper.getBuildNumber(build);
BintrayResponse response = client.pushToBintray(buildName, buildNumber, signMethod,
passphrase, uploadInfoOverride);
logger.println(response);
} else {
logger.println("Bintray push is not supported in your Artifactory version.");
}
} catch (Exception e) {
logger.println(e.getMessage());
}
}
// if the client gets back to the progress (after the redirect) page when this thread already done,
// she will get an error message because the log dies with the thread. So lets delay up to 2 seconds
long timeToWait = 2000 - (System.currentTimeMillis() - started);
if (timeToWait > 0) {
try {
Thread.sleep(timeToWait);
} catch (InterruptedException ie) {
ie.printStackTrace(listener.error(ie.getMessage()));
}
}
passphrase = null;
workerThread = null;
client.close();
}
}
}