Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JENKINS-61654: use rev from upstream where possible #155

Closed
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
20 changes: 15 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ THE SOFTWARE.
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>scm-api</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>github-branch-source</artifactId>
<version>2.10.2</version>
<exclusions>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>branch-api</artifactId>
Expand Down Expand Up @@ -160,11 +175,6 @@ THE SOFTWARE.
<classifier>tests</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>subversion</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,26 @@
import hudson.console.ConsoleAnnotator;
import hudson.console.ConsoleNote;
import hudson.model.Action;
import hudson.model.Cause.UpstreamCause;
import hudson.model.Descriptor;
import hudson.model.DescriptorVisibilityFilter;
import hudson.model.ItemGroup;
import hudson.model.Queue;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.RevisionParameterAction;
import hudson.scm.SCM;
import java.io.IOException;
import java.util.List;
import jenkins.branch.Branch;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.plugins.git.AbstractGitSCMSource.SCMRevisionImpl;
import jenkins.scm.api.SCMFileSystem;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;
import jenkins.scm.api.SCMSource;
import org.jenkinsci.plugins.github_branch_source.PullRequestSCMRevision;
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
import org.jenkinsci.plugins.workflow.flow.FlowDefinition;
Expand Down Expand Up @@ -89,18 +95,26 @@ public SCMBinder(String scriptPath) {
throw new IllegalStateException("inappropriate context");
}
Branch branch = property.getBranch();
ItemGroup<?> parent = job.getParent();
if (!(parent instanceof WorkflowMultiBranchProject)) {
throw new IllegalStateException("inappropriate context");
SCMSource scmSource = getScmSource(job, branch.getSourceId());

SCMRevision upstreamRevision = null;
if (scmSource instanceof AbstractGitSCMSource) {
upstreamRevision = getUpstreamGitScmRevisionForSameRemote(build, branch, (AbstractGitSCMSource) scmSource, listener);
}
SCMSource scmSource = ((WorkflowMultiBranchProject) parent).getSCMSource(branch.getSourceId());
if (scmSource == null) {
throw new IllegalStateException(branch.getSourceId() + " not found");

SCMHead head;
if (upstreamRevision != null) {
head = upstreamRevision.getHead();
} else {
head = branch.getHead();
}
SCMHead head = branch.getHead();

SCMRevision tip = scmSource.fetch(head, listener);
SCM scm;
if (tip != null) {
if (upstreamRevision != null) {
tip = upstreamRevision;
}
build.addAction(new SCMRevisionAction(scmSource, tip));
SCMRevision rev = scmSource.getTrustedRevision(tip, listener);
try (SCMFileSystem fs = USE_HEAVYWEIGHT_CHECKOUT ? null : SCMFileSystem.of(scmSource, head, rev)) {
Expand Down Expand Up @@ -145,6 +159,75 @@ public SCMBinder(String scriptPath) {
return new CpsScmFlowDefinition(scm, scriptPath).create(handle, listener, actions);
}

private SCMSource getScmSource(WorkflowJob job, String scmSourceId) {
ItemGroup<?> parent = job.getParent();
if (!(parent instanceof WorkflowMultiBranchProject)) {
throw new IllegalStateException("inappropriate context");
}
SCMSource scmSource = ((WorkflowMultiBranchProject) parent).getSCMSource(scmSourceId);
if (scmSource == null) {
throw new IllegalStateException(scmSourceId + " not found");
}
return scmSource;
}

/**
* This method will set a {@link RevisionParameterAction} (force the checkout of a specific rev) with the rev of the
* upstream as well as return the {@link SCMRevision} of the upstream job, if
* <li> the current run is triggered by an {@link UpstreamCause}
* <li> the upstream is a {@link WorkflowRun} that has a {@link SCMRevisionAction}
* <li> the {@link SCMSource} of that upstream is of the same (git) type as the current one (eg. GitHub - GitHub but
* not Git - Github) and they have the same remote URL
* <li> the (jenkins) branch names match (eg. PR-42 - PR-42)
* <li> the {@link SCMRevision} includes a rev, such as {@link PullRequestSCMRevision} or {@link SCMRevisionImpl}.
*/
private SCMRevision getUpstreamGitScmRevisionForSameRemote(WorkflowRun currentBuild, Branch currentBranch,
AbstractGitSCMSource currentGitScmSource, TaskListener listener) {
UpstreamCause upstreamCause = currentBuild.getCause(UpstreamCause.class);
if (upstreamCause == null) {
return null;
}

Run<?, ?> upstreamRun = upstreamCause.getUpstreamRun();
if (upstreamRun instanceof WorkflowRun) {

SCMRevisionAction scmRevisionAction = upstreamRun.getAction(SCMRevisionAction.class);
if (scmRevisionAction == null) {
return null;
}

WorkflowJob upstreamJob = ((WorkflowRun) upstreamRun).getParent();
SCMSource upstreamScmSource;
try {
upstreamScmSource = getScmSource(upstreamJob, scmRevisionAction.getSourceId());
} catch (IllegalStateException e) {
return null;
}
if (!(currentGitScmSource.getClass() == upstreamScmSource.getClass())
|| !(currentGitScmSource.getRemote().equalsIgnoreCase(
((AbstractGitSCMSource) upstreamScmSource).getRemote()))) {
return null;
}

SCMRevision upstreamScmRevision = scmRevisionAction.getRevision();
if (!currentBranch.getHead().getName().equals(upstreamScmRevision.getHead().getName())) {
return null;
}

if (upstreamScmRevision instanceof PullRequestSCMRevision) {
listener.getLogger().println("Using upstream PR-revision");
currentBuild.addAction(new RevisionParameterAction(((PullRequestSCMRevision) upstreamScmRevision).getPullHash()));
return upstreamScmRevision;

} else if (upstreamScmRevision instanceof SCMRevisionImpl) {
listener.getLogger().println("Using upstream git revision");
currentBuild.addAction(new RevisionParameterAction(((SCMRevisionImpl) upstreamScmRevision).getHash()));
return upstreamScmRevision;
}
}
return null;
}

@Extension public static class DescriptorImpl extends FlowDefinitionDescriptor {

@NonNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Util;
import hudson.model.Cause;
import hudson.model.Cause.UpstreamCause;
import hudson.model.CauseAction;
import hudson.model.Item;
import hudson.model.Result;
import hudson.model.TaskListener;
Expand Down Expand Up @@ -55,6 +58,7 @@

import jenkins.scm.impl.subversion.SubversionSampleRepoRule;
import org.acegisecurity.Authentication;
import org.jenkinsci.plugins.github_branch_source.GitHubSCMSource;
import org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
Expand Down Expand Up @@ -116,6 +120,39 @@ public class SCMBinderTest {
assertFalse(iterator.hasNext());
}

@Test public void upstreamRevisionGit() throws Exception {
sampleGitRepo.init();
ScriptApproval sa = ScriptApproval.get();
sa.approveSignature("staticField hudson.model.Items XSTREAM2");
sa.approveSignature("method com.thoughtworks.xstream.XStream toXML java.lang.Object");
sa.approveSignature("method org.jenkinsci.plugins.workflow.support.steps.build.RunWrapper getRawBuild");
sa.approveSignature("method hudson.model.Run getCauses");
sa.approveSignature("staticMethod org.codehaus.groovy.runtime.DefaultGroovyMethods inspect java.lang.Object");
sampleGitRepo.write("Jenkinsfile", "echo hudson.model.Items.XSTREAM2.toXML(scm); echo currentBuild.rawBuild.causes.inspect(); semaphore 'wait'; node {checkout scm; echo readFile('file')}");
sampleGitRepo.write("file", "initial content");
sampleGitRepo.git("add", "Jenkinsfile");
sampleGitRepo.git("commit", "--all", "--message=flow");
WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
mp.getSourcesList().add(new BranchSource(new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false)));
WorkflowJob p = WorkflowMultiBranchProjectTest.scheduleAndFindBranchProject(mp, "master");
SemaphoreStep.waitForStart("wait/1", null);
WorkflowRun b1 = p.getLastBuild();
assertNotNull(b1);
assertEquals(1, b1.getNumber());
assertRevisionAction(b1);
r.assertLogContains("Obtained Jenkinsfile from ", b1);
sampleGitRepo.write("file", "subsequent content");
sampleGitRepo.git("commit", "--all", "--message=tweaked");
SemaphoreStep.success("wait/1", null);
mp.scheduleBuild2(0, new CauseAction(new UpstreamCause(b1))); // this cause is ignored / replaced (?) with BranchIndexingCause
SemaphoreStep.waitForStart("wait/2", null);
WorkflowRun b2 = p.getLastBuild();
assertEquals(2, b2.getNumber());
r.assertLogContains("initial content", r.waitForCompletion(b1));
r.assertLogContains("initial content", r.waitForCompletion(b2));
r.assertLogNotContains("SUBSEQUENT CONTENT", b2);
}

public static void assertRevisionAction(WorkflowRun build) {
SCMRevisionAction revisionAction = build.getAction(SCMRevisionAction.class);
assertNotNull(revisionAction);
Expand Down