Skip to content
This repository has been archived by the owner on Dec 15, 2021. It is now read-only.

Commit

Permalink
Merge branch 'master' into JENKINS-26122
Browse files Browse the repository at this point in the history
* master:
  [JENKINS-27145] Noting #75 in changelog.
  Updating list of unavailable variables (again :) ).
  [FIXED JENKINS-26692] Added BuildTriggerStep.quietPeriod.
  @tfennelly requests more descriptive variable names.
  Needed to update a test as well.
  [FIXED JENKINS-26072] Added WorkspaceStep.dir.
  Updating list of unavailable variables.
  Linking to environment variable (${rootURL}/env-vars.html) in the workflow script help page.

Conflicts:
	CHANGES.md
  • Loading branch information
tfennelly committed Mar 6, 2015
2 parents 5193288 + 5a44ff7 commit 7720630
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Expand Up @@ -6,7 +6,10 @@ Only noting significant user-visible or major API changes, not internal code cle

### User changes
* JENKINS-26122: Prepend `parallel` step execution logs with the branch label.
* JENKINS-26072: you can now specify a custom workspace location to lock in a `ws` step.
* JENKINS-26692: add `quietPeriod` option for the `build` step.
* JENKINS-26619: _Snippet Generator_ did not work on Git SCM extensions.
* JENKINS-27145: showing available environment variables from help.

## 1.3 (Mar 04 2015)

Expand Down
@@ -0,0 +1,73 @@
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.workflow.steps;

import hudson.slaves.DumbSlave;
import java.io.File;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep;
import org.junit.Test;
import org.junit.Rule;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;

public class WorkspaceStepTest {

@Rule public JenkinsRule r = new JenkinsRule();

@Issue("JENKINS-26072")
@Test public void customWorkspace() throws Exception {
DumbSlave s = r.createSlave();
WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("node('" + s.getNodeName() + "') {ws('custom-location') {echo pwd()}}", true));
r.assertLogContains(s.getRemoteFS() + File.separator + "custom-location", r.assertBuildStatusSuccess(p.scheduleBuild2(0)));
}

@Issue("JENKINS-26072")
@Test public void customWorkspaceConcurrency() throws Exception {
// Currently limited to WorkspaceList.allocate:
WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
// Use master as it has 2 executors by default, whereas createSlave hardcodes 1, and I do not want to bother creating a slave by hand:
p.setDefinition(new CpsFlowDefinition("node {ws('custom-location') {echo pwd(); semaphore 'customWorkspace'}}", true));
WorkflowRun b2 = p.scheduleBuild2(0).getStartCondition().get();
SemaphoreStep.waitForStart("customWorkspace/1", b2);
WorkflowRun b3 = p.scheduleBuild2(0).getStartCondition().get();
SemaphoreStep.waitForStart("customWorkspace/2", b3);
SemaphoreStep.success("customWorkspace/1", null);
SemaphoreStep.success("customWorkspace/2", null);
while (b2.isBuilding() || b3.isBuilding()) {
Thread.sleep(100);
}
r.assertBuildStatusSuccess(b2);
r.assertBuildStatusSuccess(b3);
String location = new File(r.jenkins.getRootDir(), "custom-location").getAbsolutePath();
r.assertLogContains(location, b2);
r.assertLogNotContains("custom-location@", b2);
r.assertLogContains(location + "@2", b3);
}

}
Expand Up @@ -23,3 +23,39 @@
but if you specify multiple parameters they must all be named:
</p>
<pre>stage name: 'Build', concurrency: 1</pre>

<h3>Environment Variables</h3>

A set of environment variables are made available to all Jenkins Job types, including Workflow Jobs.
The following is a general list of variables (by name) that are available to all Job types. See the notes
below the list for Workflow specific details.

<p>
<iframe src="${rootURL}/env-vars.html" width="100%"></iframe>
</p>

The following variables are currently unavailable inside a workflow script:
<ul>
<li>EXECUTOR_NUMBER</li>
<li>NODE_NAME</li>
<li>NODE_LABELS</li>
<li>WORKSPACE</li>
</ul>

<h4>Using Environment Variables</h4>

Environment variables are injected into scripts through a variable named "<strong>env</strong>". This variable,
like any other variable, can be used in the general flow of the script, or in variable substitutions e.g. when
constructing email content when using the <code>mail</code> step:

