/******************************************************************************** * CruiseControl, a Continuous Integration Toolkit * Copyright (c) 2006, ThoughtWorks, Inc. * 200 E. Randolph, 25th Floor * Chicago, IL 60601 USA * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * + Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************************/ package net.sourceforge.cruisecontrol.publishers; import net.sourceforge.cruisecontrol.CruiseControlException; import net.sourceforge.cruisecontrol.Publisher; import net.sourceforge.cruisecontrol.gendoc.annotations.Description; import net.sourceforge.cruisecontrol.gendoc.annotations.ExamplesFile; import net.sourceforge.cruisecontrol.gendoc.annotations.Optional; import net.sourceforge.cruisecontrol.gendoc.annotations.Required; import net.sourceforge.cruisecontrol.util.ManagedCommandline; import net.sourceforge.cruisecontrol.util.ValidationHelper; import net.sourceforge.cruisecontrol.util.XMLLogHelper; import org.apache.log4j.Logger; import org.jdom.CDATA; import org.jdom.Element; import java.util.ArrayList; import java.util.List; /** * @author <a href="mailto:kevin.lee@buildmeister.com">Kevin Lee</a> */ @Description("<p>Creates a ClearCase UCM baseline for the specified view's integration " + "stream. Uses the value of the CruiseControl generated <code>${label}</code> " + "property as well as the value of the <code>baselineprefix</code> attribute " + "(if specified) to name the baseline. A baseline is only created if UCM " + "modifications are recorded in the build log. By default an incremental " + "baseline is created although a full baseline can be created too (incremental " + "baselines are recommended for Continuous Integration).</p>") @ExamplesFile public class ClearCaseBaselinePublisher implements Publisher { private boolean full = false; private String baselineprefix; private String viewtag; private String component; private static final Logger LOG = Logger.getLogger(ClearCaseBaselinePublisher.class); /** * Set the baselineprefix flag * * @param baselineprefix the status to set the flag to */ @Description("A prefix which should be applied together with the CruiseControl <code>" + "${label}</code> property, for example specifying \"EXAMPLE_\" with a " + "CruiseControl generated label of \"1_INT\" would create a baseline called " + "\"EXAMPLE_1_INT\".") @Optional public void setBaselineprefix(String baselineprefix) { this.baselineprefix = baselineprefix; } // setBaselineprefix /** * Get baselineprefix flag status * * @return String containing status of baselineprefix flag */ public String getBaselineprefix() { return this.baselineprefix; } // getBaselineprefix /** * Set the viewtag status flag * * @param viewtag the status to set the flag to */ @Description("The view tag of the UCM view that you wish to create the baseline in. " + "The baseline is created in the stream that the view is attached to.") @Required public void setViewtag(String viewtag) { this.viewtag = viewtag; } // setViewtag /** * Get viewtag flag status * * @return String containing status of viewtag flag */ public String getViewtag() { return this.viewtag; } // getViewtag /** * Set the full flag * * @param full the status to set the flag to */ @Description("Boolean value indicating whether a \"Fully Labelled\" or \"Incremental\" " + "baseline should be created. Default is false, i.e. \"Incremental\".") @Optional public void setFull(boolean full) { this.full = full; } // setFull /** * Get full flag status * * @return boolean containing status of full flag */ public boolean getFull() { return this.full; } // getFull /** * Set the component to generate the baseline for * * @param comp the name of the component */ @Description("The component to restrict the baseline creation to. By default the " + "baseline is applied to all of the stream's changed components.") @Optional public void setComponent(String comp) { this.component = comp; } // setComponent /** * Get the component flag status * * @return the component the baseline is to be applied to */ public String getComponent() { return this.component; } // getComponent /* * check whether the appropriate attributes have been set */ public void validate() throws CruiseControlException { ValidationHelper.assertIsSet(viewtag, "viewtag", this.getClass()); } // validate /** * extract the list of UCM modifications from the CruiseControl log * (assumes the UCM sourcecontrol was used) * * @param log The Cruise Control log (as a JDOM element). * @return a <code>List</code> of UCM activities */ public List getActivities(final Element log) { final List<String> activityList = new ArrayList<String>(); // get the modification list from the log if (log != null) { final Element modifications = log.getChild("modifications"); if (modifications != null) { // from this list, extract all UCM activities for (final Object o : modifications.getChildren("modification")) { final Element modification = (Element) o; final String type = modification.getAttributeValue("type"); if (type != null && type.equals("activity")) { final String activity = modification.getChild("revision").getText(); if (activity != null) { activityList.add(activity.trim()); } } } } } return activityList; } // getActivities /** * determines if the publish should take place * * @param log the CruiseControl log (as a JDOM element). * @return true if the build was successful and new activities were found */ public boolean shouldPublish(Element log) { // do not publish if no activities were found List newActivities = getActivities(log); if (newActivities.size() < 1) { LOG.info("No UCM activities in build. Skipping publisher."); return false; } return true; } // shouldPublish /* (non-Javadoc) * @see net.sourceforge.cruisecontrol.Publisher#publish(org.jdom.Element) */ public void publish(final Element log) throws CruiseControlException { final XMLLogHelper helper = new XMLLogHelper(log); // only publish if the build includes UCM activities if (!shouldPublish(log)) { return; } // should the baselinename include the prefix? final String baselinename; if (getBaselineprefix() != null) { baselinename = getBaselineprefix() + helper.getLabel(); } else { baselinename = helper.getLabel(); } // create the "cleartool mkbl" command line final ManagedCommandline cmd = new ManagedCommandline(); cmd.setExecutable("cleartool"); cmd.createArgument("mkbl"); cmd.createArguments("-view", getViewtag()); if (getFull()) { cmd.createArgument("-full"); } if (getComponent() != null) { cmd.createArguments("-component", getComponent()); } cmd.createArgument(baselinename); // execute it try { cmd.execute(); cmd.assertExitCode(0); } catch (Exception e) { final StringBuilder error = new StringBuilder("Failed to create baseline: "); error.append(baselinename); throw new CruiseControlException(error.toString(), e); } // log success final StringBuilder message = new StringBuilder("Created baseline: "); message.append(baselinename); LOG.info(message.toString()); // TODO: update ClearQuest records/activities with build number } // publish /** * for testing * @param args command line args */ public static void main(String[] args) { // create a fake cruisecontrol log Element logElement = new Element("cruisecontrol"); // add a single modification Element mods = new Element("modifications"); logElement.addContent(mods); Element mod = new Element("modification"); mod.setAttribute("type", "activity"); mods.addContent(mod); Element rev = new Element("revision"); rev.addContent(new CDATA("Some activitiy")); mod.addContent(rev); // and a build label Element info = new Element("info"); logElement.addContent(info); Element prop = new Element("property"); prop.setAttribute("name", "label"); prop.setAttribute("value", "1_TST"); info.addContent(prop); ClearCaseBaselinePublisher baseline = new ClearCaseBaselinePublisher(); baseline.setViewtag("RatlBankModel_int"); //baseline.setFull(true); //baseline.setComponent("RATLBANKMODEL_REL@\\RatlBankProjects"); //baseline.setbaselineprefix("testbl"); try { baseline.publish(logElement); } catch (CruiseControlException ex) { ex.printStackTrace(); } } } // ClearCaseBaselinePublisher