Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[JENKINS-28946] - JIRA support for Workflow jobs
  • Loading branch information
jan-zajic committed Jan 17, 2016
1 parent 2dfb2dd commit 574ddc7
Show file tree
Hide file tree
Showing 15 changed files with 598 additions and 166 deletions.
56 changes: 56 additions & 0 deletions COMPATIBILITY.md
@@ -0,0 +1,56 @@
# Plugin Compatibility with [Workflow](https://github.com/jenkinsci/workflow-plugin)

This document captures the status of features to be compatible or incompatible.

##JiraIssueUpdater usage example

You need keep reference to used scm.
As an example, you can write a flow:

```groovy
node {
def gitScm = git url: 'git@github.com:jenkinsci/jira-plugin.git', branch: 'master'
sh 'make something'
step([$class: 'hudson.plugins.jira.JiraIssueUpdater',
issueSelector: [$class: 'hudson.plugins.jira.DefaultUpdaterIssueSelector'],
scm: gitScm])
gitScm = null
}
```

Note that a pointer to scm class should be better cleared to not serialize scm object between steps.

You can add some labels to issue in jira:
```groovy
step([$class: 'hudson.plugins.jira.JiraIssueUpdater',
issueSelector: [$class: 'hudson.plugins.jira.DefaultUpdaterIssueSelector'],
scm: gitScm,
labels: [ "$version", "jenkins" ]])
```

## Other features