<p/>
<pre>
mail (to: 'devops@acme.com',
subject: "Job '${env.JOB_NAME}' (${env.BUILD_NUMBER}) is waiting for input",
body: "Please go to ${env.BUILD_URL} and verify the build");
</pre>

<p/>
For more on environment variables,
<a href="https://github.com/jenkinsci/workflow-plugin/blob/master/TUTORIAL.md#managing-the-environment" target="_blank">see
here</a>.
Expand Up @@ -90,7 +90,8 @@ public class SnippetizerTest {
@Test public void blockSteps() throws Exception {
assertRoundTrip(new ExecutorStep(null), "node {\n // some block\n}");
assertRoundTrip(new ExecutorStep("linux"), "node('linux') {\n // some block\n}");
assertRoundTrip(new WorkspaceStep(), "ws {\n // some block\n}");
assertRoundTrip(new WorkspaceStep(null), "ws {\n // some block\n}");
assertRoundTrip(new WorkspaceStep("loc"), "ws('loc') {\n // some block\n}");
}

@Test public void escapes() throws Exception {
Expand Down
Expand Up @@ -25,6 +25,7 @@
package org.jenkinsci.plugins.workflow.support.steps;

import hudson.Extension;
import hudson.Util;
import org.jenkinsci.plugins.workflow.steps.AbstractStepDescriptorImpl;
import org.jenkinsci.plugins.workflow.steps.AbstractStepImpl;
import org.kohsuke.stapler.DataBoundConstructor;
Expand All @@ -35,7 +36,15 @@
*/
public final class WorkspaceStep extends AbstractStepImpl {

@DataBoundConstructor public WorkspaceStep() {}
private final String dir;

@DataBoundConstructor public WorkspaceStep(String dir) {
this.dir = Util.fixEmptyAndTrim(dir);
}

public String getDir() {
return dir;
}

@Extension public static final class DescriptorImpl extends AbstractStepDescriptorImpl {

Expand Down
@@ -1,5 +1,6 @@
package org.jenkinsci.plugins.workflow.support.steps;

import com.google.inject.Inject;
import hudson.FilePath;
import hudson.model.Computer;
import hudson.model.Job;
Expand All @@ -21,28 +22,43 @@
*/
public class WorkspaceStepExecution extends AbstractStepExecutionImpl {

@StepContextParameter private transient Computer c;
@StepContextParameter private transient Run<?,?> r;
@Inject(optional=true) private transient WorkspaceStep step;
@StepContextParameter private transient Computer computer;
@StepContextParameter private transient Run<?,?> run;
@StepContextParameter private transient TaskListener listener;
@StepContextParameter private transient FlowNode flowNode;
private BodyExecution body;

@Override
public boolean start() throws Exception {
Job<?,?> j = r.getParent();
if (!(j instanceof TopLevelItem)) {
throw new Exception(j + " must be a top-level job");
Job<?,?> job = run.getParent();
if (!(job instanceof TopLevelItem)) {
throw new Exception(job + " must be a top-level job");
}
Node n = c.getNode();
if (n == null) {
Node node = computer.getNode();
if (node == null) {
throw new Exception("computer does not correspond to a live node");
}
FilePath p = n.getWorkspaceFor((TopLevelItem) j);
if (p == null) {
throw new IllegalStateException(n + " is offline");
WorkspaceList.Lease lease;
String dir = step.getDir();
if (dir == null) {
FilePath baseWorkspace = node.getWorkspaceFor((TopLevelItem) job);
if (baseWorkspace == null) {
throw new IllegalStateException(node + " is offline");
}
lease = computer.getWorkspaceList().allocate(baseWorkspace);
} else {
FilePath rootPath = node.getRootPath();
if (rootPath == null) {
throw new IllegalStateException(node + " is offline");
}
FilePath baseWorkspace = rootPath.child(dir);
// TODO acquire would block the CPS VM thread and not survive restarts.
// Could force the exact path to be acquired only by setting up a background thread (w/ onResume) to block,
// or adding core API to register a callback listener when any existing lease is released.
lease = computer.getWorkspaceList().allocate(baseWorkspace);
}
WorkspaceList.Lease lease = c.getWorkspaceList().allocate(p);
FilePath workspace = lease.path;
FilePath workspace = lease.path; // may be baseWorkspace + @2, @3, etc.
flowNode.addAction(new WorkspaceActionImpl(workspace, flowNode));
listener.getLogger().println("Running in " + workspace);
body = getContext().newBodyInvoker()
Expand Down
Expand Up @@ -24,14 +24,12 @@
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

/**
* @author Vivek Pandey
*/
public class BuildTriggerStep extends AbstractStepImpl {

private final String job;
private List<ParameterValue> parameters;
private boolean wait = true;
private Integer quietPeriod;

@DataBoundConstructor
public BuildTriggerStep(String job) {
Expand All @@ -58,6 +56,14 @@ public boolean getWait() {
this.wait = wait;
}

public Integer getQuietPeriod() {
return quietPeriod;
}

@DataBoundSetter public void setQuietPeriod(Integer quietPeriod) {
this.quietPeriod = quietPeriod;
}

@Extension
public static class DescriptorImpl extends AbstractStepDescriptorImpl {

Expand Down
Expand Up @@ -58,11 +58,15 @@ public boolean start() throws Exception {
if (parameters != null) {
actions.add(new ParametersAction(parameters));
}
Integer quietPeriod = step.getQuietPeriod();
if (quietPeriod == null) {
quietPeriod = project.getQuietPeriod();
}
QueueTaskFuture<?> f = new ParameterizedJobMixIn() {
@Override protected Job asJob() {
return (Job) project;
}
}.scheduleBuild2(project.getQuietPeriod(), actions.toArray(new Action[actions.size()]));
}.scheduleBuild2(quietPeriod, actions.toArray(new Action[actions.size()]));
if (f == null) {
throw new AbortException("Failed to trigger build of " + project.getFullName());
}
Expand Down
Expand Up @@ -24,4 +24,8 @@ THE SOFTWARE.
-->

<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core"/>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry title="Custom Location" field="dir">
<f:textbox/>
</f:entry>
</j:jelly>
@@ -0,0 +1,19 @@
<p>
A workspace is automatically allocated for you with the <code>node</code> step,
or you can get an alternate workspace with this <code>ws</code> step,
but by default the location is chosen automatically.
(Something like <code>SLAVE_ROOT/workspace/JOB_NAME@2</code>.)
</p>
<p>
You can instead specify a path here and that workspace will be locked instead.
(The path may be relative to the slave root, or absolute.)
</p>
<p>
If concurrent builds ask for the same workspace, a directory with a suffix such as <code>@2</code> may be locked instead.
Currently there is no option to wait to lock the exact directory requested;
if you need to enforce that behavior, you can either fail (<code>error</code>) when <code>pwd</code> indicates that you got a different directory,
or you may enforce serial execution of this part of the build by some other means such as <code>stage name: '…', concurrency: 1</code>.
</p>
<p>
If you do not care about locking, just use the <code>dir</code> step to change current directory.
</p>
Expand Up @@ -32,6 +32,9 @@ THE SOFTWARE.
<f:entry field="wait">
<f:checkbox default="true" title="Wait for completion"/>
</f:entry>
<f:entry field="quietPeriod" title="Quiet period">
<f:number clazz="number"/>
</f:entry>
<f:entry title="Parameters">
<div id="params"/>
</f:entry>
Expand Down
@@ -0,0 +1,5 @@
<div>
Optional alternate quiet period (in seconds) before building.
If unset, defaults to the quiet period defined by the downstream project
(or finally to the system-wide default quiet period).
</div>
@@ -0,0 +1,51 @@
/*
* The MIT License
*
* Copyright 2015 Jesse Glick.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package org.jenkinsci.plugins.workflow.support.steps.build;

import org.jenkinsci.plugins.workflow.steps.StepConfigTester;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;

public class BuildTriggerStepTest {

@Rule public JenkinsRule r = new JenkinsRule();

@Issue("JENKINS-26692")
@Test public void configRoundTrip() throws Exception {
BuildTriggerStep s = new BuildTriggerStep("ds");
s = new StepConfigTester(r).configRoundTrip(s);
assertEquals(null, s.getQuietPeriod());
s.setQuietPeriod(5);
s = new StepConfigTester(r).configRoundTrip(s);
assertEquals(Integer.valueOf(5), s.getQuietPeriod());
s.setQuietPeriod(0);
s = new StepConfigTester(r).configRoundTrip(s);
assertEquals(Integer.valueOf(0), s.getQuietPeriod());
}

}

0 comments on commit 7720630

Please sign in to comment.