Skip to content

Replace the avatar cache beta implementation with api provided by the scm-api plugin #978

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

Merged
merged 1 commit into from
Feb 10, 2025
Merged
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
4 changes: 3 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@
<gitHubRepo>jenkinsci/bitbucket-branch-source-plugin</gitHubRepo>
<jenkins.baseline>2.479</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.1</jenkins.version>
<hpi.compatibleSinceVersion>2.0</hpi.compatibleSinceVersion>
<hpi.compatibleSinceVersion>934.0</hpi.compatibleSinceVersion>
<!-- Jenkins.MANAGE is still in Beta -->
<useBeta>true</useBeta>
<tagNameFormat>@{project.version}</tagNameFormat>
<!-- because eclipse generate a JUnit5 run configuration with placeholder not replaced in the jvm argument section! -->
<surefire.forkNumber>1</surefire.forkNumber>
</properties>

<developers>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,19 @@
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApiFactory;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketCloudWorkspace;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketTeam;
import com.cloudbees.jenkins.plugins.bitbucket.client.repository.UserRoleInRepository;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.AbstractBitbucketEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.impl.avatars.BitbucketTeamAvatarMetadataAction;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketCredentials;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.MirrorListSupplier;
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerProject;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ShowBitbucketAvatarTrait;
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import edu.umd.cs.findbugs.annotations.CheckForNull;
Expand Down Expand Up @@ -323,7 +325,7 @@
@RestrictedSince("2.2.0")
@DataBoundSetter
public void setAutoRegisterHooks(boolean autoRegisterHook) {
traits.removeIf(trait -> trait instanceof WebhookRegistrationTrait);
traits.removeIf(WebhookRegistrationTrait.class::isInstance);
traits.add(new WebhookRegistrationTrait(
autoRegisterHook ? WebhookRegistration.ITEM : WebhookRegistration.DISABLE
));
Expand All @@ -334,8 +336,8 @@
@RestrictedSince("2.2.0")
public boolean isAutoRegisterHooks() {
for (SCMTrait<? extends SCMTrait<?>> t : traits) {
if (t instanceof WebhookRegistrationTrait) {
return ((WebhookRegistrationTrait) t).getMode() != WebhookRegistration.DISABLE;
if (t instanceof WebhookRegistrationTrait hookTrait) {
return hookTrait.getMode() != WebhookRegistration.DISABLE;
}
}
return true;
Expand All @@ -348,8 +350,8 @@
@NonNull
public String getCheckoutCredentialsId() {
for (SCMTrait<?> t : traits) {
if (t instanceof SSHCheckoutTrait) {
return StringUtils.defaultString(((SSHCheckoutTrait) t).getCredentialsId(), BitbucketSCMSource
if (t instanceof SSHCheckoutTrait sshTrait) {
return StringUtils.defaultString(sshTrait.getCredentialsId(), BitbucketSCMSource
.DescriptorImpl.ANONYMOUS);
}
}
Expand All @@ -361,7 +363,7 @@
@RestrictedSince("2.2.0")
@DataBoundSetter
public void setCheckoutCredentialsId(String checkoutCredentialsId) {
traits.removeIf(trait -> trait instanceof SSHCheckoutTrait);
traits.removeIf(SSHCheckoutTrait.class::isInstance);
if (checkoutCredentialsId != null && !BitbucketSCMSource.DescriptorImpl.SAME.equals(checkoutCredentialsId)) {
traits.add(new SSHCheckoutTrait(checkoutCredentialsId));
}
Expand All @@ -372,8 +374,8 @@
@RestrictedSince("2.2.0")
public String getPattern() {
for (SCMTrait<?> trait : traits) {
if (trait instanceof RegexSCMSourceFilterTrait) {
return ((RegexSCMSourceFilterTrait) trait).getRegex();
if (trait instanceof RegexSCMSourceFilterTrait regexTrait) {
return regexTrait.getRegex();
}
}
return ".*";
Expand Down Expand Up @@ -421,8 +423,8 @@
@NonNull
public String getIncludes() {
for (SCMTrait<?> trait : traits) {
if (trait instanceof WildcardSCMHeadFilterTrait) {
return ((WildcardSCMHeadFilterTrait) trait).getIncludes();
if (trait instanceof WildcardSCMHeadFilterTrait wildcardTrait) {
return wildcardTrait.getIncludes();
}
}
return "*";
Expand All @@ -435,12 +437,11 @@
public void setIncludes(@NonNull String includes) {
for (int i = 0; i < traits.size(); i++) {
SCMTrait<?> trait = traits.get(i);
if (trait instanceof WildcardSCMHeadFilterTrait) {
WildcardSCMHeadFilterTrait existing = (WildcardSCMHeadFilterTrait) trait;
if ("*".equals(includes) && "".equals(existing.getExcludes())) {
if (trait instanceof WildcardSCMHeadFilterTrait wildcardTrait) {
if ("*".equals(includes) && "".equals(wildcardTrait.getExcludes())) {

Check notice

Code scanning / CodeQL

Inefficient empty string test Note

Inefficient comparison to empty string, check for zero length instead.
traits.remove(i);
} else {
traits.set(i, new WildcardSCMHeadFilterTrait(includes, existing.getExcludes()));
traits.set(i, new WildcardSCMHeadFilterTrait(includes, wildcardTrait.getExcludes()));
}
return;
}
Expand All @@ -456,8 +457,8 @@
@NonNull
public String getExcludes() {
for (SCMTrait<?> trait : traits) {
if (trait instanceof WildcardSCMHeadFilterTrait) {
return ((WildcardSCMHeadFilterTrait) trait).getExcludes();
if (trait instanceof WildcardSCMHeadFilterTrait wildcardTrait) {
return wildcardTrait.getExcludes();
}
}
return "";
Expand All @@ -470,12 +471,11 @@
public void setExcludes(@NonNull String excludes) {
for (int i = 0; i < traits.size(); i++) {
SCMTrait<?> trait = traits.get(i);
if (trait instanceof WildcardSCMHeadFilterTrait) {
WildcardSCMHeadFilterTrait existing = (WildcardSCMHeadFilterTrait) trait;
if ("*".equals(existing.getIncludes()) && "".equals(excludes)) {
if (trait instanceof WildcardSCMHeadFilterTrait wildcardTrait) {
if ("*".equals(wildcardTrait.getIncludes()) && "".equals(excludes)) {

Check notice

Code scanning / CodeQL

Inefficient empty string test Note

Inefficient comparison to empty string, check for zero length instead.
traits.remove(i);
} else {
traits.set(i, new WildcardSCMHeadFilterTrait(existing.getIncludes(), excludes));
traits.set(i, new WildcardSCMHeadFilterTrait(wildcardTrait.getIncludes(), excludes));
}
return;
}
Expand Down Expand Up @@ -569,37 +569,39 @@

BitbucketAuthenticator authenticator = AuthenticationTokens.convert(BitbucketAuthenticator.authenticationContext(serverUrl), credentials);

BitbucketApi bitbucket = BitbucketApiFactory.newInstance(serverUrl, authenticator, repoOwner, null, null);
BitbucketTeam team = bitbucket.getTeam();
if (team != null) {
String defaultTeamUrl;
if (team instanceof BitbucketServerProject) {
defaultTeamUrl = serverUrl + "/projects/" + team.getName();
try (BitbucketApi client = BitbucketApiFactory.newInstance(serverUrl, authenticator, repoOwner, projectKey, null)) {
BitbucketTeam team = client.getTeam();
String avatarURL = null;
String teamURL;
String teamDisplayName;
if (team != null) {
if (showAvatar()) {

Check warning on line 578 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 578 is only partially covered, one branch is missing
avatarURL = team.getAvatar();

Check warning on line 579 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 579 is not covered by tests
}
teamURL = team.getLink("html");
teamDisplayName = StringUtils.defaultIfBlank(team.getDisplayName(), team.getName());
if (StringUtils.isNotBlank(teamURL)) {

Check warning on line 583 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 583 is only partially covered, one branch is missing
if (team instanceof BitbucketCloudWorkspace wks) {
teamURL = serverUrl + "/" + wks.getSlug();
} else {
teamURL = serverUrl + "/projects/" + team.getName();

Check warning on line 587 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 584-587 are not covered by tests
}
}
listener.getLogger().printf("Team: %s%n", HyperlinkNote.encodeTo(teamURL, teamDisplayName));
} else {
defaultTeamUrl = serverUrl + "/" + team.getName();
teamURL = serverUrl + "/" + repoOwner;
teamDisplayName = repoOwner;

Check warning on line 593 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 592-593 are not covered by tests
listener.getLogger().println("Could not resolve team details");
}
String teamUrl = StringUtils.defaultIfBlank(team.getLink("html"), defaultTeamUrl);
String teamDisplayName = StringUtils.defaultIfBlank(team.getDisplayName(), team.getName());
result.add(new ObjectMetadataAction(
teamDisplayName,
null,
teamUrl
));
result.add(new BitbucketTeamMetadataAction(serverUrl, credentials, team.getName()));
result.add(new BitbucketLink("icon-bitbucket-logo", teamUrl));
listener.getLogger().printf("Team: %s%n", HyperlinkNote.encodeTo(teamUrl, teamDisplayName));
} else {
String teamUrl = serverUrl + "/" + repoOwner;
result.add(new ObjectMetadataAction(
repoOwner,
null,
teamUrl
));
result.add(new BitbucketTeamMetadataAction(null, null, null));
result.add(new BitbucketLink("icon-bitbucket-logo", teamUrl));
listener.getLogger().println("Could not resolve team details");
result.add(new ObjectMetadataAction(teamDisplayName, null, teamURL));
result.add(new BitbucketTeamAvatarMetadataAction(avatarURL, serverUrl, owner.getFullName(), credentialsId));
result.add(new BitbucketLink("icon-bitbucket-logo", teamURL));
return result;
}
return result;
}

private boolean showAvatar() {
return traits.stream().anyMatch(ShowBitbucketAvatarTrait.class::isInstance);
}

@Symbol("bitbucket")
Expand Down Expand Up @@ -629,7 +631,6 @@
return "icon-bitbucket-scm-navigator";
}

@SuppressWarnings("unchecked")
@Override
public SCMNavigator newInstance(String name) {
BitbucketSCMNavigator instance = new BitbucketSCMNavigator(StringUtils.defaultString(name));
Expand Down Expand Up @@ -708,7 +709,7 @@
}
}
List<NamedArrayList<? extends SCMTraitDescriptor<?>>> result = new ArrayList<>();
NamedArrayList.select(all, "Repositories", it -> it instanceof SCMNavigatorTraitDescriptor, true, result);
NamedArrayList.select(all, "Repositories", SCMNavigatorTraitDescriptor.class::isInstance, true, result);

Check warning on line 712 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMNavigator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 712 is not covered by tests
NamedArrayList.select(all, "Within repository", NamedArrayList
.anyOf(NamedArrayList.withAnnotation(Discovery.class),
NamedArrayList.withAnnotation(Selection.class)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.hooks.HasPullRequests;
import com.cloudbees.jenkins.plugins.bitbucket.impl.avatars.BitbucketRepoAvatarMetadataAction;
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.BitbucketEnvVarExtension;
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.GitClientAuthenticatorExtension;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.BitbucketApiUtils;
Expand All @@ -53,6 +54,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
import com.cloudbees.jenkins.plugins.bitbucket.trait.ShowBitbucketAvatarTrait;
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.damnhandy.uri.template.UriTemplate;
Expand All @@ -65,12 +67,9 @@
import hudson.RestrictedSince;
import hudson.Util;
import hudson.console.HyperlinkNote;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.Action;
import hudson.model.Actionable;
import hudson.model.Item;
import hudson.model.Items;
import hudson.model.TaskListener;
import hudson.plugins.git.GitSCM;
import hudson.scm.SCM;
Expand All @@ -97,7 +96,6 @@
import java.util.logging.Logger;
import jenkins.authentication.tokens.api.AuthenticationTokens;
import jenkins.model.Jenkins;
import jenkins.plugins.git.MergeWithGitSCMExtension;
import jenkins.plugins.git.traits.GitBrowserSCMSourceTrait;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadCategory;
Expand Down Expand Up @@ -152,14 +150,6 @@
private static final String CLOUD_REPO_TEMPLATE = "{/owner,repo}";
private static final String SERVER_REPO_TEMPLATE = "/projects{/owner}/repos{/repo}";

/**
* Mapping classes after refactoring for backward compatibility.
*/
@Initializer(before = InitMilestone.PLUGINS_STARTED)
public static void aliases() {
Items.XSTREAM2.addCompatibilityAlias("com.cloudbees.jenkins.plugins.bitbucket.MergeWithGitSCMExtension", MergeWithGitSCMExtension.class);
}

/** How long to delay events received from Bitbucket in order to allow the API caches to sync. */
private static /*mostly final*/ int eventDelaySeconds =
Math.min(
Expand Down Expand Up @@ -1105,31 +1095,36 @@
protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event,
@NonNull TaskListener listener)
throws IOException, InterruptedException {
// TODO when we have support for trusted events, use the details from event if event was from trusted source

Check warning on line 1098 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: when we have support for trusted events, use the details from event if event was from trusted source
List<Action> result = new ArrayList<>();
final BitbucketApi bitbucket = buildBitbucketClient();
gatherPrimaryCloneLinks(bitbucket);
BitbucketRepository r = bitbucket.getRepository();
result.add(new BitbucketRepoMetadataAction(r));
String defaultBranch = bitbucket.getDefaultBranch();
if (StringUtils.isNotBlank(defaultBranch)) {
result.add(new BitbucketDefaultBranch(repoOwner, repository, defaultBranch));
try (BitbucketApi bitbucket = buildBitbucketClient()) {
gatherPrimaryCloneLinks(bitbucket);
BitbucketRepository repo = bitbucket.getRepository();
result.add(new BitbucketRepoAvatarMetadataAction(showAvatar() ? repo : null));
String defaultBranch = bitbucket.getDefaultBranch();
if (StringUtils.isNotBlank(defaultBranch)) {
result.add(new BitbucketDefaultBranch(repoOwner, repository, defaultBranch));
}
UriTemplate template;
if (BitbucketApiUtils.isCloud(getServerUrl())) {
template = UriTemplate.fromTemplate(getServerUrl() + CLOUD_REPO_TEMPLATE);
} else {
template = UriTemplate.fromTemplate(getServerUrl() + SERVER_REPO_TEMPLATE);
}
String url = template
.set("owner", repoOwner)
.set("repo", repository)
.expand();
result.add(new BitbucketLink("icon-bitbucket-repo", url));
result.add(new ObjectMetadataAction(repo.getRepositoryName(), null, url));
}
UriTemplate template;
if (BitbucketApiUtils.isCloud(getServerUrl())) {
template = UriTemplate.fromTemplate(getServerUrl() + CLOUD_REPO_TEMPLATE);
} else {
template = UriTemplate.fromTemplate(getServerUrl() + SERVER_REPO_TEMPLATE);
}
String url = template
.set("owner", repoOwner)
.set("repo", repository)
.expand();
result.add(new BitbucketLink("icon-bitbucket-repo", url));
result.add(new ObjectMetadataAction(r.getRepositoryName(), null, url));
return result;
}

private boolean showAvatar() {
return traits.stream().anyMatch(ShowBitbucketAvatarTrait.class::isInstance);
}

@NonNull
@Override
protected List<Action> retrieveActions(@NonNull SCMHead head,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,6 @@
*/
@CheckForNull
private Iterable<BitbucketBranch> branches;
// TODO private Iterable<BitbucketTag> tags;
/**
* The BitbucketApi that is used for the request.
*/
Expand Down Expand Up @@ -164,13 +163,13 @@
for (SCMHead h : includes) {
if (h instanceof BranchSCMHead) {
branchNames.add(h.getName());
} else if (h instanceof PullRequestSCMHead) {
pullRequestNumbers.add(((PullRequestSCMHead) h).getId());
} else if (h instanceof PullRequestSCMHead prHead) {
pullRequestNumbers.add(prHead.getId());
if (SCMHeadOrigin.DEFAULT.equals(h.getOrigin())) {
branchNames.add(((PullRequestSCMHead) h).getOriginName());
branchNames.add(prHead.getOriginName());
}
if (((PullRequestSCMHead) h).getCheckoutStrategy() == ChangeRequestCheckoutStrategy.MERGE) {
branchNames.add(((PullRequestSCMHead) h).getTarget().getName());
if (prHead.getCheckoutStrategy() == ChangeRequestCheckoutStrategy.MERGE) {
branchNames.add(prHead.getTarget().getName());

Check warning on line 172 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 166-172 are not covered by tests
}
} else if (h instanceof BitbucketTagSCMHead) {
tagNames.add(h.getName());
Expand Down Expand Up @@ -431,11 +430,11 @@
*/
@Override
public void close() throws IOException {
if (pullRequests instanceof Closeable) {
((Closeable) pullRequests).close();
if (pullRequests instanceof Closeable closable) {

Check warning on line 433 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 433 is only partially covered, one branch is missing
closable.close();

Check warning on line 434 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 434 is not covered by tests
}
if (branches instanceof Closeable) {
((Closeable) branches).close();
if (branches instanceof Closeable closable) {

Check warning on line 436 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 436 is only partially covered, one branch is missing
closable.close();

Check warning on line 437 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSourceRequest.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 437 is not covered by tests
}
super.close();
}
Expand Down
Loading