Skip to content
Draft
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
3 changes: 3 additions & 0 deletions src/main/java/jenkins/plugins/git/GitSCMFileSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,9 @@
GitClient.CREDENTIALS_MATCHER
)
);
if (_build != null && credential != null && credential.forRun(_build) instanceof StandardCredentials standardCredential) {

Check warning on line 384 in src/main/java/jenkins/plugins/git/GitSCMFileSystem.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 384 is only partially covered, 3 branches are missing
credential = standardCredential;
}
Comment on lines +384 to +386

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: No sure if GitSCMTelescope should contextualize the credentials similarly?

Copy link
Member Author

@dwnusbaum dwnusbaum Sep 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I looked into that, but IDK. There is also this credentials lookup in AbstractGitSCMSource which won't work, and I don't think it can be made to work:

return CredentialsMatchers
.firstOrNull(
CredentialsProvider.lookupCredentialsInItem(StandardUsernameCredentials.class, context,
ACL.SYSTEM2, URIRequirementBuilder.fromUri(getRemote()).build()),
CredentialsMatchers.allOf(CredentialsMatchers.withId(credentialsId),
GitClient.CREDENTIALS_MATCHER));

Going by https://github.com/search?type=code&q=+owner%3Ajenkinsci+gitscmtelescope, I think the answer here would just be that GitSCMSource doesn't support inference-based options for GitHubAppCredentials, you must use GitHubSCMSource. IDK if there would ever be a reason that you would have to use GitSCMSource over GitHubSCMSource, but I don't think so.

For GitSCM and its use of GitSCMFileSystem, the situation is different, since there is no GitHubSCM.

client.addDefaultCredentials(credential);
CredentialsProvider.track(owner, credential);
}
Expand Down
71 changes: 71 additions & 0 deletions src/test/java/jenkins/plugins/git/GitSCMFileSystemTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,18 @@

package jenkins.plugins.git;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
import hudson.EnvVars;
import hudson.Extension;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.plugins.git.BranchSpec;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.GitException;
import hudson.util.Secret;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Collections;
Expand All @@ -47,12 +54,15 @@
import org.eclipse.jgit.lib.ObjectId;
import org.jenkinsci.plugins.gitclient.Git;
import org.jenkinsci.plugins.gitclient.GitClient;
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.kohsuke.stapler.DataBoundConstructor;

import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
Expand Down Expand Up @@ -477,6 +487,67 @@ public void null_pointer_exception() throws Exception {
assertEquals(Constants.R_HEADS, result1.prefix);
}

@Test
public void filesystem_supports_credential_contextualization() throws Exception {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: I confirm that the test fails without the credentials contextualization.

sampleRepo.init();
sampleRepo.git("checkout", "-b", "dev");
sampleRepo.write("Jenkinsfile", "echo 'Hello, world!'");
sampleRepo.git("add", "Jenkinsfile");
sampleRepo.git("commit", "--all", "--message=dev");
var store = CredentialsProvider.lookupStores(r.jenkins).iterator().next();
store.addCredentials(Domain.global(), new ContextualizableCredentials("my-creds"));
var job = r.createProject(WorkflowJob.class);
var scm = new GitSCM(
GitSCM.createRepoList(sampleRepo.toString(), "my-creds"),
Collections.singletonList(new BranchSpec("*/dev")),
null,
null,
Collections.emptyList());
var flowDefinition = new CpsScmFlowDefinition(scm, "Jenkinsfile");
flowDefinition.setLightweight(true); // Triggers use of GitSCMFileSystem
job.setDefinition(flowDefinition);
var run = r.buildAndAssertSuccess(job);
}

private static class ContextualizableCredentials extends BaseStandardCredentials implements StandardUsernamePasswordCredentials {
private final boolean contextualized;

@DataBoundConstructor
public ContextualizableCredentials(String id) {
super(id, null);
this.contextualized = false;
}

private ContextualizableCredentials(ContextualizableCredentials base) {
super(base.getId(), null);
this.contextualized = true;
}

@Override
public String getUsername() {
if (contextualized) {
return "username";
}
throw new IllegalStateException("Requires contextualization");
}

@Override
public Secret getPassword() {
if (contextualized) {
return Secret.fromString("s3cr3t");
}
throw new IllegalStateException("Requires contextualization");
}

@Override
public ContextualizableCredentials forRun(Run<?, ?> run) {
return new ContextualizableCredentials(this);
}

@Extension
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {}
}

/** inline ${@link hudson.Functions#isWindows()} to prevent a transient remote classloader issue */
private boolean isWindows() {
return java.io.File.pathSeparatorChar==';';
Expand Down
Loading