Some features are currently not supported in workflow.
If you are adding new features please make sure that they support Jenkins Workflow Plugin.
See [here](https://github.com/jenkinsci/workflow-plugin/blob/master/COMPATIBILITY.md) for some information.
See [here](https://github.com/jenkinsci/workflow-plugin/blob/master/basic-steps/CORE-STEPS.md) for more information how core jenkins steps integrate with workflow jobs.

Running a notifiers is trickier since normally a flow in progress has no status yet, unlike a freestyle project whose status is determined before the notifier is called (never supported).
So notifiers will never be implemented as you can use the catchError step and run jira action manually.
I'm going to create a special workflow steps to replace this notifiers in future.

Other builders will be supported in future (not supported yet).

##Current status:

- [X] `JIRA Issue Parameter` supported
- [X] `Jira Version Parameter` supported
- [X] `JiraChangeLogAnnotator` supported
- [X] `JiraIssueUpdater` supported
- [ ] `JiraIssueUpdateBuilder` not supported yet
- [ ] `JiraCreateReleaseNotes` not supported yet
- [ ] `JiraCreateIssueNotifier` never supported
- [ ] `JiraIssueMigrator` never supported
- [ ] `JiraReleaseVersionUpdater` never supported
- [ ] `JiraReleaseVersionUpdaterBuilder` not supported yet
- [ ] `JiraVersionCreator` never supported
18 changes: 17 additions & 1 deletion pom.xml
Expand Up @@ -71,6 +71,11 @@
<artifactId>maven-release-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin>
<groupId>org.jenkins-ci.tools</groupId>
<artifactId>maven-hpi-plugin</artifactId>
<version>1.95</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
Expand Down Expand Up @@ -271,7 +276,18 @@
<version>0.0.4</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-scm-step</artifactId>
<version>1.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>1.12</version>
<scope>test</scope>
</dependency>
</dependencies>

<repositories>
Expand Down
@@ -1,13 +1,15 @@
package hudson.plugins.jira;

import java.util.Set;

import javax.annotation.Nonnull;

import org.kohsuke.stapler.DataBoundConstructor;

import hudson.Extension;
import hudson.model.AbstractBuild;
import hudson.model.Descriptor;
import hudson.model.Run;
import hudson.model.TaskListener;
import java.util.Set;
import javax.annotation.Nonnull;
import org.kohsuke.stapler.DataBoundConstructor;

public final class DefaultUpdaterIssueSelector extends UpdaterIssueSelector {

Expand All @@ -17,7 +19,7 @@ public DefaultUpdaterIssueSelector() {

@Override
public Set<String> findIssueIds(@Nonnull final Run<?, ?> run, @Nonnull final JiraSite site, @Nonnull final TaskListener listener) {
return Updater.findIssueIdsRecursive((AbstractBuild<?, ?>) run, site.getIssuePattern(), listener);
return Updater.findIssueIdsRecursive(run, site.getIssuePattern(), listener);
}

@Extension
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/hudson/plugins/jira/JiraBuildAction.java
@@ -1,21 +1,26 @@
package hudson.plugins.jira;

import hudson.model.AbstractBuild;
import hudson.model.Action;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import java.util.*;
import hudson.model.Action;
import hudson.model.Run;

/**
* JIRA issues related to the build.
*
* @author Kohsuke Kawaguchi
*/
public class JiraBuildAction implements Action {
public final AbstractBuild<?, ?> owner;

public final Run<?, ?> owner;

public JiraIssue[] issues;

public JiraBuildAction(AbstractBuild<?, ?> owner, Collection<JiraIssue> issues) {
public JiraBuildAction(Run<?, ?> owner, Collection<JiraIssue> issues) {
this.owner = owner;
this.issues = issues.toArray(new JiraIssue[issues.size()]);
Arrays.sort(this.issues);
Expand Down
41 changes: 27 additions & 14 deletions src/main/java/hudson/plugins/jira/JiraChangeLogAnnotator.java
@@ -1,14 +1,5 @@
package hudson.plugins.jira;

import hudson.Extension;
import hudson.MarkupText;
import hudson.Util;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.scm.ChangeLogAnnotator;
import hudson.scm.ChangeLogSet.Entry;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
Expand All @@ -19,19 +10,41 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

import hudson.Extension;
import hudson.MarkupText;
import hudson.Util;
import hudson.model.Job;
import hudson.model.Run;
import hudson.scm.ChangeLogAnnotator;
import hudson.scm.ChangeLogSet.Entry;

/**
* {@link ChangeLogAnnotator} that picks up JIRA issue IDs.
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class JiraChangeLogAnnotator extends ChangeLogAnnotator {

private static final Logger LOGGER = Logger.getLogger(JiraChangeLogAnnotator.class.getName());

public JiraChangeLogAnnotator() {
super();
LOGGER.fine("JiraChangeLogAnnotator created");
}

@Override
public void annotate(AbstractBuild<?, ?> build, Entry change, MarkupText text) {
JiraSite site = getSiteForProject(build.getProject());
if (site == null) return; // not configured with JIRA
public void annotate(Run<?, ?> build, Entry change, MarkupText text) {
JiraSite site = getSiteForProject(build.getParent());

if (site == null) {
LOGGER.fine("not configured with JIRA");
return; // not configured with JIRA
}

LOGGER.log(Level.FINE, "Using site: {0}", site.url);

// if there's any recorded detail information, try to use that, too.
JiraBuildAction a = build.getAction(JiraBuildAction.class);
Expand Down Expand Up @@ -111,7 +124,7 @@ public void annotate(AbstractBuild<?, ?> build, Entry change, MarkupText text) {
}
}

private void saveIssues(AbstractBuild<?, ?> build, JiraBuildAction a,
private void saveIssues(Run<?, ?> build, JiraBuildAction a,
Set<JiraIssue> issuesToBeSaved) {
if (a != null) {
a.addIssues(issuesToBeSaved);
Expand All @@ -127,7 +140,7 @@ private void saveIssues(AbstractBuild<?, ?> build, JiraBuildAction a,
}
}

JiraSite getSiteForProject(AbstractProject<?, ?> project) {
JiraSite getSiteForProject(Job<?, ?> project) {
return JiraSite.get(project);
}
}
52 changes: 38 additions & 14 deletions src/main/java/hudson/plugins/jira/JiraIssueUpdater.java
@@ -1,6 +1,15 @@
package hudson.plugins.jira;

import java.io.IOException;
import java.io.PrintStream;
import java.util.List;

import org.kohsuke.stapler.DataBoundConstructor;

import com.google.common.collect.Lists;

import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.matrix.MatrixAggregatable;
import hudson.matrix.MatrixAggregator;
Expand All @@ -9,41 +18,54 @@
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.scm.SCM;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.BuildStepMonitor;
import hudson.tasks.Publisher;
import hudson.tasks.Recorder;
import jenkins.model.Jenkins;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;

import org.kohsuke.stapler.DataBoundConstructor;
import java.io.IOException;
import java.io.PrintStream;
import jenkins.tasks.SimpleBuildStep;

/**
* Parses build changelog for JIRA issue IDs and then
* updates JIRA issues accordingly.
*
* @author Kohsuke Kawaguchi
*/
public class JiraIssueUpdater extends Recorder implements MatrixAggregatable {
public class JiraIssueUpdater extends Recorder implements MatrixAggregatable, SimpleBuildStep {

private UpdaterIssueSelector issueSelector;
private SCM scm;
private List<String> labels;

@DataBoundConstructor
public JiraIssueUpdater(UpdaterIssueSelector issueSelector) {
public JiraIssueUpdater(UpdaterIssueSelector issueSelector, SCM scm, List<String> labels) {
super();
this.issueSelector = issueSelector;
this.scm = scm;
if(labels != null)
this.labels = labels;
else
this.labels = Lists.newArrayList();
}

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException {
public void perform(Run<?, ?> run, FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException {
// Don't do anything for individual matrix runs.
if (build instanceof MatrixRun) {
return true;
if (run instanceof MatrixRun) {
return;
} else if(run instanceof AbstractBuild) {
AbstractBuild<?,?> abstractBuild = (AbstractBuild<?,?>) run;
Updater updater = new Updater(abstractBuild.getParent().getScm(), labels);
updater.perform(run, listener, getIssueSelector());
} else if(scm != null) {
Updater updater = new Updater(scm, labels);
updater.perform(run, listener, getIssueSelector());
} else {
throw new IllegalArgumentException("Unsupported run type "+run.getClass().getName());
}

return Updater.perform(build, listener, getIssueSelector());
}

public BuildStepMonitor getRequiredMonitorService() {
Expand All @@ -70,7 +92,8 @@ public MatrixAggregator createAggregator(MatrixBuild build, Launcher launcher, B
public boolean endBuild() throws InterruptedException, IOException {
PrintStream logger = listener.getLogger();
logger.println("End of Matrix Build. Updating JIRA.");
return Updater.perform(this.build, this.listener, getIssueSelector());
Updater updater = new Updater(this.build.getParent().getScm(), labels);
return updater.perform(this.build, this.listener, getIssueSelector());
}
};
}
Expand Down Expand Up @@ -102,4 +125,5 @@ public boolean hasIssueSelectors() {
return Jenkins.getActiveInstance().getDescriptorList(UpdaterIssueSelector.class).size() > 1;
}
}

}
7 changes: 7 additions & 0 deletions src/main/java/hudson/plugins/jira/JiraSession.java
Expand Up @@ -73,6 +73,13 @@ public void addComment(String issueId, String comment,
service.addComment(issueId, comment, groupVisibility, roleVisibility);
}

/**
* Adds new labels to the existing issue.
* Old labels remains untouched.
*
* @param issueId Jira issue ID like "MNG-1235".
* @param labels New labels to add.
*/
public void addLabels(String issueId, List<String> labels) {
List<String> newLabels = Lists.newArrayList();
Issue existingIssue = service.getIssue(issueId);
Expand Down

0 comments on commit 574ddc7

Please sign in to comment.