Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 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
29 changes: 18 additions & 11 deletions src/main/java/hudson/plugins/git/GitSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@
import org.jenkinsci.plugins.gitclient.*;
import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted;
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.StaplerRequest2;
Expand Down Expand Up @@ -368,7 +370,7 @@ public void setBrowser(GitRepositoryBrowser browser) {
+ "/*" // optional trailing '/'
;

private static final Pattern[] URL_PATTERNS = {
public static final Pattern[] URL_PATTERNS = {
/* URL style - like https://github.com/jenkinsci/git-plugin */
Pattern.compile(
"(?:\\w+://)" // protocol (scheme)
Expand Down Expand Up @@ -407,21 +409,26 @@ public void setBrowser(GitRepositoryBrowser browser) {
}
if (webUrls.size() == 1) {
String url = webUrls.iterator().next();
if (url.startsWith("https://bitbucket.org/")) {
return new BitbucketWeb(url);
}
if (url.startsWith("https://gitlab.com/")) {
return new GitLab(url);
}
if (url.startsWith("https://github.com/")) {
return new GithubWeb(url);
}
return null;
return guessBrowser(url);
}
LOGGER.log(Level.INFO, "Multiple browser guess matches for {0}", remoteRepositories);
return null;
}

@Restricted(NoExternalUse.class)
public static GitRepositoryBrowser guessBrowser(String url) {
if (url.startsWith("https://bitbucket.org/")) {
return new BitbucketWeb(url);
}
if (url.startsWith("https://gitlab.com/")) {
return new GitLab(url);
}
if (url.startsWith("https://github.com/")) {
return new GithubWeb(url);
}
return null;
}

public boolean isCreateAccountBasedOnEmail() {
DescriptorImpl gitDescriptor = getDescriptor();
return (gitDescriptor != null && gitDescriptor.isCreateAccountBasedOnEmail());
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import hudson.plugins.git.util.BuildChooserContext;
import hudson.plugins.git.util.BuildData;
import hudson.plugins.git.util.GitUtils;
import hudson.scm.RepositoryBrowser;
import hudson.scm.SCM;
import hudson.security.ACL;
import java.io.File;
Expand All @@ -75,6 +76,7 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jenkins.model.Jenkins;
import jenkins.plugins.git.traits.GitBrowserSCMSourceTrait;
Expand Down Expand Up @@ -223,6 +225,34 @@ public GitRepositoryBrowser getBrowser() {
return trait != null ? trait.getBrowser() : null;
}

@CheckForNull
public GitRepositoryBrowser guessBrowser() {
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure about this, I couldn't find any way to get to the Repository Browser from a SCMSource.

The existing getBrowser() in this class just returns null.
Javadoc says it should then fallback to the auto browser, which seems to no longer exist / is disabled by default.

I've copied the guess implementation from GitSCM as I can't see a way to go from a SCMSource to an SCM.

WorkflowRun does have a getSCMs method and its possible to get there that way.

GitBrowserSCMSourceTrait trait = SCMTrait.find(getTraits(), GitBrowserSCMSourceTrait.class);
if (trait != null) {
return trait.getBrowser();
}

Set<String> webUrls = new HashSet<>();
String remote = getRemote();
if (remote != null) {
for (Pattern p : GitSCM.URL_PATTERNS) {
Matcher m = p.matcher(remote);
if (m.matches()) {
webUrls.add("https://" + m.group(1) + "/" + m.group(2) + "/");
}
}
}
if (webUrls.isEmpty()) {
return null;
}
if (webUrls.size() == 1) {
String url = webUrls.iterator().next();
return GitSCM.guessBrowser(url);
}
LOGGER.log(Level.INFO, "Multiple browser guess matches for {0}", remote);
return null;
}

/**
* Gets Git tool to be used for this SCM Source.
* @return Git Tool or {@code null} if the default tool should be used.
Expand Down
77 changes: 77 additions & 0 deletions src/main/java/jenkins/plugins/git/GitCommitDetail.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package jenkins.plugins.git;

import hudson.model.Run;
import hudson.plugins.git.browser.GitRepositoryBrowser;
import java.io.IOException;
import java.net.URL;
import jenkins.model.details.Detail;
import jenkins.model.details.DetailGroup;
import jenkins.scm.api.SCMDetailGroup;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;

public class GitCommitDetail extends Detail {
private final GitRepositoryBrowser repositoryBrowser;

public GitCommitDetail(Run run, GitRepositoryBrowser repositoryBrowser) {
super(run);
this.repositoryBrowser = repositoryBrowser;
}

public String getIconClassName() {
return getDisplayName() == null ? null : "symbol-git-commit plugin-ionicons-api";
}

@Override
public String getDisplayName() {
SCMRevision revision = getRevision();

if (revision == null) {
return null;
}

if (revision instanceof AbstractGitSCMSource.SCMRevisionImpl abstractRevision) {
return abstractRevision.getHash().substring(0, 7);
}

return null;
}

@Override
public String getLink() {
SCMRevision revision = getRevision();

if (revision == null) {
return null;
}

if (revision instanceof AbstractGitSCMSource.SCMRevisionImpl abstractRevision && repositoryBrowser != null) {
String hash = abstractRevision.getHash();

try {
URL changeSetLink = repositoryBrowser.getChangeSetLink(hash);
return changeSetLink != null ? changeSetLink.toString() : null;
} catch (IOException e) {
return null;
}
}

return null;
}

@Override
public DetailGroup getGroup() {
return SCMDetailGroup.get();
}

private SCMRevision getRevision() {
SCMRevisionAction scmRevisionAction = getObject().getAction(SCMRevisionAction.class);


if (scmRevisionAction == null) {
return null;
}

return scmRevisionAction.getRevision();
}
}
42 changes: 42 additions & 0 deletions src/main/java/jenkins/plugins/git/GitDetailFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package jenkins.plugins.git;

import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.model.Run;
import hudson.plugins.git.browser.GitRepositoryBrowser;
import java.util.Collections;
import java.util.List;
import jenkins.model.details.Detail;
import jenkins.model.details.DetailFactory;
import jenkins.scm.api.SCMRevisionAction;
import jenkins.scm.api.SCMSource;

@Extension
public final class GitDetailFactory extends DetailFactory<Run> {

@Override
public Class<Run> type() {
return Run.class;
}

@NonNull
@Override
public List<? extends Detail> createFor(@NonNull Run target) {
SCMSource src = SCMSource.SourceByItem.findSource(target.getParent());

if (src instanceof AbstractGitSCMSource gitSource) {
SCMRevisionAction scmRevisionAction = target.getAction(SCMRevisionAction.class);

GitRepositoryBrowser repositoryBrowser = gitSource.guessBrowser();

if (scmRevisionAction == null) {
return Collections.emptyList();
}

return List.of(new GitCommitDetail(target, repositoryBrowser));
} else {
// Don't add details for non-Git SCM sources
return Collections.emptyList();
}
}
}