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

au.com.centrumsystems.hudson.plugin.buildpipeline.PipelineBuild Maven / Gradle / Ivy

Go to download

This plugin provides build pipeline functionality to Hudson and Jenkins. This allows a chain of jobs to be visualised in a new view. Manual jobs in the pipeline can be triggered by a user with the appropriate permissions manually confirming.

There is a newer version: 1.2.3
Show newest version
/*
 * The MIT License
 *
 * Copyright (c) 2011, Centrumsystems Pty Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 */
package au.com.centrumsystems.hudson.plugin.buildpipeline;

import hudson.model.Item;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.FreeStyleProject;
import hudson.model.Hudson;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import net.sf.json.JSONArray;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;

import org.apache.commons.io.IOUtils;

import au.com.centrumsystems.hudson.plugin.util.BuildUtil;
import au.com.centrumsystems.hudson.plugin.util.HudsonResult;
import au.com.centrumsystems.hudson.plugin.util.ProjectUtil;

/**
 * @author KevinV
 * 
 */
public class PipelineBuild {
    /** Represents the current build */
    private AbstractBuild currentBuild;
    /** Represents the current project */
    private AbstractProject project;
    /** Represents the upstream build */
    private AbstractBuild upstreamBuild;
    /**
     * Contains the upstreamBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT -
     * PENDING - MANUAL
     */
    private String upstreamBuildResult;
    /**
     * Contains the currentBuild result. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT - ABORT -
     * PENDING - MANUAL
     */
    private String currentBuildResult;

    /** A Logger object is used to log messages */
    private static final Logger LOGGER = Logger.getLogger(PipelineBuild.class.getName());

    /**
     * Default constructor
     */
    public PipelineBuild() {
    }

    /**
     * Creates a new PipelineBuild with currentBuild, project and upstreamBuild set.
     * 
     * @param build
     *            - current build
     * @param project
     *            - current project
     * @param previousBuild
     *            - upstream build
     */
    public PipelineBuild(final AbstractBuild build, final AbstractProject project, final AbstractBuild previousBuild) {
        this.currentBuild = build;
        this.project = project;
        this.upstreamBuild = previousBuild;
        this.currentBuildResult = "";
        this.upstreamBuildResult = "";
    }

    /**
     * @param project
     *            project
     */
    public PipelineBuild(final FreeStyleProject project) {
        this(null, project, null);
    }

    public AbstractBuild getCurrentBuild() {
        return currentBuild;
    }

    public void setCurrentBuild(final AbstractBuild currentBuild) {
        this.currentBuild = currentBuild;
    }

    public AbstractBuild getUpstreamBuild() {
        return upstreamBuild;
    }

    public void setUpstreamBuild(final AbstractBuild upstreamBuild) {
        this.upstreamBuild = upstreamBuild;
    }

    public void setProject(final AbstractProject currentProject) {
        this.project = currentProject;
    }

    /**
     * Returns the project name. If the current project is null the project name is determined using the current build.
     * 
     * @return - Project name
     */
    public AbstractProject getProject() {
        final AbstractProject currentProject;
        if (this.project == null && this.currentBuild != null) {
            currentProject = this.currentBuild.getProject();
        } else {
            currentProject = this.project;
        }
        return currentProject;
    }

    /**
     * Returns the current build number.
     * 
     * @return - Current build number or empty String is the current build is null.
     */
    private String getCurrentBuildNumber() {
        if (this.currentBuild != null) {
            return Integer.toString(currentBuild.getNumber());
        } else {
            return "";
        }
    }

    /**
     * Constructs a List of downstream PipelineBuild objects that make up the current pipeline.
     * 
     * @return - List of downstream PipelineBuild objects that make up the current pipeline.
     */
    public List getDownstreamPipeline() {
        final List pbList = new ArrayList();

        final AbstractProject currentProject;
        currentProject = getProject();

        final List> downstreamProjects = ProjectUtil.getDownstreamProjects(currentProject);
        for (final AbstractProject proj : downstreamProjects) {
            AbstractBuild returnedBuild = null;
            if (this.currentBuild != null) {
                returnedBuild = BuildUtil.getDownstreamBuild(proj, currentBuild);
            }
            final PipelineBuild newPB = new PipelineBuild(returnedBuild, proj, this.currentBuild);
            pbList.add(newPB);
        }

        return pbList;
    }

    /**
     * Build a URL of the currentBuild
     * 
     * @return URL of the currentBuild
     */
    public String getBuildResultURL() {
        return currentBuild != null ? currentBuild.getUrl() : "";
    }

