Skip to content

Commit 65d2017

Browse files
authored
[JENKINS-73471] Git scm.userRemoteConfigs.credentialsId configured by Bitbucket is null (#867)
Restore passing credentialsId to the GitSCM configuration. It would also guarantee that credentials usage is still tracked. Checking down the line, GitClient still uses the authenticator credentials reference.
1 parent 05b9d2a commit 65d2017

File tree

6 files changed

+230
-42
lines changed

6 files changed

+230
-42
lines changed

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
<dependency>
8888
<groupId>org.jenkins-ci.plugins</groupId>
8989
<artifactId>git</artifactId>
90+
<!-- TODO: remove when in bom -->
91+
<version>5.6.1-rc5349.5d1753d390a_7</version>
9092
</dependency>
9193
<dependency>
9294
<groupId>org.jenkins-ci.plugins</groupId>

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
import hudson.model.Items;
6868
import hudson.model.TaskListener;
6969
import hudson.plugins.git.GitSCM;
70-
import hudson.plugins.git.extensions.GitSCMExtension;
7170
import hudson.scm.SCM;
7271
import hudson.security.AccessControlled;
7372
import hudson.util.FormFillFailure;
@@ -1002,33 +1001,12 @@ private BitbucketCommit findPRDestinationCommit(BitbucketPullRequest pr, TaskLis
10021001
public SCM build(SCMHead head, SCMRevision revision) {
10031002
initCloneLinks();
10041003

1005-
String scmCredentialsId = credentialsId;
1004+
boolean sshAuth = traits.stream()
1005+
.anyMatch(SSHCheckoutTrait.class::isInstance);
10061006

10071007
BitbucketAuthenticator authenticator = authenticator();
1008-
GitSCMExtension scmExtension;
1009-
if (authenticator != null) {
1010-
// workaround to force git-plugin to use the configured username/password credentialsId as is
1011-
// remove this workaround as https://github.com/jenkinsci/bitbucket-branch-source-plugin/pull/867 will be merged
1012-
boolean sshAuth = traits.stream()
1013-
.anyMatch(SSHCheckoutTrait.class::isInstance);
1014-
if (sshAuth) {
1015-
// trait will do the magic
1016-
scmCredentialsId = null;
1017-
scmExtension = new GitClientAuthenticatorExtension(null);
1018-
} else if (authenticator instanceof BitbucketUsernamePasswordAuthenticator) {
1019-
scmExtension = new GitClientAuthenticatorExtension(null);
1020-
} else {
1021-
// extension overrides the configured credentialsId with a custom StandardUsernameCredentials provided by the Authenticator
1022-
scmExtension = new GitClientAuthenticatorExtension(authenticator.getCredentialsForSCM());
1023-
// will be overridden by git extension
1024-
scmCredentialsId = null;
1025-
}
1026-
} else {
1027-
scmExtension = new GitClientAuthenticatorExtension(null);
1028-
}
1029-
1030-
return new BitbucketGitSCMBuilder(this, head, revision, scmCredentialsId)
1031-
.withExtension(scmExtension)
1008+
return new BitbucketGitSCMBuilder(this, head, revision, credentialsId)
1009+
.withExtension(new GitClientAuthenticatorExtension(authenticator == null || sshAuth ? null : authenticator.getCredentialsForSCM()))
10321010
.withCloneLinks(primaryCloneLinks, mirrorCloneLinks)
10331011
.withTraits(traits)
10341012
.build();

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/avatars/AvatarCache.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import hudson.util.DaemonThreadFactory;
3636
import hudson.util.HttpResponses;
3737
import hudson.util.NamingThreadFactory;
38+
import jakarta.servlet.ServletException;
39+
import jakarta.servlet.http.HttpServletResponse;
3840
import java.awt.Color;
3941
import java.awt.Graphics2D;
4042
import java.awt.image.BufferedImage;
@@ -57,14 +59,12 @@
5759
import java.util.concurrent.ThreadPoolExecutor;
5860
import java.util.concurrent.TimeUnit;
5961
import javax.imageio.ImageIO;
60-
import javax.servlet.ServletException;
61-
import javax.servlet.http.HttpServletResponse;
6262
import jenkins.model.Jenkins;
6363
import org.apache.commons.lang.StringUtils;
6464
import org.kohsuke.stapler.HttpResponse;
6565
import org.kohsuke.stapler.QueryParameter;
66-
import org.kohsuke.stapler.StaplerRequest;
67-
import org.kohsuke.stapler.StaplerResponse;
66+
import org.kohsuke.stapler.StaplerRequest2;
67+
import org.kohsuke.stapler.StaplerResponse2;
6868

6969
import static java.awt.RenderingHints.KEY_ALPHA_INTERPOLATION;
7070
import static java.awt.RenderingHints.KEY_INTERPOLATION;
@@ -318,7 +318,7 @@ public String getUrlName() {
318318
* @param requestedSize the requested size (defaults to {@code 48x48} if unspecified).
319319
* @return the response.
320320
*/
321-
public HttpResponse doDynamic(StaplerRequest req, @QueryParameter String requestedSize) {
321+
public HttpResponse doDynamic(StaplerRequest2 req, @QueryParameter String requestedSize) {
322322
if (StringUtils.isBlank(req.getRestOfPath())) {
323323
return HttpResponses.notFound();
324324
}
@@ -352,7 +352,7 @@ public HttpResponse doDynamic(StaplerRequest req, @QueryParameter String request
352352
if (startedTime <= since) {
353353
return new HttpResponse() {
354354
@Override
355-
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
355+
public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
356356
throws IOException, ServletException {
357357
rsp.addDateHeader("Last-Modified", startedTime);
358358
rsp.addHeader("Cache-control", "max-age=365000000, immutable, public");
@@ -378,7 +378,7 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod
378378
return new HttpResponse() {
379379

380380
@Override
381-
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
381+
public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
382382
throws IOException, ServletException {
383383
rsp.addDateHeader("Last-Modified", avatar.lastModified);
384384
rsp.addHeader("Cache-control", "max-age=3600, public");
@@ -585,7 +585,7 @@ private ImageResponse(BufferedImage image, boolean flushImage, long lastModified
585585
* {@inheritDoc}
586586
*/
587587
@Override
588-
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node)
588+
public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node)
589589
throws IOException, ServletException {
590590
ByteArrayOutputStream bos = new ByteArrayOutputStream();
591591
try {

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketEndpointConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
import org.apache.commons.lang.StringUtils;
4949
import org.kohsuke.accmod.Restricted;
5050
import org.kohsuke.accmod.restrictions.NoExternalUse;
51-
import org.kohsuke.stapler.StaplerRequest;
51+
import org.kohsuke.stapler.StaplerRequest2;
5252

5353
/**
5454
* Represents the global configuration of Bitbucket Cloud and Bitbucket Server endpoints.
@@ -138,7 +138,7 @@ public ListBoxModel getEndpointItems() {
138138
* {@inheritDoc}
139139
*/
140140
@Override
141-
public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
141+
public boolean configure(StaplerRequest2 req, JSONObject json) throws FormException {
142142
req.bindJSON(this, json);
143143
return true;
144144
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/hooks/BitbucketSCMSourcePushHookReceiver.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
import hudson.model.UnprotectedRootAction;
2828
import hudson.security.csrf.CrumbExclusion;
2929
import hudson.util.HttpResponses;
30+
import jakarta.servlet.FilterChain;
31+
import jakarta.servlet.ServletException;
32+
import jakarta.servlet.http.HttpServletRequest;
33+
import jakarta.servlet.http.HttpServletResponse;
3034
import java.io.IOException;
3135
import java.nio.charset.StandardCharsets;
3236
import java.util.logging.Level;
3337
import java.util.logging.Logger;
34-
import javax.servlet.FilterChain;
35-
import javax.servlet.ServletException;
36-
import javax.servlet.http.HttpServletRequest;
37-
import javax.servlet.http.HttpServletResponse;
3838
import jenkins.scm.api.SCMEvent;
3939
import org.apache.commons.io.IOUtils;
4040
import org.kohsuke.stapler.HttpResponse;
41-
import org.kohsuke.stapler.StaplerRequest;
41+
import org.kohsuke.stapler.StaplerRequest2;
4242

4343
/**
4444
* Process Bitbucket push and pull requests creations/updates hooks.
@@ -76,7 +76,7 @@ public String getUrlName() {
7676
* @return the HTTP response object
7777
* @throws IOException if there is any issue reading the HTTP content payload.
7878
*/
79-
public HttpResponse doNotify(StaplerRequest req) throws IOException {
79+
public HttpResponse doNotify(StaplerRequest2 req) throws IOException {
8080
String origin = SCMEvent.originOf(req);
8181
String body = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
8282
String eventKey = req.getHeader("X-Event-Key");
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package com.cloudbees.jenkins.plugins.bitbucket;
2+
3+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
4+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
5+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
6+
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
7+
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
8+
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey;
9+
import com.cloudbees.plugins.credentials.CredentialsProvider;
10+
import com.cloudbees.plugins.credentials.CredentialsScope;
11+
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
12+
import com.cloudbees.plugins.credentials.domains.Domain;
13+
import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl;
14+
import hudson.plugins.git.GitSCM;
15+
import hudson.plugins.git.UserRemoteConfig;
16+
import hudson.plugins.git.extensions.impl.BuildChooserSetting;
17+
import java.io.ByteArrayOutputStream;
18+
import java.nio.charset.StandardCharsets;
19+
import java.util.Arrays;
20+
import java.util.List;
21+
import java.util.Map;
22+
import jenkins.plugins.git.AbstractGitSCMSource;
23+
import jenkins.plugins.git.GitSCMSourceDefaults;
24+
import jenkins.plugins.git.GitSampleRepoRule;
25+
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
26+
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
27+
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
28+
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
29+
import org.junit.Rule;
30+
import org.junit.Test;
31+
import org.junit.runner.RunWith;
32+
import org.jvnet.hudson.test.Issue;
33+
import org.jvnet.hudson.test.JenkinsRule;
34+
import org.jvnet.hudson.test.recipes.WithTimeout;
35+
import org.mockito.junit.MockitoJUnitRunner;
36+
37+
import static org.hamcrest.MatcherAssert.assertThat;
38+
import static org.hamcrest.Matchers.containsInAnyOrder;
39+
import static org.hamcrest.Matchers.containsString;
40+
import static org.hamcrest.Matchers.instanceOf;
41+
import static org.hamcrest.Matchers.is;
42+
import static org.mockito.Mockito.mock;
43+
import static org.mockito.Mockito.when;
44+
45+
/**
46+
* Tests different scenarios of the
47+
* {@link BitbucketSCMSource#build(jenkins.scm.api.SCMHead, jenkins.scm.api.SCMRevision)} method.
48+
*/
49+
@RunWith(MockitoJUnitRunner.class)
50+
public class BitbucketSCMSourceBuildTest {
51+
52+
private static final String CLOUD_REPO_OWNER = "cloudbeers";
53+
private static final String REPO_NAME = "stunning-adventure";
54+
private static final String BRANCH_NAME = "master";
55+
// Test private key from ssh-credentials test case
56+
// https://github.com/jenkinsci/ssh-credentials-plugin/blob/343.v884f71d78167/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest/readOldCredentials/credentials.xml
57+
private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" +
58+
"MIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8\n" +
59+
"3d2BMLjhcw1zLsYG3FWpCyn8cB1OmjKiGjvnP5EBoAolvh3qOcWKyWlVGWGgs10B\n" +
60+
"0Cgnd3OBXRPQd1cBdiQZmmeCrrH0OjQefYIF2WYN+F8iuNGraAaRsXLgITanjTb1\n" +
61+
"6w1dnk+KLpU2J5G6kG1f/Qxl4pgny80S/3TktqoknbOrYDMOSA1Zdww39cpXJHp6\n" +
62+
"feEs8tavC93rOsR2O4ZfVUCjTFAF5FtIdRv3LXY3Q5W/AyY1h45Wk6mMVnEluFjB\n" +
63+
"aA+gzVAVaHFQfuhwoj4B7jWCmfHsPG1WmyK0YQIDAQABAoIBADt1qlXiMdV0mP9S\n" +
64+
"okdm6maQ8xTugKvyODWa+R1vSFHQqhwiNr927+xFkI9SAm8iu8SrjuWTIqF2O57m\n" +
65+
"WNnYjxB2dbyT29yVY+OH1P8M5cwTVsv1xYCJbdUUHEcs5akqPLWAyXteRHQq1+as\n" +
66+
"6cxNOov/PonHr55WNH7kLtLRMV54jZ68nrEh5pWdabFa0f7d/ByIvYRJm7lpjtSp\n" +
67+
"EBp5AseXzSg2EZP3HDPYYPDHK0tMPginz9+YuQCQFoMYAkVZoKFJGsWICktd5Uk7\n" +
68+
"wveOJLOfMng1Iww6CEc871GcLn5LbafLWRxjZssK2Z1fC+pZYLLZPeKDMSxoRXm6\n" +
69+
"PShUC1ECgYEA623dmwJNYfVRgAgOYhhcxzH4TAXUmUIpadEUjOkAhe3w/abDawFT\n" +
70+
"9ianhqfhTjSZGBtUttcN40Vy+P4bsqfQKZ7p6KzrdR2zWjlYcICWhZDZPGmMxpMZ\n" +
71+
"mUFhZXsLRVhn8qed8w1t6eju7S+t9satKIMC/KrhNsFzjrgbU9eC+m0CgYEAy7nN\n" +
72+
"gMwQeGxAQSJr9H7eKkthnjMe77rLIAZEbDJhcwYVz+Iie/E4hjESQ+IuvXa1VawD\n" +
73+
"O6cD0wWdOhH2McNdMNIbM4IOaO/TOaR5jfQwBWmb4iG2BZQWQQE/HUBnoJQWUhqm\n" +
74+
"b+D+s4bHh4J+bs+ptgg9Sd9V+VxJBcu2QDmI6UUCgYBTb1pMJyK5hrFdiH1gcnXe\n" +
75+
"+myetKpFrlby83AvCBxxWoQ/wKwc7hmNcOGKLVEB4E4pZvY83jZDx0cZyySRyjtR\n" +
76+
"pMoM9ct0dBQt84jORiQSLeVvLZEAhv1ZfPxBdLvn1Y7xRkoJ60Z60Vxrnqwueva/\n" +
77+
"Fr8mQIEUYLbNa53ztrrqeQKBgQCqOY4k2F3KwWjPA9wAZyFrZaEjdsOavBGNqK7z\n" +
78+
"WQVj/umq0eDOfzgjqE0Cu7MiTFYoR5pL9bmUUVSWePuliQANEwH3f+xackmkGHIY\n" +
79+
"0rhtTVkbEd/tuVb+6fO6lV4BJrufzvTS9sTbbPq7l6XdIVdE6o2LdDl6Kko5tYWL\n" +
80+
"FIf5oQKBgQDLHK/9NTb3VHp+Qriu2Vp8Pnaw6YF6pETfgjyrH2ULSW/R07v+AECC\n" +
81+
"sPr+d/hx2MQWp54HglY8lv98rOrRjMiRw1GVoXs+Ut9vkupmrpvzNE7ITl0tzBqD\n" +
82+
"sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n" +
83+
"-----END RSA PRIVATE KEY-----\n";
84+
85+
@Rule
86+
public JenkinsRule j = new JenkinsRule();
87+
88+
@Rule
89+
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();
90+
91+
92+
@Test
93+
@Issue("JENKINS-73471")
94+
@WithTimeout(120)
95+
public void buildWhenSetSSHCheckoutTraitThenEmptyAuthenticatorExtension() throws Exception {
96+
String jenkinsFile = "Jenkinsfile";
97+
sampleRepo.init();
98+
sampleRepo.write(jenkinsFile, "node { checkout scm }");
99+
sampleRepo.git("add", jenkinsFile);
100+
sampleRepo.git("commit", "--all", "--message=defined");
101+
102+
StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
103+
"user-pass", null, "user", "pass");
104+
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
105+
.addCredentials(Domain.global(), userPassCredentials);
106+
StandardUsernameCredentials sshCredentials = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "user-key", "user",
107+
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(PRIVATE_KEY), null, null);
108+
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
109+
.addCredentials(Domain.global(), sshCredentials);
110+
111+
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
112+
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
113+
instance.setOwner(owner);
114+
instance.setCredentialsId(userPassCredentials.getId());
115+
instance.setTraits(Arrays.asList(
116+
new BranchDiscoveryTrait(1),
117+
new SSHCheckoutTrait(sshCredentials.getId())));
118+
119+
BitbucketRepository repository = mock(BitbucketRepository.class);
120+
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
121+
new BitbucketHref("http", sampleRepo.toString()),
122+
new BitbucketHref("ssh", String.format("ssh://user@localhost/%s", sampleRepo))
123+
)));
124+
BitbucketApi client = mock(BitbucketApi.class);
125+
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
126+
when(client.getRepository()).thenReturn(repository);
127+
128+
BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
129+
AbstractGitSCMSource.SCMRevisionImpl revision =
130+
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
131+
GitSCM build = (GitSCM)instance.build(head, revision);
132+
assertThat(build.getUserRemoteConfigs().size(), is(1));
133+
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
134+
assertThat(remoteConfig.getUrl(), is(String.format("ssh://user@localhost/%s", sampleRepo)));
135+
assertThat(remoteConfig.getRefspec(), is(String.format("+refs/heads/%s:refs/remotes/origin/%s", BRANCH_NAME, BRANCH_NAME)));
136+
assertThat(remoteConfig.getCredentialsId(), is(sshCredentials.getId()));
137+
assertThat(build.getExtensions(), containsInAnyOrder(
138+
instanceOf(BuildChooserSetting.class),
139+
instanceOf(GitSCMSourceDefaults.class),
140+
instanceOf(GitClientAuthenticatorExtension.class))
141+
);
142+
143+
// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
144+
// Then check that the checkout uses GIT_SSH from the git-client logs
145+
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
146+
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
147+
WorkflowRun run = job.scheduleBuild2(0).get();
148+
149+
ByteArrayOutputStream byteArrayOutStr = new ByteArrayOutputStream();
150+
run.writeWholeLogTo(byteArrayOutStr);
151+
assertThat(byteArrayOutStr.toString(StandardCharsets.UTF_8), containsString("using GIT_SSH to set credentials"));
152+
}
153+
154+
@Test
155+
@WithTimeout(120)
156+
public void buildBasicAuthThenAuthenticatorExtension() throws Exception {
157+
String jenkinsFile = "Jenkinsfile";
158+
sampleRepo.init();
159+
sampleRepo.write(jenkinsFile, "node { checkout scm }");
160+
sampleRepo.git("add", jenkinsFile);
161+
sampleRepo.git("commit", "--all", "--message=defined");
162+
163+
StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
164+
"user-pass", null, "user", "pass");
165+
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
166+
.addCredentials(Domain.global(), userPassCredentials);
167+
168+
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
169+
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
170+
instance.setOwner(owner);
171+
instance.setCredentialsId(userPassCredentials.getId());
172+
instance.setTraits(List.of(new BranchDiscoveryTrait(1)));
173+
174+
BitbucketRepository repository = mock(BitbucketRepository.class);
175+
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
176+
new BitbucketHref("http", sampleRepo.toString()),
177+
new BitbucketHref("ssh", String.format("ssh://localhost:%s", sampleRepo))
178+
)));
179+
BitbucketServerAPIClient client = mock(BitbucketServerAPIClient.class);
180+
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
181+
when(client.getRepository()).thenReturn(repository);
182+
183+
BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
184+
AbstractGitSCMSource.SCMRevisionImpl revision =
185+
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
186+
GitSCM build = (GitSCM)instance.build(head, revision);
187+
assertThat(build.getUserRemoteConfigs().size(), is(1));
188+
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
189+
assertThat(remoteConfig.getUrl(), is(sampleRepo.toString()));
190+
assertThat(remoteConfig.getRefspec(), is(String.format("+refs/heads/%s:refs/remotes/origin/%s", BRANCH_NAME, BRANCH_NAME)));
191+
assertThat(remoteConfig.getCredentialsId(), is(userPassCredentials.getId()));
192+
assertThat(build.getExtensions(), containsInAnyOrder(
193+
instanceOf(BuildChooserSetting.class),
194+
instanceOf(GitSCMSourceDefaults.class),
195+
instanceOf(GitClientAuthenticatorExtension.class)
196+
));
197+
198+
// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
199+
// Then check that the checkout scm uses GIT_ASKPASS from the git-client logs
200+
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
201+
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
202+
WorkflowRun run = job.scheduleBuild2(0).get();
203+
204+
ByteArrayOutputStream byteArrayOutStr = new ByteArrayOutputStream();
205+
run.writeWholeLogTo(byteArrayOutStr);
206+
assertThat(byteArrayOutStr.toString(StandardCharsets.UTF_8), containsString("using GIT_ASKPASS to set credentials"));
207+
}
208+
}

0 commit comments

Comments
 (0)