Skip to content

Commit

Permalink
Restore support for per-job repository URL, username + password.
Browse files Browse the repository at this point in the history
Modify repo, username + password defaulting so it falls back at runtime, not at job-creation time.
Changed workspaceName templating to DRY up code and make it work with any Jenkins variables, not just NODE_NAME and JOB_NAME.
Ensured there's help for all these config values.
Unit tests for code changes.
  • Loading branch information
RoystonS committed Mar 27, 2013
1 parent 5ad407d commit 6b71ffa
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 98 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -7,3 +7,4 @@ work
*.iml
*.iws
*.ipr
bin/
117 changes: 62 additions & 55 deletions src/main/java/com/deluan/jenkins/plugins/rtc/JazzSCM.java
Expand Up @@ -4,8 +4,6 @@
import com.deluan.jenkins.plugins.rtc.changelog.JazzChangeLogWriter;
import com.deluan.jenkins.plugins.rtc.changelog.JazzChangeSet;
import com.deluan.jenkins.plugins.rtc.commands.UpdateWorkItemsCommand;
import com.deluan.jenkins.plugins.rtc.commands.*;
import com.deluan.jenkins.plugins.rtc.commands.Command;

import hudson.EnvVars;
import hudson.Extension;
Expand All @@ -24,15 +22,11 @@
import org.kohsuke.stapler.StaplerRequest;

import java.io.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Date;
import java.util.GregorianCalendar;