    /**
     * Builds a URL of the current project
     * 
     * @return URL - of the project
     */
    public String getProjectURL() {
        return project != null ? project.getUrl() : "";
    }

    /**
     * Determines the result of the current build.
     * 
     * @return - String representing the build result
     * @see PipelineBuild#getBuildResult(AbstractBuild)
     */
    public String getCurrentBuildResult() {
        if (this.currentBuildResult.isEmpty()) {
            this.currentBuildResult = getBuildResult(this.currentBuild);
        }
        return this.currentBuildResult;
    }

    /**
     * Determines the result of the upstream build.
     * 
     * @return - String representing the build result
     * @see PipelineBuild#getBuildResult(AbstractBuild)
     */
    public String getUpstreamBuildResult() {
        if (this.upstreamBuildResult.isEmpty()) {
            this.upstreamBuildResult = getBuildResult(this.upstreamBuild);
        }
        return this.upstreamBuildResult;
    }

    /**
     * Determines the result for a particular build. Can be one of the following: - BUILDING - SUCCESS - FAILURE - UNSTABLE - NOT_BUILT -
     * ABORT - PENDING - MANUAL
     * 
     * @param build
     *            - The build for which a result is requested.
     * @return - String representing the build result
     */
    private String getBuildResult(final AbstractBuild build) {
        String buildResult;
        // If AbstractBuild exists determine its current status
        if (build != null) {
            if (build.isBuilding()) {
                buildResult = HudsonResult.BUILDING.toString();
            } else {
                buildResult = HudsonResult.values()[build.getResult().ordinal].toString();
            }
        } else {
            // Otherwise determine its pending status
            buildResult = getPendingStatus();
        }

        return buildResult;
    }

    /**
     * Determines the pending currentBuild status of a currentBuild in the pipeline that has not been completed. (i.e. the currentBuild is
     * null)
     * 
     * @return - PENDING: Current currentBuild is pending the execution of upstream builds. MANUAL: Current currentBuild requires a manual
     *         trigger
     */
    private String getPendingStatus() {
        String pendingStatus = HudsonResult.PENDING.toString();
        final PipelineBuild upstreamPB = getUpstreamPipelineBuild();

        if (upstreamPB != null) {
            if (this.getUpstreamBuild() != null) {
                if (getUpstreamBuildResult().equals(HudsonResult.SUCCESS.toString())) {
                    if (ProjectUtil.isManualTrigger(this.upstreamBuild.getProject(), this.project)) {
                        pendingStatus = HudsonResult.MANUAL.toString();
                    }
                }
            }
        }
        return pendingStatus;
    }

    /**
     * Returns the upstream PipelineBuild object from the current PipelineBuild object.
     * 
     * @return - Upstream PipelineBuild object from the current PipelineBuild object
     */
    public PipelineBuild getUpstreamPipelineBuild() {
        final List upstreamProjects = getProject().getUpstreamProjects();
        final AbstractProject previousProject;
        final PipelineBuild previousPB = new PipelineBuild();
        if (upstreamProjects.size() > 0) {
            previousProject = upstreamProjects.get(0);
            previousPB.setCurrentBuild(this.getUpstreamBuild());
            previousPB.setProject(previousProject);
        }
        return previousPB;
    }

