/* =========================================================================== * Copyright (c) 2007 Serena Software. All rights reserved. * * Use of the Sample Code provided by Serena is governed by the following * terms and conditions. By using the Sample Code, you agree to be bound by * the terms contained herein. If you do not agree to the terms herein, do * not install, copy, or use the Sample Code. * * 1. GRANT OF LICENSE. Subject to the terms and conditions herein, you * shall have the nonexclusive, nontransferable right to use the Sample Code * for the sole purpose of developing applications for use solely with the * Serena software product(s) that you have licensed separately from Serena. * Such applications shall be for your internal use only. You further agree * that you will not: (a) sell, market, or distribute any copies of the * Sample Code or any derivatives or components thereof; (b) use the Sample * Code or any derivatives thereof for any commercial purpose; or (c) assign * or transfer rights to the Sample Code or any derivatives thereof. * * 2. DISCLAIMER OF WARRANTIES. TO THE MAXIMUM EXTENT PERMITTED BY * APPLICABLE LAW, SERENA PROVIDES THE SAMPLE CODE AS IS AND WITH ALL * FAULTS, AND HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EITHER * EXPRESSED, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY * IMPLIED WARRANTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A * PARTICULAR PURPOSE, OF LACK OF VIRUSES, OF RESULTS, AND OF LACK OF * NEGLIGENCE OR LACK OF WORKMANLIKE EFFORT, CONDITION OF TITLE, QUIET * ENJOYMENT, OR NON-INFRINGEMENT. THE ENTIRE RISK AS TO THE QUALITY OF * OR ARISING OUT OF USE OR PERFORMANCE OF THE SAMPLE CODE, IF ANY, * REMAINS WITH YOU. * * 3. EXCLUSION OF DAMAGES. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE * LAW, YOU AGREE THAT IN CONSIDERATION FOR RECEIVING THE SAMPLE CODE AT NO * CHARGE TO YOU, SERENA SHALL NOT BE LIABLE FOR ANY DAMAGES WHATSOEVER, * INCLUDING BUT NOT LIMITED TO DIRECT, SPECIAL, INCIDENTAL, INDIRECT, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, DAMAGES FOR LOSS OF * PROFITS OR CONFIDENTIAL OR OTHER INFORMATION, FOR BUSINESS INTERRUPTION, * FOR PERSONAL INJURY, FOR LOSS OF PRIVACY, FOR NEGLIGENCE, AND FOR ANY * OTHER LOSS WHATSOEVER) ARISING OUT OF OR IN ANY WAY RELATED TO THE USE * OF OR INABILITY TO USE THE SAMPLE CODE, EVEN IN THE EVENT OF THE FAULT, * TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY, OR BREACH OF CONTRACT, * EVEN IF SERENA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE * FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE * MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW. NOTWITHSTANDING THE ABOVE, * IN NO EVENT SHALL SERENA'S LIABILITY UNDER THIS AGREEMENT OR WITH RESPECT * TO YOUR USE OF THE SAMPLE CODE AND DERIVATIVES THEREOF EXCEED US$10.00. * * 4. INDEMNIFICATION. You hereby agree to defend, indemnify and hold * harmless Serena from and against any and all liability, loss or claim * arising from this agreement or from (i) your license of, use of or * reliance upon the Sample Code or any related documentation or materials, * or (ii) your development, use or reliance upon any application or * derivative work created from the Sample Code. * * 5. TERMINATION OF THE LICENSE. This agreement and the underlying * license granted hereby shall terminate if and when your license to the * applicable Serena software product terminates or if you breach any terms * and conditions of this agreement. * * 6. CONFIDENTIALITY. The Sample Code and all information relating to the * Sample Code (collectively "Confidential Information") are the * confidential information of Serena. You agree to maintain the * Confidential Information in strict confidence for Serena. You agree not * to disclose or duplicate, nor allow to be disclosed or duplicated, any * Confidential Information, in whole or in part, except as permitted in * this Agreement. You shall take all reasonable steps necessary to ensure * that the Confidential Information is not made available or disclosed by * you or by your employees to any other person, firm, or corporation. You * agree that all authorized persons having access to the Confidential * Information shall observe and perform under this nondisclosure covenant. * You agree to immediately notify Serena of any unauthorized access to or * possession of the Confidential Information. * * 7. AFFILIATES. Serena as used herein shall refer to Serena Software, * Inc. and its affiliates. An entity shall be considered to be an * affiliate of Serena if it is an entity that controls, is controlled by, * or is under common control with Serena. * * 8. GENERAL. Title and full ownership rights to the Sample Code, * including any derivative works shall remain with Serena. If a court of * competent jurisdiction holds any provision of this agreement illegal or * otherwise unenforceable, that provision shall be severed and the * remainder of the agreement shall remain in full force and effect. * =========================================================================== */ /** ** @brief This experimental plugin extends Hudson support for Dimensions SCM repositories ** ** @author Tim Payne ** **/ package hudson.plugins.dimensionsscm; // Dimensions imports import hudson.plugins.dimensionsscm.DimensionsAPI; import hudson.plugins.dimensionsscm.DimensionsSCM; import hudson.plugins.dimensionsscm.Logger; import hudson.plugins.dimensionsscm.CheckInAPITask; import hudson.plugins.dimensionsscm.CheckInCmdTask; import hudson.plugins.dimensionsscm.GetHostDetailsTask; import com.serena.dmclient.api.DimensionsResult; // Hudson imports import hudson.Extension; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Descriptor.FormException; import hudson.model.Descriptor; import hudson.model.Result; import hudson.model.TaskListener; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Notifier; import hudson.tasks.Publisher; import hudson.util.FormValidation; import hudson.Util; import hudson.FilePath; import hudson.util.VariableResolver; import hudson.tasks.BuildStepMonitor; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; // General imports import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeSet; import java.util.Vector; import java.util.Calendar; import java.net.InetSocketAddress; import java.net.InetAddress; import java.net.UnknownHostException; import javax.servlet.ServletException; public class ArtifactUploader extends Notifier implements Serializable { public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } private String[] patterns = new String[0]; private boolean forceCheckIn = false; private boolean forceTip = false; private String owningPart = null; /** * Default constructor. */ @DataBoundConstructor public ArtifactUploader(String[] patterns, boolean fTip, boolean fMerge, String part) { // Check the folders specified have data specified if (patterns != null) { Logger.Debug("patterns are populated"); Vector<String> x = new Vector<String>(); for(int t=0;t<patterns.length;t++) { if (StringUtils.isNotEmpty(patterns[t])) x.add(patterns[t]); } this.patterns = (String[])x.toArray(new String[1]); } else { this.patterns[0] = ".*"; } this.forceCheckIn = fTip; this.forceTip = fMerge; this.owningPart = part; } /* * Gets the patterns to upload * @return patterns */ public String[] getPatterns() { return this.patterns; } /* * Gets the owning part * @return patterns */ public String getOwningPart() { return this.owningPart; } /* * Gets force tip * @return forceTip */ public boolean isForceCheckIn() { return this.forceCheckIn; } /* * Gets force merge * @return forceMerge */ public boolean isForceTip() { return this.forceTip; } @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { long key=-1; Logger.Debug("Invoking perform callout " + this.getClass().getName()); FilePath workspace = build.getWorkspace(); boolean bRet = false; boolean isStream = false; try { if (!(build.getProject().getScm() instanceof DimensionsSCM)) { listener.fatalError("[DIMENSIONS] This plugin only works with the Dimensions SCM engine."); build.setResult(Result.FAILURE); throw new IOException("[DIMENSIONS] This plugin only works with a Dimensions SCM engine"); } if (build.getResult() == Result.SUCCESS) { DimensionsSCM scm = (DimensionsSCM)build.getProject().getScm(); DimensionsAPI dmSCM = new DimensionsAPI(); Logger.Debug("Calculating version of Dimensions..."); int version = 2009; key = dmSCM.login(scm.getJobUserName(), scm.getJobPasswd(), scm.getJobDatabase(), scm.getJobServer()); if (key>0) { // Get the server version Logger.Debug("Login worked."); version = dmSCM.getDmVersion(); if (version == 0) { version = 2009; } if (version != 10) { isStream = dmSCM.isStream(key,scm.getProject()); } dmSCM.logout(key); } // Get the details of the master InetAddress netAddr = InetAddress.getLocalHost(); byte[] ipAddr = netAddr.getAddress(); String hostname = netAddr.getHostName(); String projectName = build.getProject().getName(); int buildNo = build.getNumber(); Logger.Debug("Checking if master or slave..."); boolean master = false; GetHostDetailsTask buildHost = new GetHostDetailsTask(hostname); master = workspace.act(buildHost); if (master) { // Running on master... listener.getLogger().println("[DIMENSIONS] Running checkin on master..."); listener.getLogger().flush(); // Using Java API because this allows the plugin to work on platforms // where Dimensions has not been ported, e.g. MAC OS, which is what // I use CheckInAPITask task = new CheckInAPITask(build,scm, buildNo, projectName,version, this,workspace,listener); bRet = workspace.act(task); } else { // Running on slave... Have to use the command line as Java API will not // work on remote hosts. Cannot serialise it... { // VariableResolver does not appear to be serialisable either, so... VariableResolver<String> myResolver = build.getBuildVariableResolver(); String requests = myResolver.resolve("DM_TARGET_REQUEST"); listener.getLogger().println("[DIMENSIONS] Running checkin on slave..."); listener.getLogger().flush(); CheckInCmdTask task = new CheckInCmdTask(scm.getJobUserName(), scm.getJobPasswd(), scm.getJobDatabase(), scm.getJobServer(), scm.getProject(), requests,isForceCheckIn(),isForceTip(), getPatterns(),version,isStream, buildNo, projectName, getOwningPart(), workspace,listener); bRet = workspace.act(task); } } } else { bRet = true; } if (!bRet) { build.setResult(Result.FAILURE); } } catch(Exception e) { listener.fatalError("Unable to load build artifacts into Dimensions - " + e.getMessage()); build.setResult(Result.FAILURE); return false; } finally { } return bRet; } /** * The ArtifactUploader Descriptor class. */ @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> { /* * Loads the descriptor */ public DescriptorImpl() { super(ArtifactUploader.class); load(); Logger.Debug("Loading " + this.getClass().getName()); } public String getDisplayName() { return "Load any build artifacts into the Dimensions repository"; } /* * This builder can be used with all project types */ @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return true; } /* * Save the descriptor configuration */ @Override public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { req.bindParameters(this,"ArtifactUploader"); return super.configure(req, formData); } @Override public Notifier newInstance(StaplerRequest req, JSONObject formData) throws FormException { // Get variables and then construct a new object String[] patterns = req.getParameterValues("artifactuploader.patterns"); Boolean fTip = Boolean.valueOf("on".equalsIgnoreCase(req.getParameter("artifactuploader.forceCheckIn"))); Boolean fMerge = Boolean.valueOf("on".equalsIgnoreCase(req.getParameter("artifactuploader.forceTip"))); String oPart = req.getParameter("artifactuploader.owningPart"); if (oPart != null) oPart = Util.fixNull(req.getParameter("artifactuploader.owningPart").trim()); ArtifactUploader artifactor = new ArtifactUploader(patterns,fTip,fMerge,oPart); return artifactor; } } }