/* * Copyright (C) 2010 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jfrog.hudson; import hudson.Extension; import hudson.Launcher; import hudson.maven.MavenModuleSet; import hudson.maven.MavenModuleSetBuild; import hudson.maven.reporters.MavenAbstractArtifactRecord; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Hudson; import hudson.model.Result; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; import hudson.tasks.Recorder; import hudson.util.Scrambler; import net.sf.json.JSONObject; import org.jfrog.build.client.ArtifactoryBuildInfoClient; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import java.io.IOException; import java.util.List; /** * {@link Publisher} for {@link hudson.maven.MavenModuleSetBuild} to deploy artifacts to Artifactory only after a build * is fully succeeded. ` * * @author Yossi Shaul * <p/> * TODO [yl]: This class is used by Gradle as well - need to factor out the repository data object form here. * Currently this class fqn is hardcoded into thge configuration so that */ public class ArtifactoryRedeployPublisher extends Recorder { /** * Repository URL and repository to deploy artifacts to. */ private final ServerDetails details; private final boolean deployArtifacts; private final String username; private final String scrambledPassword; private final boolean includeEnvVars; @DataBoundConstructor public ArtifactoryRedeployPublisher(ServerDetails details, boolean deployArtifacts, String username, String password, boolean includeEnvVars) { this.details = details; this.deployArtifacts = deployArtifacts; this.username = username; this.includeEnvVars = includeEnvVars; this.scrambledPassword = Scrambler.scramble(password); /*DescriptorExtensionList<Publisher, Descriptor<Publisher>> descriptors = Publisher.all(); Descriptor<Publisher> redeployPublisher = descriptors.find(RedeployPublisher.DescriptorImpl.class.getName()); if (redeployPublisher != null) { descriptors.remove(redeployPublisher); }*/ } public boolean isDeployArtifacts() { return deployArtifacts; } public boolean isIncludeEnvVars() { return includeEnvVars; } public String getUsername() { return username; } public String getPassword() { return Scrambler.descramble(scrambledPassword); } public String getArtifactoryName() { return details != null ? details.artifactoryName : null; } public String getRepositoryKey() { return details != null ? details.repositoryKey : null; } @Override public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { if (build.getResult().isWorseThan(Result.SUCCESS)) { return true; // build failed. Don't publish } if (getArtifactoryServer() == null) { listener.getLogger().format("No Artifactory server configured for %s. " + "Please check your configuration.", getArtifactoryName()).println(); build.setResult(Result.FAILURE); return true; } MavenAbstractArtifactRecord mar = getAction(build); if (mar == null) { listener.getLogger().println("No artifacts are recorded. Is this a Maven project?"); build.setResult(Result.FAILURE); return true; } ArtifactoryServer server = getArtifactoryServer(); ArtifactoryBuildInfoClient client = server.createArtifactoryClient(getUsername(), getPassword()); MavenModuleSetBuild mavenBuild = (MavenModuleSetBuild) build; try { verifySupportedArtifactoryVersion(client); if (deployArtifacts) { new ArtifactsDeployer(this, client, mavenBuild, mar, listener).deploy(); } new BuildInfoDeployer(this, client, mavenBuild, listener).deploy(); // add the result action build.getActions().add(new BuildInfoResultAction(this, build)); return true; } catch (Exception e) { e.printStackTrace(listener.error(e.getMessage())); } finally { client.shutdown(); } // failed build.setResult(Result.FAILURE); return true; } private void verifySupportedArtifactoryVersion(ArtifactoryBuildInfoClient client) throws Exception { // get the version of artifactory, if it is an unsupported version, an UnsupportedOperationException // will be thrown, and no deployment will commence. client.verifyCompatibleArtifactoryVersion(); } /** * Obtains the {@link MavenAbstractArtifactRecord} that we'll work on. * <p/> * This allows promoted-builds plugin to reuse the code for delayed deployment. */ protected MavenAbstractArtifactRecord getAction(AbstractBuild<?, ?> build) { return build.getAction(MavenAbstractArtifactRecord.class); } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } public ArtifactoryServer getArtifactoryServer() { List<ArtifactoryServer> servers = getDescriptor().getArtifactoryServers(); for (ArtifactoryServer server : servers) { if (server.getName().equals(getArtifactoryName())) { return server; } } return null; } @Extension public static class DescriptorImpl extends BuildStepDescriptor<Publisher> { public DescriptorImpl() { } @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return jobType == MavenModuleSet.class; } @Override public ArtifactoryRedeployPublisher newInstance(StaplerRequest req, JSONObject formData) throws FormException { return req.bindJSON(ArtifactoryRedeployPublisher.class, formData); } @Override public String getDisplayName() { return "Deploy artifacts to Artifactory"; //return Messages.RedeployPublisher_getDisplayName(); } /** * Returns the list of {@link ArtifactoryServer} configured. * * @return can be empty but never null. */ public List<ArtifactoryServer> getArtifactoryServers() { ArtifactoryBuilder.DescriptorImpl descriptor = (ArtifactoryBuilder.DescriptorImpl) Hudson.getInstance().getDescriptor(ArtifactoryBuilder.class); return descriptor.getArtifactoryServers(); } } }