/**
Expand All @@ -45,30 +39,31 @@ public class JazzSCM extends SCM {

private String repositoryLocation;
private String workspaceName;
private String workspaceName2;
private String streamName;
private String username;
private Secret password;
private String loadRules;
private boolean useUpdate;
private String commonWorkspaceUNC;
private String agentsUsingCommonWorkspace;
private AbstractBuild build;

AbstractBuild build;

private JazzRepositoryBrowser repositoryBrowser;

@DataBoundConstructor
public JazzSCM(String repositoryLocation, String workspaceName, String streamName, String loadRules, boolean useUpdate) {

public JazzSCM(String repositoryLocation, String workspaceName, String streamName,
String username, String password,
String loadRules, boolean useUpdate) {
this.repositoryLocation = repositoryLocation;
this.workspaceName = workspaceName;
this.streamName = streamName;
this.username = username;
this.password = StringUtils.isEmpty(password) ? null : Secret.fromString(password);
this.loadRules = loadRules;
this.useUpdate = useUpdate;
}

public AbstractBuild getAbstractBuild() {
return build;
}

}

public String getRepositoryLocation() {
return repositoryLocation;
}
Expand All @@ -89,11 +84,6 @@ public String getAgentsUsingCommonWorkspace() {
return agentsUsingCommonWorkspace;
}

public String getDefaultWorkspaceName() {
return StringUtils.defaultString(workspaceName, "${NODE_NAME}_${JOB_NAME}");
//return StringUtils.defaultString(workspaceName, "${JOB_NAME}");
}

public String getStreamName() {
return streamName;
}
Expand All @@ -103,16 +93,16 @@ public String getLoadRules() {
}

public String getUsername() {
return getDescriptor().getRTCUserName();
return username;
}

public String getPassword() {
return Secret.toString(getDescriptor().getRTCPassword());
return Secret.toString(password);
}

private JazzClient getClientInstance(Launcher launcher, TaskListener listener, FilePath jobWorkspace) {
private JazzClient getClientInstance(Launcher launcher, TaskListener listener, FilePath jobWorkspace) throws IOException, InterruptedException {
return new JazzClient(launcher, listener, jobWorkspace, getDescriptor().getJazzExecutable(),
getConfiguration());
getConfiguration(listener));
}

@Override
Expand All @@ -130,11 +120,6 @@ protected PollingResult compareRemoteRevisionWith(AbstractProject<?, ?> project,
listener.error("Build was null. Not sure what scenarios cause this.");
result = PollingResult.BUILD_NOW;
} else {
Node node = build.getBuiltOn();
String nodeName = node.getNodeName();
workspaceName2 = workspaceName.replace("${NODE_NAME}", nodeName);
workspaceName2 = workspaceName2.replace("${JOB_NAME}", build.getProject().getName());

JazzClient client = getClientInstance(launcher, listener, workspace);
try {
//return PollingResult.SIGNIFICANT;
Expand All @@ -149,11 +134,6 @@ protected PollingResult compareRemoteRevisionWith(AbstractProject<?, ?> project,
@Override
public boolean checkout(AbstractBuild<?, ?> build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException {
this.build = build;

Node node = build.getBuiltOn();
String nodeName = node.getNodeName();
workspaceName2 = workspaceName.replace("${NODE_NAME}", nodeName);
workspaceName2 = workspaceName2.replace("${JOB_NAME}", build.getProject().getName());

JazzClient client = getClientInstance(launcher, listener, workspace);

Expand Down Expand Up @@ -261,9 +241,8 @@ void delete(File f) throws IOException {
}
}

private List<JazzChangeSet> updateWorkItems(AbstractBuild<?, ?> build, BuildListener listener, JazzClient client) {
private List<JazzChangeSet> updateWorkItems(AbstractBuild<?, ?> build, BuildListener listener, JazzClient client) throws IOException, InterruptedException {
List<JazzChangeSet> changes = null;
String pathString = null;
FilePath path = null;
try {
path = build.getWorkspace();
Expand Down Expand Up @@ -293,7 +272,7 @@ private List<JazzChangeSet> updateWorkItems(AbstractBuild<?, ?> build, BuildList
controllerPort = "8080";
}

JazzConfiguration config = getConfiguration();
JazzConfiguration config = getConfiguration(listener);
try {
path.act(new com.deluan.jenkins.plugins.rtc.commands.LoadCommand.RemoteFileWriter(path.getRemote() + File.separator + build.getFullDisplayName().substring(0, build.getFullDisplayName().indexOf(" ")) + ".txt", config.getLoadRules()));
} catch (Exception e) {
Expand All @@ -302,7 +281,7 @@ private List<JazzChangeSet> updateWorkItems(AbstractBuild<?, ?> build, BuildList
config.consoleOut("Caused by: " + e.getCause());
}

UpdateWorkItemsCommand cmd = new UpdateWorkItemsCommand(getConfiguration());
UpdateWorkItemsCommand cmd = new UpdateWorkItemsCommand(config);

//set parameters
cmd.setUserName(config.getUsername());
Expand All @@ -320,7 +299,6 @@ private List<JazzChangeSet> updateWorkItems(AbstractBuild<?, ?> build, BuildList
listener.error("" + e);
listener.error("Continuing");
}
boolean result = true;
String stdOut = strBuf.toString();

if (stdOut.indexOf("List of all change sets since last build for ") > 0) {
Expand Down Expand Up @@ -423,17 +401,50 @@ public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

public JazzConfiguration getConfiguration() {
JazzConfiguration configuration = new JazzConfiguration();
configuration.setUsername(getDescriptor().getRTCUserName());
configuration.setPassword(Secret.toString(getDescriptor().getRTCPassword()));
JazzConfiguration getConfiguration(final TaskListener listener) throws IOException, InterruptedException {
final JazzConfiguration configuration = new JazzConfiguration();

final DescriptorImpl globalConfig = getDescriptor();

// Note: with all of the fallbacks below, we perform the fallback here on demand
// rather than in the .jelly file: if the global configuration changes after
// the job is created, we want that new global configuration value to be used,
// not just the one that was present when this job's configation was created.

// Use job-specific username if specified, otherwise fall back to globally-configured
String username = this.username;
if (StringUtils.isEmpty(username)) {
username = globalConfig.getRTCUserName();
}
configuration.setUsername(username);

// Use job-specific password if specified, otherwise fall back to globally-configured
Secret password = this.password;
if (password == null || StringUtils.isEmpty(Secret.toString(password))) {
password = globalConfig.getRTCPassword();
}
configuration.setPassword(Secret.toString(password));

// Use job-specific repo if specified, otherwise fall back to globally-configured
String repositoryLocation = this.repositoryLocation;
if (StringUtils.isEmpty(repositoryLocation)) {
repositoryLocation = globalConfig.getRTCServerURL();
}
configuration.setRepositoryLocation(repositoryLocation);

// Expand environment variables such as NODE_NAME and JOB_NAME to produce the actual workspace name.
String workspaceName = this.workspaceName;
if (this.build != null) {
final EnvVars environment = build.getEnvironment(listener);
workspaceName = environment.expand(workspaceName);
}
configuration.setWorkspaceName(workspaceName);

configuration.setStreamName(streamName);
configuration.setLoadRules(loadRules);
configuration.setWorkspaceName(workspaceName2);
configuration.setUseUpdate(useUpdate);
configuration.setUseUpdate(useUpdate);

return configuration;
return configuration;
}

@Extension
Expand Down Expand Up @@ -463,11 +474,11 @@ public String getDefaultWS() {
public String getRTCUserName() {
return RTCUserName;
}

public Secret getRTCPassword() {
return RTCPassword;
}

@Override
public SCM newInstance(StaplerRequest req, JSONObject formData) throws FormException {
JazzSCM scm = (JazzSCM) super.newInstance(req, formData);
Expand Down Expand Up @@ -498,13 +509,9 @@ public String getJazzExecutable() {
}

public String getRTCServerURL() {
if (RTCServerURL == null) {
return "";
} else {
return RTCServerURL;
}
return Util.fixEmpty(RTCServerURL);
}

public FormValidation doExecutableCheck(@QueryParameter String value) {
return FormValidation.validateExecutable(value);
}
Expand Down
@@ -1,23 +1,40 @@
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout"
xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<f:entry title="${%Repository URL}" field="repositoryLocation">
<f:textbox default="${descriptor.RTCServerURL}"/>
</f:entry>
<f:entry title="${%Build workspace}" field="workspaceName">
<f:textbox default="${descriptor.defaultWS}"/>
</f:entry>

<f:entry title="${%Source stream}" field="streamName">
<f:textbox/>
</f:entry>

<f:entry title="${%Load rules}" field="loadRules">
<f:textarea/>
</f:entry>
<f:entry title="Use update">
<f:checkbox name="useUpdate" checked="${h.defaultToTrue(scm.useUpdate)}" default="${descriptor.defaultUseUpdate}" />
If checked, Jenkins will use previously downloaded files whenever possible,
making the build faster.
But this causes the artifacts from the
previous build to remain when a new build starts.

<f:entry title="Use update" field="useUpdate">
<f:checkbox checked="${h.defaultToTrue(scm.useUpdate)}" default="${descriptor.defaultUseUpdate}" />
</f:entry>

<t:listScmBrowsers name="rtc.browser"/>
</j:jelly>

<f:block>
RTC Job-Specific Overrides:
</f:block>

<f:entry title="${%Repository URL}" field="repositoryLocation"
description="If left blank, uses global value (currently ${descriptor.RTCServerURL})">
<f:textbox/>

</f:entry>

<f:entry title="${%RTC User Name}" field="username"
description="If left blank, uses global value (currently ${descriptor.RTCUserName})">
<f:textbox/>
</f:entry>

<f:entry title="${%RTC Password}" field="password"
description="If left blank, uses global value">
<f:password/>
</f:entry>
</j:jelly>
Expand Up @@ -6,20 +6,14 @@
<f:textbox name="rtc.jazzExecutable" value="${descriptor.jazzExecutable}"
checkUrl="'${rootURL}/scm/JazzSCM/executableCheck?value='+escape(this.value)"/>
</f:entry>
<f:entry title="RTC Server URL">
<f:entry title="RTC Server URL" help="/plugin/rtc/help/RTCServerURL.html">
<f:textbox name="rtc.RTCServerURL" value="${descriptor.RTCServerURL}"/>
</f:entry>
<f:entry title="RTC User Name">
<f:entry title="RTC User Name" help="/plugin/rtc/help/RTCUserName.html">
<f:textbox name="rtc.RTCUserName" value="${descriptor.RTCUserName}"/>
</f:entry>
<f:entry title="RTC Password">
<f:entry title="RTC Password" help="/plugin/rtc/help/RTCPassword.html">
<f:password name="rtc.RTCPassword" value="${descriptor.RTCPassword}"/>
</f:entry>
<!--<f:entry>
<f:password field="RTC password" value="${descriptor.RTCPassword}"/>
</f:entry>-->
<!--<f:entry title="${%Build user's password}" field="password" value="${descriptor.RTCPassword}">
<f:password/>
</f:entry>-->
</f:section>
</j:jelly>
@@ -1,6 +1,6 @@
<div>
<p>
This is the password for the user you have set up in Jazz to perform your builds.
Jazz best practice suggests that this is a dedicated user.
</p>
This is the password for the user you have set up in Jazz to perform your builds.
Jazz best practice suggests that this is a dedicated user.
<p>
If left blank, this will default to the value specified in the global configuration.
</div>
@@ -1,6 +1,6 @@
<div>
<p>
This is the URL for your project's Repository. Typically it is something
like http://[hostname]:9443/[projectname]
</p>
This is the URL for your project's Repository. Typically it is something
like http://[hostname]:9443/[projectname]
<p>
If left blank, this will default to the value specified in the global configuration.
</div>
@@ -0,0 +1,5 @@
<div>
If checked, Jenkins will use previously downloaded files whenever possible,
making the build faster. But this causes the artifacts from the previous build
to remain when a new build starts.
</div>
@@ -1,6 +1,6 @@
<div>
<p>
This is the username for the user you have set up in Jazz to perform your builds.
Jazz best practice suggests that this is a dedicated user.
</p>
This is the username for the user you have set up in Jazz to perform your builds.
Jazz best practice suggests that this is a dedicated user.
<p>
If left blank, this will default to the value specified in the global configuration.
</div>
@@ -1,6 +1,7 @@
<div>
<p>
This is the workspace you have created for the builds to run from. Jazz
best practice suggests this is a dedicated workspace.
</p>
</div>
This is the workspace for the builds to run from. Jazz
best practice suggests this is a dedicated workspace.
<p>
Variables such as <code>${NODE_NAME}</code> and <code>${JOB_NAME}</code>
will be replaced when the job runs.
</div>
6 changes: 6 additions & 0 deletions src/main/webapp/help/RTCPassword.html
@@ -0,0 +1,6 @@
<div>
This is the password for the user you have set up in Jazz to perform your builds.
Jazz best practice suggests that this is a dedicated user.
<p>
This value will be used as the default password for jobs using RTC. Individual jobs can override the value.
</div>

0 comments on commit 6b71ffa

Please sign in to comment.