Navigation Menu

Skip to content

Commit

Permalink
[JENKINS-46064] Check the changelog of all builds when it is a PR
Browse files Browse the repository at this point in the history
  • Loading branch information
rsandell committed Aug 15, 2017
1 parent 10738bc commit 465eea3
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 6 deletions.
6 changes: 6 additions & 0 deletions pipeline-model-definition/pom.xml
Expand Up @@ -241,6 +241,12 @@
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>github-branch-source</artifactId>
<version>2.0.5</version>
<scope>test</scope>
</dependency>

</dependencies>

Expand Down
Expand Up @@ -29,6 +29,7 @@ import hudson.scm.ChangeLogSet
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditional
import org.jenkinsci.plugins.pipeline.modeldefinition.when.DeclarativeStageConditionalScript
import org.jenkinsci.plugins.workflow.cps.CpsScript
import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty
import org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper

abstract class AbstractChangelogConditionalScript<S extends DeclarativeStageConditional<S>> extends DeclarativeStageConditionalScript<S> {
Expand All @@ -42,7 +43,49 @@ abstract class AbstractChangelogConditionalScript<S extends DeclarativeStageCond
initializeEval()
RunWrapper run = this.script.getProperty("currentBuild")
if (run != null) {
List<ChangeLogSet<? extends ChangeLogSet.Entry>> changeSets = run.getChangeSets()
List<ChangeLogSet<? extends ChangeLogSet.Entry>> changeSets = []
def branchJobProperty = run.rawBuild.parent.getProperty(BranchJobProperty.class) //TODO is there an easier way of finding this?
if (branchJobProperty != null) {
def branch = branchJobProperty.getBranch()
/*
Some special handling for pull requests to take into consideration all the builds for a particular PR.
Since a PR is a series of changes that will be merged in some way as one unit so all the changes should be considered.
There is a difference in for example Gerrit where the change that is going to be merged is only the one commit in the latest patch set,
so the previous builds in the change request are not really dependant on each other.
Otherwise we could have just done this for all ChangeRequestSCMHead instances.
A better approach than checking each specific implementation would be nice.
There are some caveats here, like if build 3 contains a revert commit of what is in build 2
we will still "trigger" for change sets on the commit that was reverted.
*/
boolean includeAllBuilds = false
try {
Class githubPr = Class.forName("org.jenkinsci.plugins.github_branch_source.PullRequestSCMHead")
if (branch.head.class.isAssignableFrom(githubPr)) {
includeAllBuilds = true
}
} catch (ClassNotFoundException _) { /*Ignore*/}
if (!includeAllBuilds) { //If not a GitHub pr then maybe bitbucket?
try {
Class bitbucketPr = Class.forName("com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMHead")
if (branch.head.class.isAssignableFrom(bitbucketPr)) {
includeAllBuilds = true
}
} catch (ClassNotFoundException _) { /*Ignore*/
}
}
if (includeAllBuilds) {
script.echo "Examining changelog from all builds of this change request."
for (RunWrapper currB = run; currB != null; currB = currB.previousBuild) {
changeSets.addAll(currB.getChangeSets())
}
}
}

if (changeSets.isEmpty()) { //in case none of the above applies
changeSets = run.getChangeSets()
}


if (changeSets.isEmpty()) {
if (run.number <= 1) {
script.echo "Warning, empty changelog. Probably because this is the first build." //TODO JENKINS-46086
Expand Down
Expand Up @@ -543,6 +543,10 @@ public ExpectationsBuilder resetForNewRun(Result result) {
buildMatchers = new ArrayList<>();
return this;
}

public WorkflowRun getRun() {
return run;
}
}

public class EnvBuilder {
Expand Down
Expand Up @@ -30,18 +30,24 @@
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import hudson.model.Result;
import hudson.model.Slave;
import jenkins.branch.Branch;
import jenkins.branch.BranchProperty;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.github_branch_source.BranchSCMHead;
import org.jenkinsci.plugins.github_branch_source.PullRequestSCMHead;
import org.jenkinsci.plugins.pipeline.modeldefinition.endpoints.ModelConverterAction;
import org.jenkinsci.plugins.pipeline.modeldefinition.model.Stage;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.multibranch.BranchJobProperty;
import org.junit.BeforeClass;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Collections;

Expand Down Expand Up @@ -127,7 +133,7 @@ public void paramsInWhenExpression() throws Exception {

@Test
public void whenChangeset() throws Exception {
//First time build always skips the changelog
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changeset")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("JS World");
Expand All @@ -144,9 +150,58 @@ public void whenChangeset() throws Exception {
.go();
}

@Test
public void whenChangesetPR() throws Exception {
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changeset")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("JS World");
builder.go();

builder.resetForNewRun(Result.SUCCESS);

sampleRepo.write("webapp/js/somecode.js", "//fake file");
sampleRepo.git("add", "webapp/js/somecode.js");
sampleRepo.git("commit", "--message=files");

builder.logContains("Hello", "JS World")
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();

builder.resetForNewRun(Result.SUCCESS);
fakePRMarker(builder.getRun().getParent());

sampleRepo.write("dontcare.txt", "empty");
sampleRepo.git("add", "dontcare.txt");
sampleRepo.git("commit", "--message=file");

builder.logContains("Hello", "JS World", "Examining changelog from all builds of this change request") //Should go for the file added in build 2
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();
}

@Test
public void whenChangelog() throws Exception {
//First time build always skips the changelog
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changelog")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("Dull World");
builder.go();

builder.resetForNewRun(Result.SUCCESS);

sampleRepo.write("something.txt", "//fake file");
sampleRepo.git("add", "something.txt");
sampleRepo.git("commit", "-m", "Some title that we don't care about\n\nSome explanation\n[DEPENDENCY] some-app#45");

builder.logContains("Hello", "Dull World")
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();
}

@Test
public void whenChangelogPR() throws Exception {
//TODO JENKINS-46086 First time build always skips the changelog
final ExpectationsBuilder builder = expect("when/changelog", "changelog")
.logContains("Hello", "Stage 'Two' skipped due to when conditional", "Warning, empty changelog. Probably because this is the first build.")
.logNotContains("Dull World");
Expand All @@ -161,6 +216,31 @@ public void whenChangelog() throws Exception {
builder.logContains("Hello", "Dull World")
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();

builder.resetForNewRun(Result.SUCCESS);
fakePRMarker(builder.getRun().getParent());

sampleRepo.write("something2.txt", "//fake file");
sampleRepo.git("add", "something2.txt");
sampleRepo.git("commit", "-m", "Some title");

builder.logContains("Hello", "Dull World", "Examining changelog from all builds of this change request") //Should go for the log in build 2
.logNotContains("Stage 'Two' skipped due to when conditional", "Warning, empty changelog.")
.go();

}


static void fakePRMarker(WorkflowJob job) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
//Black magic ahead!!
final Constructor<PullRequestSCMHead> pullRequestSCMHeadConstructor = PullRequestSCMHead.class.getDeclaredConstructor(String.class, boolean.class, int.class, BranchSCMHead.class, String.class, String.class, String.class);
pullRequestSCMHeadConstructor.setAccessible(true);
final PullRequestSCMHead scmHead = pullRequestSCMHeadConstructor.newInstance("fake", false, 0, null, "fake", "fake", "master");
Branch b = new Branch("fake", scmHead, null, Collections.<BranchProperty>emptyList());
final Constructor<BranchJobProperty> branchJobPropertyConstructor = BranchJobProperty.class.getDeclaredConstructor(Branch.class);
branchJobPropertyConstructor.setAccessible(true);
final BranchJobProperty branchJobProperty = branchJobPropertyConstructor.newInstance(b);
job.addProperty(branchJobProperty);

}
}

0 comments on commit 465eea3

Please sign in to comment.