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: allow customization of used SCMRevision #157

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@
import hudson.model.TaskListener;
import hudson.scm.SCM;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import jenkins.branch.Branch;
import jenkins.scm.api.SCMFileSystem;
import jenkins.scm.api.SCMHead;
Expand Down Expand Up @@ -89,16 +92,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 = ((WorkflowMultiBranchProject) parent).getSCMSource(branch.getSourceId());
if (scmSource == null) {
throw new IllegalStateException(branch.getSourceId() + " not found");
SCMSource scmSource = getScmSource(job, branch.getSourceId());

SCMRevision customizedRevision = Optional.ofNullable(scmSource.getTraits())
.orElse(Collections.emptyList()).stream()
.filter(SCMRevisionCustomizationTrait.class::isInstance)
.map(SCMRevisionCustomizationTrait.class::cast)
.max(Comparator.comparing(SCMRevisionCustomizationTrait::getPrecedence))
.map(trait -> trait.customize(build, listener))
.orElse(null);

SCMHead head;
SCMRevision tip;
if (customizedRevision != null) {
head = customizedRevision.getHead();
tip = customizedRevision;
} else {
head = branch.getHead();
tip = scmSource.fetch(head, listener);
}
SCMHead head = branch.getHead();
SCMRevision tip = scmSource.fetch(head, listener);

SCM scm;
if (tip != null) {
build.addAction(new SCMRevisionAction(scmSource, tip));
Expand Down Expand Up @@ -145,6 +158,18 @@ 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;
}

@Extension public static class DescriptorImpl extends FlowDefinitionDescriptor {

@NonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.jenkinsci.plugins.workflow.multibranch;

import hudson.model.TaskListener;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.trait.SCMSourceTrait;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;

public abstract class SCMRevisionCustomizationTrait extends SCMSourceTrait {

/** Customize the SCMRevision. */
public abstract SCMRevision customize(WorkflowRun build, TaskListener listener);

/** If multiple traits of this type are found, the highest precedence will be taken. */
public abstract int getPrecedence();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.jenkinsci.plugins.workflow.multibranch;

import hudson.Extension;
import hudson.model.TaskListener;
import jenkins.plugins.git.GitSCMBuilder;
import jenkins.plugins.git.GitSCMSource;
import jenkins.plugins.git.GitSCMSourceContext;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.trait.SCMBuilder;
import jenkins.scm.api.trait.SCMSourceContext;
import jenkins.scm.api.trait.SCMSourceTraitDescriptor;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;

/** Sample {@link SCMRevisionCustomizationTrait} that has a fixed revision to build. Git only in this example. */
public class FixedSCMRevisionCustomizationTrait extends SCMRevisionCustomizationTrait {

private final SCMSource scmSource;
private final SCMRevision revision;

FixedSCMRevisionCustomizationTrait(SCMSource scmSource, SCMRevision revision) {
this.scmSource = scmSource;
this.revision = revision;
}

@Override
public SCMRevision customize(WorkflowRun build, TaskListener listener) {
build.addAction(new SCMRevisionAction(scmSource, revision));
return revision;
}

@Override
public int getPrecedence() {
return 0;
}

@Extension
public static class DescriptorImpl extends SCMSourceTraitDescriptor {

@Override
public Class<? extends SCMSourceContext> getContextClass() {
return GitSCMSourceContext.class;
}

@Override
public Class<? extends SCMSource> getSourceClass() {
return GitSCMSource.class;
}

@Override
public Class<? extends SCMBuilder> getBuilderClass() {
return GitSCMBuilder.class;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
import java.util.TreeSet;
import jenkins.branch.BranchSource;
import jenkins.model.Jenkins;
import jenkins.plugins.git.GitBranchSCMHead;
import jenkins.plugins.git.GitBranchSCMRevision;
import jenkins.plugins.git.GitSCMSource;
import jenkins.plugins.git.GitSampleRepoRule;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;
import jenkins.scm.api.SCMSourceDescriptor;
import jenkins.scm.api.trait.SCMSourceTrait;
import jenkins.scm.impl.subversion.SubversionSCMFileSystem;
import jenkins.scm.impl.subversion.SubversionSCMSource;
import static org.hamcrest.Matchers.*;
Expand Down Expand Up @@ -116,6 +118,35 @@ public class SCMBinderTest {
assertFalse(iterator.hasNext());
}

@Test public void customRevision() throws Exception {
sampleGitRepo.init();
sampleGitRepo.write("Jenkinsfile", "semaphore 'wait'; node {checkout scm; echo readFile('file')}");
sampleGitRepo.write("file", "initial content");
sampleGitRepo.git("add", "Jenkinsfile");
sampleGitRepo.git("commit", "--all", "--message=flow");
String firstCommit = sampleGitRepo.head();
sampleGitRepo.write("file", "subsequent content");
sampleGitRepo.git("commit", "--all", "--message=tweaked"); // will not be built
GitBranchSCMHead head = new GitBranchSCMHead("master");
GitBranchSCMRevision firstRevision = new GitBranchSCMRevision(head, firstCommit);
WorkflowMultiBranchProject mp = r.jenkins.createProject(WorkflowMultiBranchProject.class, "p");
GitSCMSource gitSCMSource = new GitSCMSource(null, sampleGitRepo.toString(), "", "*", "", false);
List<SCMSourceTrait> scmTraits = gitSCMSource.getTraits();
scmTraits.add(new FixedSCMRevisionCustomizationTrait(gitSCMSource, firstRevision));
gitSCMSource.setTraits(scmTraits);
mp.getSourcesList().add(new BranchSource(gitSCMSource));
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);
SemaphoreStep.success("wait/1", null);
r.assertLogContains("initial content", r.waitForCompletion(b1));
r.assertLogNotContains("SUBSEQUENT CONTENT", b1);
}

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