Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

p4.prog reporting feature #47

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/java/com/tek42/perforce/Depot.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public class Depot {
private Status status;
private Groups groups;
private Counters counters;
private String programName;
private String programVersion;

public Depot() {
this(new DefaultExecutorFactory());
Expand Down Expand Up @@ -572,5 +574,20 @@ public static boolean safeEquals(String newValue, String currentValue) {
return newValue.equals(currentValue);
}

public String getProgramName() {
return programName;
}

public void setProgramName(String prog) {
this.programName = prog;
}

public String getProgramVersion() {
return programVersion;
}

public void setProgramVersion(String version) {
this.programVersion = version;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -147,22 +147,38 @@ public boolean reject(String line){
* @return A (possibly) modified string array to be executed in place of the original.
*/
protected String[] getExtraParams(String cmd[]) {
List<String> newCmd = new ArrayList<String>();

// Copy over p4 executable
newCmd.add(cmd[0]);

String program = depot.getProgramName();
if (program != null) {
// Insert program to be reported to Perforce server
newCmd.add("-z");
newCmd.add("prog=" + program);
}

String version = depot.getProgramVersion();
if (version != null) {
// Insert version to be reported to Perforce server
newCmd.add("-z");
newCmd.add("version=" + version);
}

String ticket = depot.getP4Ticket();

if(ticket != null) {
// Insert the ticket for the password if tickets are being used...
String newCmds[] = new String[cmd.length + 2];
newCmds[0] = getP4Exe();
newCmds[1] = "-P";
newCmds[2] = ticket;
for(int i = 3; (i - 2) < cmd.length; i++) {
newCmds[i] = cmd[i - 2];
}
cmd = newCmds;
} else {
cmd[0] = getP4Exe();
newCmd.add("-P");
newCmd.add(ticket);
}

// Append the remaining original parameters
for(int i = 1; i < cmd.length; i++) {
newCmd.add(cmd[i]);
}
return cmd;

return newCmd.toArray(new String[newCmd.size()]);
}

/**
Expand Down
102 changes: 101 additions & 1 deletion src/main/java/hudson/plugins/perforce/PerforceSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,17 @@ public class PerforceSCM extends SCM {
* Hash
*/
String slaveClientNameFormat = null;

/**
* Perforce program name to report to the Perforce server
*/
String p4ProgramName = null;
String p4ProgramPollingName = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about moving this variables and the logic to a separate Describable class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why not. I do see the benefits of having this as a separate describable class or extension point. I'm just after the functionality.


/** Regular expression for validation of p4 Program Name
*/
private static final String P4_PROGRAM_NAME_PATTERN =
"[_a-zA-Z0-9@.\\[\\]\\(\\)\\<\\>-]+";

/**
* We need to store the changelog file name for the build so that we can expose
Expand Down Expand Up @@ -328,7 +339,9 @@ public PerforceSCM(
boolean excludedFilesCaseSensitivity,
DepotType depotType,
WorkspaceCleanupConfig cleanWorkspace,
MaskViewConfig useViewMask
MaskViewConfig useViewMask,
String p4ProgramName,
String p4ProgramPollingName
) {

this.configVersion = 2L;
Expand Down Expand Up @@ -423,6 +436,9 @@ public PerforceSCM(
this.excludedUsers = Util.fixEmptyAndTrim(excludedUsers);
this.excludedFiles = Util.fixEmptyAndTrim(excludedFiles);
this.excludedFilesCaseSensitivity = excludedFilesCaseSensitivity;

setP4ProgramName(p4ProgramName);
setP4ProgramPollingName(p4ProgramPollingName);
}

/**
Expand Down Expand Up @@ -891,6 +907,16 @@ public boolean checkout(AbstractBuild build, Launcher launcher,
}

try {
String progName = getEffectiveP4ProgramName();
if (progName != null) {
log.println("p4.prog=" + progName);
if (progName.matches(P4_PROGRAM_NAME_PATTERN)) {
depot.setProgramName(progName);
} else {
log.println("WARNING: p4.prog don't match " + P4_PROGRAM_NAME_PATTERN + " (ignoring it)");
}
}

// keep projectPath local so any modifications for slaves don't get saved
String projectPath;
projectPath = getEffectiveProjectPath(build, build.getProject(), log, depot);
Expand Down Expand Up @@ -1302,6 +1328,16 @@ protected PollingResult compareRemoteRevisionWith(AbstractProject<?, ?> project,
depot = getDepot(buildNode.createLauncher(listener),buildNode.getRootPath(),project,null, buildNode);
logger.println("Using node: " + buildNode.getDisplayName());
}

String progName = getEffectiveP4ProgramPollingName();
if (progName != null) {
logger.println("p4.prog=" + progName);
if (progName.matches(P4_PROGRAM_NAME_PATTERN)) {
depot.setProgramName(progName);
} else {
logger.println("WARNING: p4.prog don't match " + P4_PROGRAM_NAME_PATTERN + " (ignoring it)");
}
}

Workspace p4workspace = getPerforceWorkspace(project, getEffectiveProjectPath(null, project, logger, depot), depot, buildNode, null, launcher, workspace, listener, true);
saveWorkspaceIfDirty(depot, p4workspace, logger);
Expand Down Expand Up @@ -1851,6 +1887,9 @@ public static final class PerforceSCMDescriptor extends SCMDescriptor<PerforceSC
private final static int P4_INFINITE_TIMEOUT_SEC = 0;
private final static int P4_MINIMAL_TIMEOUT_SEC = 30;

private String p4ProgramNameDefault;
private String p4ProgramPollingNameDefault;

public PerforceSCMDescriptor() {
super(PerforceSCM.class, PerforceRepositoryBrowser.class);
load();
Expand Down Expand Up @@ -1926,6 +1965,9 @@ public boolean configure(StaplerRequest req, JSONObject json) throws FormExcepti
p4ClientPattern = Util.fixEmpty(req.getParameter("p4.clientPattern").trim());
passwordExposeDisabled = json.getBoolean("passwordExposeDisabled");

p4ProgramNameDefault = Util.fixEmptyAndTrim(json.getString("p4ProgramNameDefault"));
p4ProgramPollingNameDefault = Util.fixEmptyAndTrim(json.getString("p4ProgramPollingNameDefault"));

// ReadLine timeout
String p4timeoutStr = Util.fixEmpty(req.getParameter("p4.readLineTimeout").trim());
p4ReadlineTimeout = P4_INFINITE_TIMEOUT_SEC;
Expand Down Expand Up @@ -2326,6 +2368,17 @@ public FormValidation doValidateForceSync(StaplerRequest req) {
}
return FormValidation.ok();
}

/**
* Limit allowed characters to prevent shell injection.
*/
public FormValidation doValidateP4ProgramName(StaplerRequest req, @QueryParameter String p4prog) {
p4prog = Util.fixEmptyAndTrim(p4prog);
if (p4prog == null || p4prog.matches(P4_PROGRAM_NAME_PATTERN)) {
return FormValidation.ok();
}
return FormValidation.error("Forbidden characters in string. Name shall match " + P4_PROGRAM_NAME_PATTERN);
}

public List<String> getAllLineEndChoices() {
List<String> allChoices = Arrays.asList(
Expand All @@ -2351,6 +2404,14 @@ public String getAppName() {
return Hudson.getInstance().getDisplayName();
}

public String getP4ProgramNameDefault() {
return p4ProgramNameDefault;
}

public String getP4ProgramPollingNameDefault() {
return p4ProgramPollingNameDefault;
}

@Extension
public static class ItemListenerImpl extends ItemListener {
@Override
Expand Down Expand Up @@ -3196,4 +3257,43 @@ public boolean supportsPolling() {
return true;
}

public String getP4ProgramName() {
return p4ProgramName;
}

public void setP4ProgramName(String name) {
this.p4ProgramName = Util.fixEmptyAndTrim(name);
}

public String getP4ProgramPollingName() {
return p4ProgramPollingName;
}

public void setP4ProgramPollingName(String name) {
this.p4ProgramPollingName = Util.fixEmptyAndTrim(name);
}

public String getEffectiveP4ProgramName() {
String name = getP4ProgramName();
if (name == null) {
name = ((PerforceSCMDescriptor)getDescriptor()).getP4ProgramNameDefault();
}
return Util.fixEmptyAndTrim(name);
}

public String getEffectiveP4ProgramPollingName() {
String name = getP4ProgramPollingName();
if (name == null) {
name = getP4ProgramName();
}
if (name == null) { // Falback to global config
PerforceSCMDescriptor desc = (PerforceSCMDescriptor)getDescriptor();
name = desc.getP4ProgramPollingNameDefault();
if (name == null) {
name = desc.getP4ProgramNameDefault();
}
}
return Util.fixEmptyAndTrim(name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,17 @@
<f:checkbox name="p4.useViewMaskForChangeLog" checked="${instance.useViewMaskForChangeLog}"/>
</f:entry>
</f:optionalBlock>

<f:entry title="p4 Program Name" help="/plugin/perforce/help/p4ProgramName.html">
<f:textbox field="p4ProgramName" id="p4ProgramName"
checkUrl="'${rootURL}/scm/PerforceSCM/validateP4ProgramName?p4prog='+escape(this.form.elements['p4ProgramName'].value)"/>
</f:entry>

<f:entry title="p4 Program Polling Name" help="/plugin/perforce/help/p4ProgramPollingName.html">
<f:textbox field="p4ProgramPollingName" id="p4ProgramPollingName"
checkUrl="'${rootURL}/scm/PerforceSCM/validateP4ProgramName?p4prog='+escape(this.form.elements['p4ProgramPollingName'].value)"/>
</f:entry>

</f:advanced>

<t:listScmBrowsers name="browser" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@
<f:checkbox name="p4.passwordExposeDisabled" checked="${descriptor.passwordExposeDisabled}"/>
<f:description>Option globally disables exposal of Perforce passwords</f:description>
</f:entry>


<f:entry title="p4 Program Name Default" help="/plugin/perforce/help/p4ProgramNameDefault.html">
<f:textbox field="p4ProgramNameDefault" id="p4ProgramNameDefault"
checkUrl="'${rootURL}/scm/PerforceSCM/validateP4ProgramName?p4prog='+escape(this.form.elements['p4ProgramNameDefault'].value)"/>
</f:entry>

<f:entry title="p4 Program Polling Name Default" help="/plugin/perforce/help/p4ProgramPollingNameDefault.html">
<f:textbox field="p4ProgramPollingNameDefault" id="p4ProgramPollingNameDefault"
checkUrl="'${rootURL}/scm/PerforceSCM/validateP4ProgramName?p4prog='+escape(this.form.elements['p4ProgramPollingNameDefault'].value)"/>
</f:entry>

</f:section>
</j:jelly>
13 changes: 13 additions & 0 deletions src/main/webapp/help/p4ProgramName.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div>
<p>
Set what Program Name shall be reported to Perforce server.
</p>
<p>
Setting this value will add extra option "-z prog=name" every time
p4 is connecting to the Perforce server. This can be later examined
with p4 monitor by Perforce administrators.

Useful to debug Perforce connections, group then together and identify
their source.
</p>
</div>
15 changes: 15 additions & 0 deletions src/main/webapp/help/p4ProgramNameDefault.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div>
<p>
Set what Program Name shall be reported to Perforce server.
</p>
<p>
Setting this value will add extra option "-z prog=name" every time
p4 is connecting to the Perforce server. This can be later examined
with p4 monitor command by Perforce administrators.
Useful to debug Perforce connections, group then together and identify
their source.

This is a global setting that will be used for all Jenkins jobs, unless
not explicitly overridden in job specific Perforce configuration.
</p>
</div>
16 changes: 16 additions & 0 deletions src/main/webapp/help/p4ProgramPollingName.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div>
<p>
Set what Program Name shall be reported to Perforce server when job is
polling for new changes.
</p>
<p>
This field's value will be used when polling for new changes.
If not set, the value of "p4 Program Name" field will be used.
Use it when you would like to have granular identification of what
connections are tied with polling and what with fetching stages.

There is also corresponding "p4 Program Default Polling Name" option at
Jenkins global configuration. Although before that is used, the local
"p4 Program Name", if set, takes precedence.
</p>
</div>
26 changes: 26 additions & 0 deletions src/main/webapp/help/p4ProgramPollingNameDefault.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div>
<p>
Set what Program Name shall be reported to Perforce server when job is
polling for new changes.
</p>
<p>
This field's value will be used when polling for new changes.
If not set, the value of "p4 Program Name" field will be used.
Use it when you would like to have granular identification of what
connections are tied with polling and what with fetching sources.

This is a global setting that will be used for all Jenkins jobs, unless
not explicitly overridden in job specific Perforce configuration.

This option is overridden by both job specific settings related to this
feature, i.e. "p4 Program Polling Name" and "p4 Program Name".

The effective value resolution order is:
<pre>
job specific p4 "Program Polling Name"
else job specific p4 "Program Name"
else global p4 "Program Polling Name"
else global p4 "Program Name"
</pre>
</p>
</div>
Loading