All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hudson.tasks.ArtifactArchiver Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 *
 * Copyright (c) 2004-2011 Oracle Corporation.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *
 *    Inc., Kohsuke Kawaguchi, Brian Westrich,   Jean-Baptiste Quenot, Anton Kozak
 *
 *
 *******************************************************************************/ 

package hudson.tasks;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Hudson;
import hudson.model.Result;
import hudson.util.FormValidation;
import java.io.File;
import java.io.IOException;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

/**
 * Copies the artifacts into an archive directory.
 *
 * @author Kohsuke Kawaguchi
 */
public class ArtifactArchiver extends Recorder {

    /**
     * Comma- or space-separated list of patterns of files/directories to be
     * archived.
     */
    private final String artifacts;
    /**
     * Possibly null 'excludes' pattern as in Ant.
     */
    private final String excludes;
    /**
     * Type of compression with will be applied to artifacts before
     * master<->slave transfer.
     */
    private FilePath.TarCompression compressionType;
    /**
     * Just keep the last successful artifact set, no more.
     */
    private final boolean latestOnly;
    private final boolean autoValidateFileMask;
    private static final Boolean allowEmptyArchive =
            Boolean.getBoolean(ArtifactArchiver.class.getName() + ".warnOnEmpty");

    /**
     * @deprecated as of 2.0.1
     */
    @Deprecated
    public ArtifactArchiver(String artifacts, String excludes, boolean latestOnly) {
        this(artifacts, excludes, latestOnly, FilePath.TarCompression.GZIP.name());
    }

    /**
     * @deprecated as of 2.0.2
     */
    @Deprecated
    public ArtifactArchiver(String artifacts, String excludes, boolean latestOnly, String compressionType) {
        this(artifacts, excludes, latestOnly, compressionType, false);
    }

    @DataBoundConstructor
    public ArtifactArchiver(String artifacts, String excludes, boolean latestOnly, String compressionType,
            boolean autoValidateFileMask) {
        this.artifacts = Util.fixEmptyAndTrim(artifacts);
        this.excludes = Util.fixEmptyAndTrim(excludes);
        this.latestOnly = latestOnly;
        setCompressionType(compressionType);
        this.autoValidateFileMask = autoValidateFileMask;
    }

    /**
     * @inheritDoc
     */
    @Override
    public boolean needsToRun(Result buildResult) {
        //TODO it seems we shouldn't archive if build result is worse than SUCCESS, investigate this
        return buildResult.isBetterThan(Result.ABORTED);
    }

    public String getArtifacts() {
        return artifacts;
    }

    public String getExcludes() {
        return excludes;
    }

    public boolean isLatestOnly() {
        return latestOnly;
    }

    public boolean isAutoValidateFileMask() {
        return autoValidateFileMask;
    }

    /**
     * Returns compression type.
     *
     * @return compression type.
     */
    public FilePath.TarCompression getCompressionType() {
        return compressionType;
    }

    /**
     * Sets compression type.
     *
     * @param compression compression type.
     */
    public void setCompressionType(String compression) {
        try {
            compressionType = (compression != null ? FilePath.TarCompression.valueOf(compression)
                    : FilePath.TarCompression.GZIP);
        } catch (IllegalArgumentException e) {
            compressionType = FilePath.TarCompression.GZIP;
        }
    }

    private void listenerWarnOrError(BuildListener listener, String message) {
        if (allowEmptyArchive) {
            listener.getLogger().println(String.format("WARN: %s", message));
        } else {
            listener.error(message);
        }
    }

    @Override
    public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener)
            throws InterruptedException {
        if (artifacts.length() == 0) {
            listener.error(Messages.ArtifactArchiver_NoIncludes());
            build.setResult(Result.FAILURE);
            return true;
        }

        File dir = build.getArtifactsDir();
        dir.mkdirs();

        listener.getLogger().println(Messages.ArtifactArchiver_ARCHIVING_ARTIFACTS());
        try {
            FilePath ws = build.getWorkspace();
            if (ws == null) { // #3330: slave down?
                return true;
            }

            String artifacts = build.getEnvironment(listener).expand(this.artifacts);
            if (ws.copyRecursiveTo(artifacts, excludes, new FilePath(dir), compressionType) == 0) {
                if (build.getResult().isBetterOrEqualTo(Result.UNSTABLE)) {
                    // If the build failed, don't complain that there was no matching artifact.
                    // The build probably didn't even get to the point where it produces artifacts.
                    listenerWarnOrError(listener, Messages.ArtifactArchiver_NoMatchFound(artifacts));
                    String msg = null;
                    try {
                        msg = ws.validateAntFileMask(artifacts);
                    } catch (Exception e) {
                        listenerWarnOrError(listener, e.getMessage());
                    }
                    if (msg != null) {
                        listenerWarnOrError(listener, msg);
                    }
                }
                if (!allowEmptyArchive) {
                    build.setResult(Result.FAILURE);
                }
                return true;
            }
        } catch (IOException e) {
            Util.displayIOException(e, listener);
            e.printStackTrace(listener.error(Messages.ArtifactArchiver_FailedToArchive(artifacts)));
            build.setResult(Result.FAILURE);
            return true;
        }

        return true;
    }

    @Override
    public boolean prebuild(AbstractBuild build, BuildListener listener) {
        if (latestOnly) {
            AbstractBuild b = build.getProject().getLastCompletedBuild();
            Result bestResultSoFar = Result.NOT_BUILT;
            while (b != null) {
                if (b.getResult().isBetterThan(bestResultSoFar)) {
                    bestResultSoFar = b.getResult();
                } else {
                    // remove old artifacts
                    File ad = b.getArtifactsDir();
                    if (ad.exists()) {
                        listener.getLogger().println(Messages.ArtifactArchiver_DeletingOld(b.getDisplayName()));
                        try {
                            Util.deleteRecursive(ad);
                        } catch (IOException e) {
                            e.printStackTrace(listener.error(e.getMessage()));
                        }
                    }
                }
                b = b.getPreviousBuild();
            }
        }
        return true;
    }

    public BuildStepMonitor getRequiredMonitorService() {
        return BuildStepMonitor.NONE;
    }
    /**
     * @deprecated as of 1.286 Some plugin depends on this, so this field is
     * left here and points to the last created instance. Use
     * {@link Hudson#getDescriptorByType(Class)} instead.
     */
    public static volatile DescriptorImpl DESCRIPTOR;

    @Extension
    public static class DescriptorImpl extends BuildStepDescriptor {

        public DescriptorImpl() {
            DESCRIPTOR = this; // backward compatibility
        }

        public String getDisplayName() {
            return Messages.ArtifactArchiver_DisplayName();
        }

        /**
         * Performs on-the-fly validation on the file mask wildcard.
         */
        public FormValidation doCheckArtifacts(@AncestorInPath AbstractProject project,
                @QueryParameter String artifacts,
                @QueryParameter boolean force) throws IOException {
            if (!force) {
                return FormValidation.ok();
            }
            return FilePath.validateFileMask(project.getSomeWorkspace(), artifacts);
        }

        @Override
        public ArtifactArchiver newInstance(StaplerRequest req, JSONObject formData) throws FormException {
            return req.bindJSON(ArtifactArchiver.class, formData);
        }

        public boolean isApplicable(Class jobType) {
            return true;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy