Skip to content
Merged
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
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ General rules:

- check the [general Jenkins development guide](https://www.jenkins.io/doc/developer/book/)
- make sure to provide tests
- when adding new fields, make sure to [include backward-compatibility](https://www.jenkins.io/doc/developer/persistence/backward-compatibility/) and tests for that
- mark the Pull Request as _draft_ initially, to make sure all the checks pass correctly, then convert it to non-draft.

## Setting up your environment
Expand All @@ -22,20 +23,19 @@ brew install pre-commit && pre-commit install --install-hooks

Use [docker-compose](./docker-compose.yml) to run a local Jenkins instance with the plugin installed. The configuration includes local volumes for both: Jenkins and ssh-agent, so you can easily test the plugin in a clean environment.

```bash

### Atlassian sources import

To resolve some binary compatibility issues [JENKINS-48357](https://issues.jenkins-ci.org/browse/JENKINS-48357),
To resolve [some binary compatibility issues](https://github.com/jenkinsci/jira-plugin/pull/140),
the sources from the artifact [com.atlassian.httpclient:atlassian-httpclient-plugin:0.23](https://packages.atlassian.com/maven-external/com/atlassian/httpclient/atlassian-httpclient-plugin/0.23.0/)
has been imported in the project to have control over http(s) protocol transport layer.
The downloaded sources didn't have any license headers but based on the [pom](https://packages.atlassian.com/maven-external/com/atlassian/httpclient/atlassian-httpclient-plugin/0.23.0/atlassian-httpclient-plugin-0.23.0.pom)
sources are Apache License (see pom in src/main/resources/atlassian-httpclient-plugin-0.23.0.pom)
sources are Apache License (see pom in src/main/resources/atlassian-httpclient-plugin-0.23.0.pom)

### Testing

There is a [Jira Cloud](https://jenkins-jira-plugin.atlassian.net/) test instance that the changes can be tested against, official maintainers are admins that can grant access for testing to PR submitters on a need-to-have basis.

### Releasing the plugin

Make sure you have your `~/.m2/settings.xml` configured accordingly - refer to [releasing Jenkins plugins](https://www.jenkins.io/doc/developer/publishing/releasing/).
See [releasing Jenkins plugins](https://www.jenkins.io/doc/developer/publishing/releasing-manually/).
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
[![Jenkins CI](https://ci.jenkins.io/buildStatus/icon?job=Plugins/jira-plugin/master)](https://ci.jenkins.io/job/Plugins/job/jira-plugin/)
[![Contributors](https://img.shields.io/github/contributors/jenkinsci/jira-plugin.svg)](https://github.com/jenkinsci/jira-plugin/graphs/contributors)

See user documentation at [https://jenkinsci.github.io/jira-plugin/](https://jenkinsci.github.io/jira-plugin/)
1. See user documentation at [https://jenkinsci.github.io/jira-plugin/](https://jenkinsci.github.io/jira-plugin/).
1. Use [Declarative pipelines](https://www.jenkins.io/doc/book/pipeline/#declarative-versus-scripted-pipeline-syntax).
1. Check [jira plugin steps reference](https://www.jenkins.io/doc/pipeline/steps/jira/).

## i18n

Expand All @@ -22,10 +24,3 @@ See user documentation at [https://jenkinsci.github.io/jira-plugin/](https://jen

This plugin uses [CrowdIn platform](https://jenkins.crowdin.com/jira-plugin) as the frontend to manage translations. If you would like to contribute translation of this plugin in your language, you're most welcome! For details, see [jenkins.io CrowdIn introduction](https://www.jenkins.io/doc/developer/crowdin/translating-plugins/).

## Contributing

There have been many developers involved in the development of this plugin and there are many downstream users who depend on it. Tests help us assure that we're delivering a reliable plugin and that we've communicated our intent to other developers in a way that they can detect when they run tests.

- each change should be covered by appropriate unit tests
- in case it is not testable via a unit test, it should be tested against a real Jira instance - possibly both Jira Server and Jira Cloud. There is a [Jira Cloud test instance](https://jenkins-jira-plugin.atlassian.net/) that we are using for testing the plugin releases - let us know in the Pull Request in case you need access for testing

30 changes: 27 additions & 3 deletions src/main/java/hudson/plugins/jira/JiraVersionCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@
import hudson.tasks.Publisher;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.StaplerRequest2;

/**
* A build step which creates new Jira version
*
* @author Artem Koshelev artkoshelev@gmail.com
* @deprecated Replaced by {@link JiraVersionCreatorBuilder}. Read its description to see why.
* Kept for backward compatibility.
* @deprecated Replaced by {@link JiraVersionCreatorBuilder}. Read its
* description to see why. Kept for backward compatibility.
*/
@Deprecated
public class JiraVersionCreator extends Notifier {

private String jiraVersion;
private String jiraProjectKey;
private Boolean failIfAlreadyExists = true;

Check warning on line 29 in src/main/java/hudson/plugins/jira/JiraVersionCreator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 29 is not covered by tests

@DataBoundConstructor
public JiraVersionCreator(String jiraVersion, String jiraProjectKey) {
Expand Down Expand Up @@ -52,16 +55,37 @@
this.jiraProjectKey = jiraProjectKey;
}

public boolean isFailIfAlreadyExists() {
return failIfAlreadyExists;
}

@DataBoundSetter
public void setFailIfAlreadyExists(boolean failIfAlreadyExists) {
this.failIfAlreadyExists = failIfAlreadyExists;
}

@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) {
return new VersionCreator().perform(build.getProject(), jiraVersion, jiraProjectKey, build, listener);
VersionCreator versionCreator = new VersionCreator();
versionCreator.setFailIfAlreadyExists(failIfAlreadyExists);
versionCreator.setJiraVersion(jiraVersion);
versionCreator.setJiraProjectKey(jiraProjectKey);
return versionCreator.perform(build.getProject(), build, listener);

Check warning on line 73 in src/main/java/hudson/plugins/jira/JiraVersionCreator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 69-73 are not covered by tests
}

@Override
public BuildStepDescriptor<Publisher> getDescriptor() {
return DESCRIPTOR;
}

protected Object readResolve() {
if (failIfAlreadyExists == null) {
setFailIfAlreadyExists(true);
}

return this;
}

@Extension
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.StaplerRequest2;

/**
Expand All @@ -25,6 +26,7 @@ public class JiraVersionCreatorBuilder extends Builder implements SimpleBuildSte

private String jiraVersion;
private String jiraProjectKey;
private Boolean failIfAlreadyExists = true;

@DataBoundConstructor
public JiraVersionCreatorBuilder(String jiraVersion, String jiraProjectKey) {
Expand Down Expand Up @@ -53,9 +55,30 @@ public void setJiraProjectKey(String jiraProjectKey) {
this.jiraProjectKey = jiraProjectKey;
}

public boolean isFailIfAlreadyExists() {
return failIfAlreadyExists;
}

protected Object readResolve() {
if (failIfAlreadyExists == null) {
setFailIfAlreadyExists(true);
}

return this;
}

@DataBoundSetter
public void setFailIfAlreadyExists(boolean failIfAlreadyExists) {
this.failIfAlreadyExists = failIfAlreadyExists;
}

@Override
public void perform(Run<?, ?> run, EnvVars env, TaskListener listener) {
new VersionCreator().perform(run.getParent(), jiraVersion, jiraProjectKey, run, listener);
VersionCreator versionCreator = new VersionCreator();
versionCreator.setFailIfAlreadyExists(failIfAlreadyExists);
versionCreator.setJiraVersion(jiraVersion);
versionCreator.setJiraProjectKey(jiraProjectKey);
versionCreator.perform(run.getParent(), run, listener);
}

@Override
Expand Down
39 changes: 30 additions & 9 deletions src/main/java/hudson/plugins/jira/VersionCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,28 @@ class VersionCreator {

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

protected boolean perform(
Job<?, ?> project, String jiraVersion, String jiraProjectKey, Run<?, ?> build, TaskListener listener) {
private boolean failIfAlreadyExists = true;

private String jiraVersion;

private String jiraProjectKey;

public VersionCreator setFailIfAlreadyExists(boolean failIfAlreadyExists) {
this.failIfAlreadyExists = failIfAlreadyExists;
return this;
}

public VersionCreator setJiraVersion(String jiraVersion) {
this.jiraVersion = jiraVersion;
return this;
}

public VersionCreator setJiraProjectKey(String jiraProjectKey) {
this.jiraProjectKey = jiraProjectKey;
return this;
}

protected boolean perform(Job<?, ?> project, Run<?, ?> build, TaskListener listener) {
String realVersion = null;
String realProjectKey = null;

Expand All @@ -42,13 +62,17 @@ protected boolean perform(
List<ExtendedVersion> existingVersions =
Optional.ofNullable(session.getVersions(realProjectKey)).orElse(Collections.emptyList());

// past logic to fail the build if the version already exists
// check if version already exists
if (existingVersions.stream().anyMatch(v -> v.getName().equals(finalRealVersion))) {
listener.getLogger().println(Messages.JiraVersionCreator_VersionExists(realVersion, realProjectKey));
if (listener instanceof BuildListener) {
((BuildListener) listener).finished(Result.FAILURE);
if (failIfAlreadyExists) {
if (listener instanceof BuildListener) {
((BuildListener) listener).finished(Result.FAILURE);
}
return false;
}
return false;
// version exists but we don't fail the build
return true;
}

listener.getLogger().println(Messages.JiraVersionCreator_CreatingVersion(realVersion, realProjectKey));
Expand All @@ -59,9 +83,6 @@ protected boolean perform(
listener.fatalError("Unable to add version %s to Jira project %s", realVersion, realProjectKey, e));
}

if (listener instanceof BuildListener) {
((BuildListener) listener).finished(Result.FAILURE);
}
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@
<f:entry title="${%Jira Project Key}" field="jiraProjectKey">
<f:textbox/>
</f:entry>
<f:entry title="${%Fail if version already exists}" field="failIfAlreadyExists">
<f:checkbox default="true"/>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div>
When checked (default), the build will fail if the version already exists in Jira.
When unchecked, the build will continue successfully even if the version already exists.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@
<f:entry title="${%Jira Project Key}" field="jiraProjectKey">
<f:textbox/>
</f:entry>
<f:entry title="${%Fail if version already exists}" field="failIfAlreadyExists">
<f:checkbox default="true"/>
</f:entry>
</j:jelly>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div>
When checked (default), the build will fail if the version already exists in Jira.
When unchecked, the build will continue successfully even if the version already exists.
</div>
2 changes: 1 addition & 1 deletion src/main/webapp/help-version-create.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<div>
Creates a new version in Jira in the project with the given key. If the version already exists, the build will fail.
Creates a new version in Jira in the project with the given key. By default, if the version already exists, the build will fail. This behavior can be changed by unchecking the "Fail if version already exists" option.
</div>
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package hudson.plugins.jira;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import hudson.model.Result;
import hudson.util.XStream2;
import java.util.Collections;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.WithoutJenkins;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;

class JiraVersionCreatorBuilderTest {
Expand All @@ -19,6 +26,8 @@ class JiraVersionCreatorBuilderTest {

private JiraSession session;

private final XStream2 xStream2 = new XStream2();

@BeforeEach
void createMocks() {
site = mock(JiraSite.class);
Expand All @@ -38,4 +47,56 @@ void testPipelineWithJiraSite(JenkinsRule r) throws Exception {
WorkflowRun b = r.buildAndAssertStatus(Result.SUCCESS, job);
r.assertLogContains("[Jira] Creating version Version in project project-key.", b);
}

@Test
@WithoutJenkins
void readResolveSetsFailIfAlreadyExistsWhenMissingInConfig() {
String xml = """
<hudson.plugins.jira.JiraVersionCreatorBuilder>
<jiraVersion>1.0</jiraVersion>
<jiraProjectKey>PROJ</jiraProjectKey>
</hudson.plugins.jira.JiraVersionCreatorBuilder>
""";
JiraVersionCreatorBuilder builder = (JiraVersionCreatorBuilder) xStream2.fromXML(xml);

assertTrue(builder.isFailIfAlreadyExists());

xml = """
<hudson.plugins.jira.JiraVersionCreator>
<jiraVersion>1.2</jiraVersion>
<jiraProjectKey>PROJ</jiraProjectKey>
</hudson.plugins.jira.JiraVersionCreator>
""";
JiraVersionCreator notifier = (JiraVersionCreator) xStream2.fromXML(xml);

assertThat(notifier.getJiraProjectKey(), is("PROJ"));
assertThat(notifier.getJiraVersion(), is("1.2"));
assertTrue(notifier.isFailIfAlreadyExists());
}

@Test
@WithoutJenkins
void readResolvePresentInConfig() {
String xml = """
<hudson.plugins.jira.JiraVersionCreatorBuilder>
<jiraVersion>1.0</jiraVersion>
<jiraProjectKey>PROJ</jiraProjectKey>
<failIfAlreadyExists>false</failIfAlreadyExists>
</hudson.plugins.jira.JiraVersionCreatorBuilder>
""";
JiraVersionCreatorBuilder builder = (JiraVersionCreatorBuilder) xStream2.fromXML(xml);

assertFalse(builder.isFailIfAlreadyExists());

xml = """
<hudson.plugins.jira.JiraVersionCreator>
<jiraVersion>1.0</jiraVersion>
<jiraProjectKey>PROJ</jiraProjectKey>
<failIfAlreadyExists>false</failIfAlreadyExists>
</hudson.plugins.jira.JiraVersionCreator>
""";
JiraVersionCreator notifier = (JiraVersionCreator) xStream2.fromXML(xml);

assertFalse(notifier.isFailIfAlreadyExists());
}
}
Loading
Loading