    /**
     * Returns the current build duration.
     * 
     * @return - Current build duration or an empty String if the current build is null.
     */
    public String getBuildDuration() {
        if (this.currentBuild != null) {
            return this.currentBuild.getDurationString();
        } else {
            return "";
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "Project: " + getProject().getName() + " : Build: " + getCurrentBuildNumber();
    }

    /**
     * Returns the current build description.
     * 
     * @return - Current build description or the project name if the current build is null.
     */
    public String getBuildDescription() {
        if (this.currentBuild != null) {
            return this.currentBuild.toString();
        } else {
            return "Pending build of project: " + this.getProject().getName();
        }
    }

    /**
     * Get the SCM revision no of a particular currentBuild
     * 
     * @return The revision number of the currentBuild or "No Revision"
     */
    public String getScmRevision() {
        String revNo = "No Revision";
        if (currentBuild != null) {
            if ("hudson.scm.SubversionSCM".equals(project.getScm().getType())) {
                final String svnNo = svnNo();
                if (svnNo != null) {
                    revNo = svnNo;
                }
            } else if ("hudson.plugins.git.GitSCM".equals(project.getScm().getType())) {
                final String gitNo = gitNo();
                if (gitNo != null) {
                    revNo = gitNo;
                }
            } else if ("hudson.plugins.mercurial.MercurialSCM".equals(project.getScm().getType())) {
                final String hgNo = hgNo();
                if (hgNo != null) {
                    revNo = hgNo;
                }
            }
        }
        return revNo;
    }

    /**
     * Returns the last hg rev
     * 
     * @return revision number of the current build
     */
    private String hgNo() {
        InputStream inputStream = null;
        try {
            String revNo = null;
            final URL url = new URL(Hudson.getInstance().getRootUrl() + currentBuild.getUrl() + "api/json?tree=changeSet[items[node,rev]]");
            inputStream = url.openStream();
            final JSONObject json = (JSONObject) JSONSerializer.toJSON(IOUtils.toString(inputStream));
            if (json != null) {
                try {

                    final JSONArray items = json.getJSONObject("changeSet").getJSONArray("items");
                    if (items != null && items.size() >= 1) {
                        revNo = "Hg: " + items.getJSONObject(0).getString("rev") + ":" + items.getJSONObject(0).getString("node");
                    }
                } catch (final JSONException e) {
                    // This is not great, but swallow jquery parsing exceptions assuming that some element did not exist, will have a better
                    // solution one I find a JSON query lib or method
                    LOGGER.finest("did not find svn revision in " + json);
                }
            }
            return revNo;
        } catch (final MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (final IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * Get the Git revision no of a particular currentBuild
     * 
     * @return The revision number of the currentBuild
     */
    private String gitNo() {
        InputStream inputStream = null;
        try {
            String revNo = null;
            final URL url = new URL(Hudson.getInstance().getRootUrl() + currentBuild.getUrl()
                + "api/json?tree=actions[lastBuiltRevision[SHA1]]");
            inputStream = url.openStream();
            final JSONObject json = (JSONObject) JSONSerializer.toJSON(IOUtils.toString(inputStream));
            if (json != null) {
                try {
                    final JSONArray actions = json.getJSONArray("actions");
                    for (final Object object : actions) {
                        if (object instanceof JSONObject) {
                            final JSONObject jsonObject = (JSONObject) object;
                            if (jsonObject.containsKey("lastBuiltRevision")) {
                                revNo = "Git: " + jsonObject.getJSONObject("lastBuiltRevision").getString("SHA1");
                            }
                        }
                    }

                } catch (final JSONException e) {
                    // This is not great, but swallow jquery parsing exceptions assuming that some element did not exist, will have a better
                    // solution one I find a JSON query lib or method
                    LOGGER.finest("did not find git revision in " + json);
                }
            }
            return revNo;
        } catch (final MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (final IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * Using the hudson api get the subversion info (yes, it is going the long way around)
     * 
     * @return subversion revision
     */
    private String svnNo() {
        InputStream inputStream = null;
        try {
            String revNo = null;
            final URL url = new URL(Hudson.getInstance().getRootUrl() + currentBuild.getUrl()
                + "api/json?tree=changeSet[revisions[revision]]");
            inputStream = url.openStream();
            final JSONObject json = (JSONObject) JSONSerializer.toJSON(IOUtils.toString(inputStream));
            if (json != null) {
                try {
                    revNo = "Svn: " + json.getJSONObject("changeSet").getJSONArray("revisions").getJSONObject(0).getString("revision");
                } catch (final JSONException e) {
                    // This is not great, but swallow jquery parsing exceptions assuming that some element did not exist, will have a better
                    // solution one I find a JSON query lib or method
                    LOGGER.finest("did not find svn revision in " + json);
                }
            }
            return revNo;
        } catch (final MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (final IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (final IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    /**
     * Checks whether the user has Build permission for the current project.
     * 
     * @return - true: Has Build permission; false: Does not have Build permission
     * @see hudson.model.Item
     */
    public boolean hasBuildPermission() {
        boolean buildPermission = false;
        // If no security is enabled then allow builds
        if (!Hudson.getInstance().isUseSecurity()) {
            buildPermission = true;
        } else if (this.project != null) {
            // If security is enabled check if BUILD is enabled
            buildPermission = this.project.hasPermission(Item.BUILD);
        }
        return buildPermission;
    }

    public boolean isManual() {
        return getCurrentBuildResult().equals(HudsonResult.MANUAL.toString());
    }

    /**
     * Start time of build
     * 
     * @return start time
     */
    public Date getStartTime() {
        return currentBuild != null ? currentBuild.getTime() : null;

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy