
org.hudsonci.plugins.team.cli.CopyTeamCommand Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of copy-team-cli Show documentation
Show all versions of copy-team-cli Show documentation
Adds copy-team CLI command.
The newest version!
/*
* Copyright (c) 2013 Hudson.
* 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:
* Hudson - initial API and implementation and/or initial documentation
*/
package org.hudsonci.plugins.team.cli;
import hudson.Extension;
import hudson.XmlFile;
import hudson.cli.CLICommand;
import hudson.model.Failure;
import hudson.model.Hudson;
import hudson.model.Job;
import hudson.model.TopLevelItem;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.eclipse.hudson.security.team.Team;
import org.eclipse.hudson.security.team.TeamManager;
import org.eclipse.hudson.security.team.TeamManager.TeamAlreadyExistsException;
import org.eclipse.hudson.security.team.TeamManager.TeamNotFoundException;
import org.eclipse.hudson.security.team.TeamNode;
import org.eclipse.hudson.security.team.TeamView;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.Option;
/**
* Copy a team and its jobs from the command line. User must be sys admin.
* EMAIL
* Email are replaced with contents of email argument.
*
*
*
*
* hudson-tasks-Mailer
*
*
* ${email}
* false
* false
*
* false
* true
*
*
*
* If email not specified, replace entire entry with:
*
*
* hudson-tasks-Mailer
*
* false
* false
*
*
*
* Cascading project names qualified with the old team name are requalified
* with the new team name. E.g., for copy-team Team1 TeamX
*
*
* Team1.JobBill2
*
* Team1.JobBill5
*
*
* Is converted to:
*
*
* TeamX.JobBill2
*
* TeamX.JobBill5
*
*
* If build trigger is specified, replace old team name with new team name, e.g.,
*
*
*
*
* hudson-tasks-BuildTrigger
*
*
* Team1.JobBill4, Team1.JobBill5
*
* Is converted to:
*
*
*
*
* hudson-tasks-BuildTrigger
*
*
* TeamX.JobBill4, TeamX.JobBill5
*
* Note that project names that are not qualified with the old team name
* are untouched.
* -nodes option
*
*
* Value
* Meaning
*
*
* move
* Move nodes owned by the from team to the to team.
* visible
* Make nodes owned by the from team visible to the to team
* ignore
* Do nothing about nodes owned by the from team (default)
*
*
*
* -views option
*
* Value
* Meaning
*
*
* move
* Move views owned by the from team to the to team.
* visible
* Make views owned by the from team visible to the to team
* ignore
* Do nothing about views owned by the from team (default)
*
*
* @author Bob Foster
*/
@Extension
public class CopyTeamCommand extends CLICommand {
@Override
public String getShortDescription() {
return "Copy a team and its jobs to a newly created team";
}
@Argument(metaVar = "FROM", usage = "Team name to copy (required)", required=true, index=0)
public String from;
@Argument(metaVar = "TO", usage = "Team name to create (required)", required=true, index=1)
public String to;
@Argument(metaVar = "EMAIL", usage = "Email recipients separated by commas (optional); if not specified, recipients will be removed", required=false, index=2)
public String email;
@Option(name = "-n", aliases="--nodes", usage = "MOVE (move nodes to new team), VISIBLE (make nodes visible to new team), IGNORE (ignore nodes - default), ")
public String nodes;
@Option(name = "-v", aliases="--views", usage = "MOVE (move views to new team), VISIBLE (make views visible to new team), IGNORE (ignore views - default), ")
public String views;
protected int run() throws Exception {
Hudson h = Hudson.getInstance();
if (!h.isTeamManagementEnabled()) {
stderr.println("Team management is not enabled");
return -1;
}
TeamManager teamManager = h.getTeamManager();
if (!teamManager.isCurrentUserSysAdmin()) {
stderr.println("User not authorized to create team");
return -1;
}
Team fromTeam;
try {
fromTeam = teamManager.findTeam(from);
} catch (TeamNotFoundException e) {
stderr.println("From team "+from+" not found");
return -1;
}
if (nodes != null && !("move".equalsIgnoreCase(nodes) || "visible".equalsIgnoreCase(nodes) || "ignore".equalsIgnoreCase(nodes))) {
stderr.println("nodes must be one of move, visible or ignore");
}
if (views != null && !("move".equalsIgnoreCase(views) || "visible".equalsIgnoreCase(views) || "ignore".equalsIgnoreCase(views))) {
stderr.println("views must be one of move, visible or ignore");
}
Team toTeam = null;
try {
toTeam = teamManager.createTeam(to);
} catch (IOException ex) {
stderr.println(ex.getMessage());
return -1;
} catch (TeamAlreadyExistsException ex) {
stderr.println("To team "+to+" already exists");
return -1;
}
Set jobNames = fromTeam.getJobNames();
for (String jobName : jobNames) {
String unqualifiedName = teamManager.getUnqualifiedJobName(jobName);
TopLevelItem item = h.getItem(jobName);
if (item instanceof Job) {
Job job = (Job) item;
XmlFile file = job.getConfigFile();
InputStream in;
try {
in = fixConfigFile(file, from, to, email);
} catch (Failure ex) {
stderr.println("Error reading config.xml for job "+jobName);
stderr.println(ex.getMessage());
return -1;
}
h.createProjectFromXML(unqualifiedName, to, in);
}
}
if (nodes != null) {
if ("move".equalsIgnoreCase(nodes)) {
for (String nodeName : fromTeam.getNodeNames()) {
teamManager.moveNode(fromTeam, toTeam, nodeName);
}
} else if ("visible".equalsIgnoreCase(nodes)) {
for (TeamNode teamNode : fromTeam.getNodes()) {
teamManager.addNodeVisibility(teamNode, toTeam.getName());
// Assume target team wants to be able to use the node.
teamManager.setNodeEnabled(teamNode.getId(), toTeam, true);
}
}
}
if (views != null) {
if ("move".equalsIgnoreCase(views)) {
for (String viewName : fromTeam.getViewNames()) {
teamManager.moveView(fromTeam, toTeam, viewName);
}
} else if ("visible".equalsIgnoreCase(nodes)) {
for (TeamView teamView : fromTeam.getViews()) {
teamManager.addViewVisibility(teamView, toTeam.getName());
}
}
}
return 0;
}
/*
* This method intentionally does not use XStream or any of the objects
* associated with various elements in the config.xml file, dealing
* instead with the "raw" XML, because the objects and their methods
* have too many unforseeable side effects.
*/
private InputStream fixConfigFile(XmlFile file, String oldTeam, String newTeam, String email) {
InputStream in = null;
String oldPrefix = oldTeam + TeamManager.TEAM_SEPARATOR;
String newPrefix = newTeam + TeamManager.TEAM_SEPARATOR;
try {
in = new FileInputStream(file.getFile());
SAXReader reader = new SAXReader();
Document doc = reader.read(in);
Element root = doc.getRootElement();
// The root element name varies by project type, e.g.,
// project, matrix-project, etc. Code assumes that
// following elements are common to all project types.
// Cascading
Element cascadingParent = root.element("cascadingProjectName");
if (cascadingParent != null) {
fixTeamName(cascadingParent, oldPrefix, newPrefix);
}
Element cascadingChildren = root.element("cascadingChildrenNames");
if (cascadingChildren != null) {
for (Object elem : cascadingChildren.elements("string")) {
fixTeamName((Element) elem, oldPrefix, newPrefix);
}
}
// Properties (open-ended problem)
Element properties = root.element("project-properties");
if (properties == null) {
throw new Failure("Project has no ");
}
List removeEntries = new ArrayList();
for (Object ent : properties.elements("entry")) {
Element entry = (Element) ent;
Element extProp = entry.element("external-property");
Element origValue = extProp != null ? extProp.element("originalValue") : null;
Element str = entry.element("string");
if (str != null) {
String propName = str.getTextTrim();
if ("hudson-tasks-Mailer".equals(propName)) {
// email
if (extProp != null) {
if (email == null) {
// A recent fix removes entries that are not specified
removeEntries.add(entry);
/*
// Replace entire entry
entry.remove(extProp);
extProp = entry.addElement("external-property");
Element propOver = extProp.addElement("propertyOverridden");
propOver.setText("false");
Element modified = extProp.addElement("modified");
modified.setText("false");
*/
} else if (origValue != null) {
fixEmailProperty(origValue, email);
}
}
} else if ("hudson-tasks-BuildTrigger".equals(propName)) {
// trigger
if (origValue != null) {
fixTriggerProperty(origValue, oldPrefix, newPrefix);
}
} else if ("builders".equals(propName)) {
// can be many of these
Element describableList = entry.element("describable-list-property");
origValue = describableList != null ? describableList.element("originalValue") : null;
// copyartifact
List artifacts = origValue != null ? origValue.elements("hudson.plugins.copyartifact.CopyArtifact") : null;
if (artifacts != null) {
for (Object obj : artifacts) {
Element copyArtifact = (Element) obj;
Element projectName = copyArtifact.element("projectName");
fixTeamName(projectName, oldPrefix, newPrefix);
}
}
// multijob
List multiJob = origValue != null ? origValue.elements("com.tikal.jenkins.plugins.multijob.MultiJobBuilder") : null;
for (Object obj : multiJob) {
Element builder = (Element) obj;
List jobs = builder.elements("phaseJobs");
if (jobs != null) {
for (Object j : jobs) {
Element job = (Element) j;
List configs = job.elements("com.tikal.jenkins.plugins.multijob.PhaseJobsConfig");
if (configs != null) {
for (Object c : configs) {
Element config = (Element) c;
Element jobName = config.element("jobName");
fixTeamName(jobName, oldPrefix, newPrefix);
}
}
}
}
}
} else if ("hudson-plugins-redmine-RedmineProjectProperty".equals(propName)) {
Element baseProp = entry.element("base-property");
Element projectName = baseProp != null ? baseProp.element("projectName") : null;
if (projectName != null) {
fixTeamName(projectName, oldPrefix, newPrefix);
}
}
}
}
for (Element entry : removeEntries) {
properties.remove(entry);
}
StringWriter writer = new StringWriter();
doc.write(writer);
return new ByteArrayInputStream(writer.toString().getBytes("UTF-8"));
} catch (FileNotFoundException ex) {
throw new Failure("File not found");
} catch (DocumentException ex) {
throw new Failure("Unable to parse document");
} catch (IOException ex) {
throw new Failure("Document write failed");
} finally {
try {
in.close();
} catch (IOException ex) {
;
}
}
}
private void fixTeamName(Element element, String oldPrefix, String newPrefix) {
String jobName = element.getTextTrim();
if (jobName.startsWith(oldPrefix)) {
element.setText(newPrefix+jobName.substring(oldPrefix.length()));
}
}
private void fixTriggerProperty(Element origValue, String oldPrefix, String newPrefix) {
Element cp = origValue.element("childProjects");
String childProjects = cp.getTextTrim();
List children = new ArrayList();
StringTokenizer st = new StringTokenizer(childProjects, ", ");
boolean changed = false;
while (st.hasMoreTokens()) {
String child = st.nextToken();
if (child.startsWith(oldPrefix)) {
changed = true;
child = newPrefix + child.substring(oldPrefix.length());
}
children.add(child);
}
if (changed) {
childProjects = StringUtils.join(children, ", ");
cp.setText(childProjects);
}
}
private void fixEmailProperty(Element origValue, String email) {
Element recipients = origValue.element("recipients");
if (recipients != null) {
recipients.setText(email);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy