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-29705
Browse files Browse the repository at this point in the history
  • Loading branch information
jglick committed Nov 22, 2015
2 parents 5e8fdbd + 13a467c commit b255726
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 42 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Expand Up @@ -2,6 +2,14 @@

Only noting significant user changes, not internal code cleanups and minor bug fixes.

## 1.12 (upcoming)

* [JENKINS-31585](https://issues.jenkins-ci.org/browse/JENKINS-31585): make new script editor resizable.

## 1.12-beta-1 (Nov 19 2015)

* [JENKINS-25889](https://issues.jenkins-ci.org/browse/JENKINS-25889): error when submitting to an `input` step after a Jenkins restart.

## 1.11 (Nov 12 2015)

* _Workflow: Multibranch_ plugin now released as nonbeta and available from the regular update center. (Currently not included in _Workflow: Aggregator_.)
Expand Down
4 changes: 2 additions & 2 deletions COMPATIBILITY.md
Expand Up @@ -52,7 +52,6 @@ Entries list the class name serving as the entry point to the relevant functiona
- [ ] SonarQube Jenkins: [SONARJNKNS-213](http://jira.sonarsource.com/browse/SONARJNKNS-213)
- [ ] `VSphereBuildStepContainer` (`vsphere-cloud`): [JENKINS-28930](https://issues.jenkins-ci.org/browse/JENKINS-28930)
- [X] `ScoveragePublisher` (`scoverage`): supported as of 1.2.0
- [ ] `XShellBuilder` (`xshell`): [JENKINS-30372](https://issues.jenkins-ci.org/browse/JENKINS-30372)
- [ ] `AWSCodeDeployPublisher` (`codedeploy`): [issue 36](https://github.com/awslabs/aws-codedeploy-plugin/issues/36)

## Build wrappers
Expand All @@ -68,6 +67,7 @@ Entries list the class name serving as the entry point to the relevant functiona
- [X] `NpmPackagesBuildWrapper` (`nodejs`): scheduled to be supported as of 0.3
- [ ] `AnsiColorBuildWrapper` (`ansicolor`): scheduled to be supported as of 0.4.2
- [ ] `CustomToolInstallWrapper` (`custom-tools-plugin`): [JENKINS-30680](https://issues.jenkins-ci.org/browse/JENKINS-30680)
- [ ] `PortAllocator` (`port-allocator`): [JENKINS-31449](https://issues.jenkins-ci.org/browse/JENKINS-31449)

## Triggers

Expand Down Expand Up @@ -104,7 +104,7 @@ Entries list the class name serving as the entry point to the relevant functiona
- [ ] `lockable-resources` : [JENKINS-30269](https://issues.jenkins-ci.org/browse/JENKINS-30269)
- [X] `customize-build-now`: supported as of 1.1
- [ ] `test-results-analyzer` : [JENKINS-30522](https://issues.jenkins-ci.org/browse/JENKINS-30522)
- [ ] `embeddable-build-status`: [JENKINS-28642](https://issues.jenkins-ci.org/browse/JENKINS-28642)
- [X] `embeddable-build-status`: scheduled to be supported as of 1.9

## Custom steps

Expand Down
6 changes: 3 additions & 3 deletions TUTORIAL.md
Expand Up @@ -736,7 +736,7 @@ A [separate document](cps-global-lib/README.md) has details on this system.

## Creating Multibranch Projects

A new **Workflow: Multibranch** plugin (as of this writing still in beta) offers a better way of versioning your Workflow and managing your project.
The **Workflow: Multibranch** plugin offers a better way of versioning your Workflow and managing your project.
You need to create a distinct project type, **Multibranch Workflow**.

When you have a multibranch workflow, the configuration screen will resemble **Workflow script from SCM** in that your Workflow script comes from source control, not the Jenkins job configuration.
Expand All @@ -748,10 +748,10 @@ Jenkins expects to find a script named `Jenkinsfile` in branches it can build.
From this script, the command `checkout scm` suffices to check out your project’s source code inside some `node {}`.

Say you start with just a `master` branch, then you want to experiment with some changes, so you `git checkout -b newfeature` and push some commits.
Jenkins automatically detects the new branch in your repository and creates a new subproject for itwith its own build history unrelated to trunk, so no one will mind if it has red/yellow balls for a while.
Jenkins automatically detects the new branch in your repository and creates a new subproject for itwith its own build history unrelated to trunk, so no one will mind if it has red/yellow balls for a while.
If you choose, you can ask for the subproject to be automatically removed after the branch is merged and deleted.

If you want to change your Workflow scriptfor example, to add a new Jenkins publisher step corresponding to reports your `Makefile`/`pom.xml`/etc. is newly creatingyou just edit `Jenkinsfile` in your change.
If you want to change your Workflow scriptfor example, to add a new Jenkins publisher step corresponding to reports your `Makefile`/`pom.xml`/etc. is newly creatingyou just edit `Jenkinsfile` in your change.
The Workflow script is always synchronized with the rest of the source code you are working on: `checkout scm` checks out the same revision as the script is loaded from.

# Exploring the Snippet Generator
Expand Down
Expand Up @@ -24,43 +24,78 @@

package org.jenkinsci.plugins.workflow.steps.input;

import com.google.common.base.Function;
import java.io.IOException;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.workflow.SingleJobTestBase;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.graph.FlowGraphWalker;
import org.jenkinsci.plugins.workflow.graph.FlowNode;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.support.actions.PauseAction;
import org.jenkinsci.plugins.workflow.support.steps.input.InputAction;
import org.jenkinsci.plugins.workflow.support.steps.input.InputStepExecution;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runners.model.Statement;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.recipes.LocalData;

public class InputStepRestartTest extends SingleJobTestBase {

@Issue("JENKINS-25889")
@Test public void restart() {
story.addStep(new Statement() {
@Override public void evaluate() throws Throwable {
p = jenkins().createProject(WorkflowJob.class, "demo");
p.setDefinition(new CpsFlowDefinition("input 'paused'"));
startBuilding();
waitForWorkflowToSuspend();
assertTrue(b.isBuilding());
story.j.waitForMessage("paused", b);
}
});
story.addStep(new Statement() {
@Override public void evaluate() throws Throwable {
rebuildContext(story.j);
assertThatWorkflowIsSuspended();
StepExecution.applyAll(InputStepExecution.class, new Function<InputStepExecution,Void>() {
@Override public Void apply(InputStepExecution exec) {
try {
exec.doProceedEmpty();
} catch (IOException x) {
assert false : x;
}
return null;
}
}).get();
proceed(b);
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b));
sanity(b);
}
});
}

private void proceed(WorkflowRun b) throws Exception {
InputAction a = b.getAction(InputAction.class);
assertNotNull(a);
assertEquals(1, a.getExecutions().size());
story.j.submit(story.j.createWebClient().getPage(b, a.getUrlName()).getFormByName(a.getExecutions().get(0).getId()), "proceed");
}

private void sanity(WorkflowRun b) throws Exception {
List<PauseAction> pauses = new ArrayList<PauseAction>();
for (FlowNode n : new FlowGraphWalker(b.getExecution())) {
pauses.addAll(PauseAction.getPauseActions(n));
}
assertEquals(1, pauses.size());
assertFalse(pauses.get(0).isPaused());
String xml = FileUtils.readFileToString(new File(b.getRootDir(), "build.xml"));
assertFalse(xml, xml.contains(InputStepExecution.class.getName()));
}

@Issue("JENKINS-25889")
@LocalData // from 1.4.2
@Test public void oldFlow() {
story.addStep(new Statement() {
@Override public void evaluate() throws Throwable {
WorkflowJob p = jenkins().getItemByFullName("p", WorkflowJob.class);
assertNotNull(p);
WorkflowRun b = p.getLastBuild();
assertNotNull(b);
assertEquals(1, b.getNumber());
proceed(b);
story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b));
sanity(b);
}
});
}
Expand Down
Binary file not shown.
2 changes: 1 addition & 1 deletion cps/gulpfile.js
Expand Up @@ -8,5 +8,5 @@ var builder = require('jenkins-js-builder');
// See https://github.com/tfennelly/jenkins-js-builder#bundling
//
builder.bundle('src/main/js/workflow-editor.js')
.withExternalModuleMapping('jquery-detached', 'jquery-detached:jquery2')
.withExternalModuleMapping('jqueryui-detached', 'jquery-detached:jqueryui1')
.inDir('target/generated-resources/adjuncts/org/jenkinsci/plugins/workflow/cps');
2 changes: 1 addition & 1 deletion cps/package.json
Expand Up @@ -11,7 +11,7 @@
},
"dependencies": {
"jenkins-js-modules": "1.3.0",
"jquery-detached": "^2.1.4-v2",
"jqueryui-detached": "^1.11.4-v9",
"window-handle": "0.0.6"
}
}
2 changes: 1 addition & 1 deletion cps/src/main/js/samples.js
@@ -1,7 +1,7 @@
var samples = [];

exports.addSamplesWidget = function(editor) {
var $ = require('jquery-detached').getJQuery();
var $ = require('jqueryui-detached').getJQueryUI();

if ($('#workflow-editor-wrapper .samples').length) {
// Already there.
Expand Down
19 changes: 16 additions & 3 deletions cps/src/main/js/workflow-editor.js
@@ -1,4 +1,4 @@
var $ = require('jquery-detached').getJQuery();
var $ = require('jqueryui-detached').getJQueryUI();
var jenkinsJSModules = require('jenkins-js-modules');
var wrapper = $('#workflow-editor-wrapper');
var textarea = $('textarea', wrapper);
Expand All @@ -21,7 +21,8 @@ jenkinsJSModules.import('ace-editor:ace-editor-122')
var editor = this.editor;

// Attach the ACE editor instance to the element. Useful for testing.
$('#workflow-editor').get(0).aceEditor = editor;
var $wfEditor = $('#workflow-editor');
$wfEditor.get(0).aceEditor = editor;

acePack.addPackOverride('snippets/groovy.js', '../workflow-cps/snippets/workflow.js');

Expand All @@ -33,7 +34,6 @@ jenkinsJSModules.import('ace-editor:ace-editor-122')
editor.setTheme("ace/theme/tomorrow");
editor.setAutoScrollEditorIntoView(true);
editor.setOption("minLines", 20);
editor.setOption("maxLines", 20);
// enable autocompletion and snippets
editor.setOptions({
enableBasicAutocompletion: true,
Expand All @@ -57,6 +57,19 @@ jenkinsJSModules.import('ace-editor:ace-editor-122')
}
showSamplesWidget();
});

// Make the editor resizable using jQuery UI resizable (http://api.jqueryui.com/resizable).
// ACE Editor doesn't have this as a config option.
$wfEditor.wrap('<div class="jquery-ui-1"></div>');
$wfEditor.resizable({
handles: "s", // Only allow vertical resize off the bottom/south border
resize: function() {
editor.resize();
}
});
// Make the bottom border a bit thicker as a visual cue.
// May not be enough for some people's taste.
$wfEditor.css('border-bottom-width', '0.4em');
});
});

Expand Down
2 changes: 1 addition & 1 deletion demo/Dockerfile
Expand Up @@ -11,7 +11,7 @@ RUN ln -sv /usr/local/apache-maven-$MAVEN_VERSION /usr/local/maven
WORKDIR /opt
# jetty package is still 8
ENV JETTY_VERSION 9.2.12.v20150709
RUN wget -O - "http://mirrors.ibiblio.org/eclipse/jetty/$JETTY_VERSION/dist/jetty-distribution-$JETTY_VERSION.tar.gz" | tar xvfz -
RUN wget -O - "http://archive.eclipse.org/jetty/$JETTY_VERSION/dist/jetty-distribution-$JETTY_VERSION.tar.gz" | tar xvfz -
RUN ln -sv jetty-distribution-$JETTY_VERSION jetty
RUN cd /tmp; ln -s /opt/jetty/webapps
RUN chown -R jenkins /opt/jetty/logs
Expand Down
Expand Up @@ -230,7 +230,7 @@ private AsynchronousExecution sleep() {
LOGGER.log(Level.WARNING, null, x);
}
getExecutor().recordCauseOfInterruption(WorkflowRun.this, listener);
printLater("term", "Forcibly terminate running steps");
printLater("term", "Click here to forcibly terminate running steps");
}
@Override public boolean blocksRestart() {
return execution.blocksRestart();
Expand Down Expand Up @@ -299,7 +299,7 @@ public void doTerm() {
}
@Override public void onFailure(Throwable t) {}
});
printLater("kill", "Forcibly kill entire build");
printLater("kill", "Click here to forcibly kill entire build");
}

/** Immediately kills the build. */
Expand Down
Expand Up @@ -6,16 +6,22 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import org.jenkinsci.plugins.workflow.flow.FlowExecution;
import org.jenkinsci.plugins.workflow.flow.FlowExecutionList;
import org.jenkinsci.plugins.workflow.steps.StepExecution;

/**
* Records the pending inputs required.
*
* @author Kohsuke Kawaguchi
*/
public class InputAction implements RunAction2 {

private final List<InputStepExecution> executions = new ArrayList<InputStepExecution>();
private static final Logger LOGGER = Logger.getLogger(InputAction.class.getName());

private transient List<InputStepExecution> executions = new ArrayList<InputStepExecution>();
private List<String> ids = new ArrayList<String>();

private transient Run<?,?> run;

Expand All @@ -27,9 +33,51 @@ public void onAttached(Run<?, ?> r) {
@Override
public void onLoad(Run<?, ?> r) {
this.run = r;
assert executions != null && !executions.contains(null) : executions;
for (InputStepExecution step : executions) {
step.run = run;
synchronized (this) {
if (ids == null) {
// Loading from before JENKINS-25889 fix. Load the IDs and discard the executions, which lack state anyway.
assert executions != null && !executions.contains(null) : executions;
ids = new ArrayList<String>();
for (InputStepExecution execution : executions) {
ids.add(execution.getId());
}
executions = null;
}
}
}

private synchronized void loadExecutions() {
if (executions == null) {
executions = new ArrayList<InputStepExecution>();
try {
FlowExecution execution = null;
for (FlowExecution _execution : FlowExecutionList.get()) {
if (_execution.getOwner().getExecutable() == run) {
execution = _execution;
break;
}
}
if (execution != null) {
// Futures.addCallback is the safer way to iterate, but we need to know that the result is in.
// And we cannot start the calculation during onLoad because we are still inside WorkflowRun.onLoad and the CpsFlowExecution is not yet initialized.
// WorkflowRun has getExecutionPromise but that is not accessible via API.
for (StepExecution se : execution.getCurrentExecutions(true).get()) {
if (se instanceof InputStepExecution) {
InputStepExecution ise = (InputStepExecution) se;
if (ids.contains(ise.getId())) {
executions.add(ise);
}
}
}
if (executions.size() < ids.size()) {
LOGGER.log(Level.WARNING, "some input IDs not restored from {0}", run);
}
} else {
LOGGER.log(Level.WARNING, "no flow execution found for {0}", run);
}
} catch (Exception x) {
LOGGER.log(Level.WARNING, null, x);
}
}
}

Expand All @@ -39,12 +87,14 @@ public void onLoad(Run<?, ?> r) {

@Override
public String getIconFileName() {
loadExecutions();
if (executions.isEmpty()) return null;
else return "help.png";
}

@Override
public String getDisplayName() {
loadExecutions();
if (executions.isEmpty()) return null;
else return "Paused for Input";
}
Expand All @@ -55,11 +105,14 @@ public String getUrlName() {
}

public synchronized void add(@Nonnull InputStepExecution step) throws IOException {
loadExecutions();
this.executions.add(step);
ids.add(step.getId());
run.save();
}

public synchronized InputStepExecution getExecution(String id) {
loadExecutions();
for (InputStepExecution e : executions) {
if (e.input.getId().equals(id))
return e;
Expand All @@ -68,14 +121,17 @@ public synchronized InputStepExecution getExecution(String id) {
}

public synchronized List<InputStepExecution> getExecutions() {
loadExecutions();
return new ArrayList<InputStepExecution>(executions);
}

/**
* Called when {@link InputStepExecution} is completed to remove it from the active input list.
*/
public synchronized void remove(InputStepExecution exec) throws IOException {
loadExecutions();
executions.remove(exec);
ids.remove(exec.getId());
run.save();
}

Expand Down

0 comments on commit b255726

Please sign in to comment.