Java tutorial
/* * The MIT License * * Copyright (c) 2016-2017, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.cloudbees.jenkins.plugins.bitbucket; 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.BitbucketHref; 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.server.client.repository.BitbucketServerProject; import com.cloudbees.plugins.credentials.CredentialsNameProvider; import com.cloudbees.plugins.credentials.common.StandardCredentials; import com.damnhandy.uri.template.UriTemplate; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.RestrictedSince; import hudson.Util; import hudson.console.HyperlinkNote; import hudson.model.Action; import hudson.model.TaskListener; import hudson.plugins.git.GitSCM; import hudson.plugins.mercurial.MercurialSCM; import hudson.plugins.mercurial.traits.MercurialBrowserSCMSourceTrait; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import java.io.IOException; import java.io.ObjectStreamException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.authentication.tokens.api.AuthenticationTokens; import jenkins.model.Jenkins; import jenkins.plugins.git.traits.GitBrowserSCMSourceTrait; import jenkins.scm.api.SCMNavigator; import jenkins.scm.api.SCMNavigatorDescriptor; import jenkins.scm.api.SCMNavigatorEvent; import jenkins.scm.api.SCMNavigatorOwner; import jenkins.scm.api.SCMSource; import jenkins.scm.api.SCMSourceCategory; import jenkins.scm.api.SCMSourceObserver; import jenkins.scm.api.SCMSourceOwner; import jenkins.scm.api.metadata.ObjectMetadataAction; import jenkins.scm.api.mixin.ChangeRequestCheckoutStrategy; import jenkins.scm.api.trait.SCMNavigatorRequest; import jenkins.scm.api.trait.SCMNavigatorTrait; import jenkins.scm.api.trait.SCMNavigatorTraitDescriptor; import jenkins.scm.api.trait.SCMSourceTrait; import jenkins.scm.api.trait.SCMTrait; import jenkins.scm.api.trait.SCMTraitDescriptor; import jenkins.scm.impl.UncategorizedSCMSourceCategory; import jenkins.scm.impl.form.NamedArrayList; import jenkins.scm.impl.trait.Discovery; import jenkins.scm.impl.trait.RegexSCMSourceFilterTrait; import jenkins.scm.impl.trait.Selection; import jenkins.scm.impl.trait.WildcardSCMHeadFilterTrait; import org.apache.commons.lang.StringUtils; import org.jenkins.ui.icon.Icon; import org.jenkins.ui.icon.IconSet; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; public class BitbucketSCMNavigator extends SCMNavigator { private static final Logger LOGGER = Logger.getLogger(BitbucketSCMSource.class.getName()); @NonNull private String serverUrl; @CheckForNull private String credentialsId; @NonNull private final String repoOwner; @NonNull private List<SCMTrait<? extends SCMTrait<?>>> traits; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient String checkoutCredentialsId; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient String pattern; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient boolean autoRegisterHooks; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient String includes; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient String excludes; @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") private transient String bitbucketServerUrl; @DataBoundConstructor public BitbucketSCMNavigator(String repoOwner) { this.serverUrl = BitbucketCloudEndpoint.SERVER_URL; this.repoOwner = repoOwner; this.traits = new ArrayList<>(); this.credentialsId = null; // highlighting the default is anonymous unless you configure explicitly } @Deprecated // retained for binary compatibility public BitbucketSCMNavigator(String repoOwner, String credentialsId, String checkoutCredentialsId) { this.serverUrl = BitbucketCloudEndpoint.SERVER_URL; this.repoOwner = repoOwner; this.traits = new ArrayList<>(); this.credentialsId = Util.fixEmpty(credentialsId); // code invoking legacy constructor will want the legacy discovery model this.traits.add(new BranchDiscoveryTrait(true, true)); this.traits.add(new OriginPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.HEAD))); this.traits.add(new ForkPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.HEAD), new ForkPullRequestDiscoveryTrait.TrustEveryone())); this.traits.add(new PublicRepoPullRequestFilterTrait()); if (checkoutCredentialsId != null && !BitbucketSCMSource.DescriptorImpl.SAME.equals(checkoutCredentialsId)) { this.traits.add(new SSHCheckoutTrait(checkoutCredentialsId)); } } @SuppressWarnings({ "ConstantConditions", "deprecation" }) @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification = "Only non-null after we set them here!") private Object readResolve() throws ObjectStreamException { if (serverUrl == null) { serverUrl = BitbucketEndpointConfiguration.get().readResolveServerUrl(bitbucketServerUrl); } if (serverUrl == null) { LOGGER.log(Level.WARNING, "BitbucketSCMNavigator::readResolve : serverUrl is still empty"); } if (traits == null) { // legacy instance, reconstruct traits to reflect legacy behaviour traits = new ArrayList<>(); this.traits.add(new BranchDiscoveryTrait(true, true)); this.traits.add(new OriginPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.HEAD))); this.traits.add(new ForkPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.HEAD), new ForkPullRequestDiscoveryTrait.TrustEveryone())); this.traits.add(new PublicRepoPullRequestFilterTrait()); if ((includes != null && !"*".equals(includes)) || (excludes != null && !"".equals(excludes))) { traits.add(new WildcardSCMHeadFilterTrait(StringUtils.defaultIfBlank(includes, "*"), StringUtils.defaultIfBlank(excludes, ""))); } if (checkoutCredentialsId != null && !BitbucketSCMSource.DescriptorImpl.SAME.equals(checkoutCredentialsId) && !checkoutCredentialsId.equals(credentialsId)) { traits.add(new SSHCheckoutTrait(checkoutCredentialsId)); } traits.add(new WebhookRegistrationTrait( autoRegisterHooks ? WebhookRegistration.ITEM : WebhookRegistration.DISABLE)); if (pattern != null && !".*".equals(pattern)) { traits.add(new RegexSCMSourceFilterTrait(pattern)); } } return this; } @CheckForNull public String getCredentialsId() { return credentialsId; } public String getRepoOwner() { return repoOwner; } @NonNull public List<SCMTrait<?>> getTraits() { return Collections.<SCMTrait<?>>unmodifiableList(traits); } @DataBoundSetter public void setCredentialsId(@CheckForNull String credentialsId) { this.credentialsId = Util.fixEmpty(credentialsId); } @SuppressWarnings("unchecked") @DataBoundSetter public void setTraits(@NonNull List<SCMTrait> traits) { // the reduced generics in the method signature are a workaround for JENKINS-26535 this.traits = new ArrayList<>((List) /*defensive*/Util.fixNull(traits)); } public String getServerUrl() { return serverUrl; } @DataBoundSetter public void setServerUrl(String serverUrl) { serverUrl = BitbucketEndpointConfiguration.normalizeServerUrl(serverUrl); if (!StringUtils.equals(this.serverUrl, serverUrl)) { this.serverUrl = serverUrl; resetId(); } } @Deprecated @Restricted(DoNotUse.class) @RestrictedSince("2.2.0") @DataBoundSetter public void setPattern(String pattern) { for (int i = 0; i < traits.size(); i++) { SCMTrait<?> trait = traits.get(i); if (trait instanceof RegexSCMSourceFilterTrait) { if (".*".equals(pattern)) { traits.remove(i); } else { traits.set(i, new RegexSCMSourceFilterTrait(pattern)); } return; } } if (!".*".equals(pattern)) { traits.add(new RegexSCMSourceFilterTrait(pattern)); } } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @DataBoundSetter public void setAutoRegisterHooks(boolean autoRegisterHook) { traits.removeIf(trait -> trait instanceof WebhookRegistrationTrait); traits.add(new WebhookRegistrationTrait( autoRegisterHook ? WebhookRegistration.ITEM : WebhookRegistration.DISABLE)); } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") public boolean isAutoRegisterHooks() { for (SCMTrait<? extends SCMTrait<?>> t : traits) { if (t instanceof WebhookRegistrationTrait) { return ((WebhookRegistrationTrait) t).getMode() != WebhookRegistration.DISABLE; } } return true; } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @NonNull public String getCheckoutCredentialsId() { for (SCMTrait<?> t : traits) { if (t instanceof SSHCheckoutTrait) { return StringUtils.defaultString(((SSHCheckoutTrait) t).getCredentialsId(), BitbucketSCMSource.DescriptorImpl.ANONYMOUS); } } return BitbucketSCMSource.DescriptorImpl.SAME; } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @DataBoundSetter public void setCheckoutCredentialsId(String checkoutCredentialsId) { traits.removeIf(trait -> trait instanceof SSHCheckoutTrait); if (checkoutCredentialsId != null && !BitbucketSCMSource.DescriptorImpl.SAME.equals(checkoutCredentialsId)) { traits.add(new SSHCheckoutTrait(checkoutCredentialsId)); } } @Deprecated @Restricted(DoNotUse.class) @RestrictedSince("2.2.0") public String getPattern() { for (SCMTrait<?> trait : traits) { if (trait instanceof RegexSCMSourceFilterTrait) { return ((RegexSCMSourceFilterTrait) trait).getRegex(); } } return ".*"; } @Deprecated @Restricted(DoNotUse.class) @RestrictedSince("2.2.0") @DataBoundSetter public void setBitbucketServerUrl(String url) { url = BitbucketEndpointConfiguration.normalizeServerUrl(url); AbstractBitbucketEndpoint endpoint = BitbucketEndpointConfiguration.get().findEndpoint(url); if (endpoint != null) { // we have a match setServerUrl(url); return; } LOGGER.log(Level.WARNING, "Call to legacy setBitbucketServerUrl({0}) method is configuring an url missing " + "from the global configuration.", url); setServerUrl(url); } @Deprecated @Restricted(DoNotUse.class) @RestrictedSince("2.2.0") @CheckForNull public String getBitbucketServerUrl() { if (BitbucketEndpointConfiguration.get().findEndpoint(serverUrl) instanceof BitbucketCloudEndpoint) { return null; } return serverUrl; } @NonNull public String getEndpointJenkinsRootUrl() { return AbstractBitbucketEndpoint.getEndpointJenkinsRootUrl(serverUrl); } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @NonNull public String getIncludes() { for (SCMTrait<?> trait : traits) { if (trait instanceof WildcardSCMHeadFilterTrait) { return ((WildcardSCMHeadFilterTrait) trait).getIncludes(); } } return "*"; } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @DataBoundSetter 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())) { traits.remove(i); } else { traits.set(i, new WildcardSCMHeadFilterTrait(includes, existing.getExcludes())); } return; } } if (!"*".equals(includes)) { traits.add(new WildcardSCMHeadFilterTrait(includes, "")); } } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @NonNull public String getExcludes() { for (SCMTrait<?> trait : traits) { if (trait instanceof WildcardSCMHeadFilterTrait) { return ((WildcardSCMHeadFilterTrait) trait).getExcludes(); } } return ""; } @Deprecated @Restricted(NoExternalUse.class) @RestrictedSince("2.2.0") @DataBoundSetter 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)) { traits.remove(i); } else { traits.set(i, new WildcardSCMHeadFilterTrait(existing.getIncludes(), excludes)); } return; } } if (!"".equals(excludes)) { traits.add(new WildcardSCMHeadFilterTrait("*", excludes)); } } @NonNull @Override protected String id() { return serverUrl + "::" + repoOwner; } @Override public void visitSources(SCMSourceObserver observer) throws IOException, InterruptedException { TaskListener listener = observer.getListener(); if (StringUtils.isBlank(repoOwner)) { listener.getLogger().format("Must specify a repository owner%n"); return; } StandardCredentials credentials = BitbucketCredentials.lookupCredentials(serverUrl, observer.getContext(), credentialsId, StandardCredentials.class); if (credentials == null) { listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", serverUrl); } else { listener.getLogger().format("Connecting to %s using %s%n", serverUrl, CredentialsNameProvider.name(credentials)); } try (final BitbucketSCMNavigatorRequest request = new BitbucketSCMNavigatorContext().withTraits(traits) .newRequest(this, observer)) { SourceFactory sourceFactory = new SourceFactory(request); WitnessImpl witness = new WitnessImpl(request, listener); BitbucketAuthenticator authenticator = AuthenticationTokens .convert(BitbucketAuthenticator.authenticationContext(serverUrl), credentials); BitbucketApi bitbucket = BitbucketApiFactory.newInstance(serverUrl, authenticator, repoOwner, null); BitbucketTeam team = bitbucket.getTeam(); if (team != null) { // Navigate repositories of the team listener.getLogger().format("Looking up repositories of team %s%n", repoOwner); request.withRepositories(bitbucket.getRepositories()); } else { // Navigate the repositories of the repoOwner as a user listener.getLogger().format("Looking up repositories of user %s%n", repoOwner); request.withRepositories(bitbucket.getRepositories(UserRoleInRepository.OWNER)); } for (BitbucketRepository repo : request.repositories()) { if (request.process(repo.getRepositoryName(), sourceFactory, null, witness)) { listener.getLogger().format("%d repositories were processed (query completed)%n", witness.getCount()); } } listener.getLogger().format("%d repositories were processed%n", witness.getCount()); } } @NonNull @Override public List<Action> retrieveActions(@NonNull SCMNavigatorOwner owner, @CheckForNull SCMNavigatorEvent 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 listener.getLogger().printf("Looking up team details of %s...%n", getRepoOwner()); List<Action> result = new ArrayList<>(); StandardCredentials credentials = BitbucketCredentials.lookupCredentials(serverUrl, owner, credentialsId, StandardCredentials.class); if (credentials == null) { listener.getLogger().format("Connecting to %s with no credentials, anonymous access%n", serverUrl); } else { listener.getLogger().format("Connecting to %s using %s%n", serverUrl, CredentialsNameProvider.name(credentials)); } BitbucketAuthenticator authenticator = AuthenticationTokens .convert(BitbucketAuthenticator.authenticationContext(serverUrl), credentials); BitbucketApi bitbucket = BitbucketApiFactory.newInstance(serverUrl, authenticator, repoOwner, null); BitbucketTeam team = bitbucket.getTeam(); if (team != null) { String defaultTeamUrl; if (team instanceof BitbucketServerProject) { defaultTeamUrl = serverUrl + "/projects/" + team.getName(); } else { defaultTeamUrl = serverUrl + "/" + team.getName(); } String teamUrl = StringUtils.defaultIfBlank(getLink(team.getLinks(), "html"), defaultTeamUrl); String teamDisplayName = StringUtils.defaultIfBlank(team.getDisplayName(), team.getName()); result.add(new ObjectMetadataAction(teamDisplayName, null, teamUrl)); String avatarUrl; if (team instanceof BitbucketServerProject) { avatarUrl = UriTemplate.fromTemplate(serverUrl + "/rest/api/1.0/projects/{repo}/avatar.png") .set("repo", repoOwner).expand(); } else { avatarUrl = getLink(team.getLinks(), "avatar"); } result.add(new BitbucketTeamMetadataAction(avatarUrl)); 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)); result.add(new BitbucketLink("icon-bitbucket-logo", teamUrl)); listener.getLogger().println("Could not resolve team details"); } return result; } private static String getLink(Map<String, List<BitbucketHref>> links, String name) { if (links == null) { return null; } List<BitbucketHref> hrefs = links.get(name); if (hrefs == null || hrefs.isEmpty()) { return null; } BitbucketHref href = hrefs.get(0); return href == null ? null : href.getHref(); } @Symbol("bitbucket") @Extension public static class DescriptorImpl extends SCMNavigatorDescriptor { public static final String ANONYMOUS = BitbucketSCMSource.DescriptorImpl.ANONYMOUS; public static final String SAME = BitbucketSCMSource.DescriptorImpl.SAME; @Override public String getDisplayName() { return Messages.BitbucketSCMNavigator_DisplayName(); } @Override public String getDescription() { return Messages.BitbucketSCMNavigator_Description(); } @Override public String getIconFilePathPattern() { return "plugin/cloudbees-bitbucket-branch-source/images/:size/bitbucket-scmnavigator.png"; } @Override public String getIconClassName() { return "icon-bitbucket-scmnavigator"; } @SuppressWarnings("unchecked") @Override public SCMNavigator newInstance(String name) { BitbucketSCMNavigator instance = new BitbucketSCMNavigator(StringUtils.defaultString(name)); instance.setTraits((List) getTraitsDefaults()); return instance; } @SuppressWarnings("unused") // used By stapler public boolean isServerUrlSelectable() { return BitbucketEndpointConfiguration.get().isEndpointSelectable(); } @SuppressWarnings("unused") // used By stapler public ListBoxModel doFillServerUrlItems() { return BitbucketEndpointConfiguration.get().getEndpointItems(); } @SuppressWarnings("unused") // used By stapler public FormValidation doCheckCredentialsId(@AncestorInPath SCMSourceOwner context, @QueryParameter String serverUrl, @QueryParameter String value) { return BitbucketCredentials.checkCredentialsId(context, value, serverUrl); } @SuppressWarnings("unused") // used By stapler public static FormValidation doCheckServerUrl(@QueryParameter String value) { if (BitbucketEndpointConfiguration.get().findEndpoint(value) == null) { return FormValidation.error("Unregistered Server: " + value); } return FormValidation.ok(); } @SuppressWarnings("unused") // used By stapler public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner context, @QueryParameter String serverUrl) { return BitbucketCredentials.fillCredentialsIdItems(context, serverUrl); } public List<NamedArrayList<? extends SCMTraitDescriptor<?>>> getTraitsDescriptorLists() { BitbucketSCMSource.DescriptorImpl sourceDescriptor = Jenkins.getInstance() .getDescriptorByType(BitbucketSCMSource.DescriptorImpl.class); List<SCMTraitDescriptor<?>> all = new ArrayList<>(); all.addAll(SCMNavigatorTrait._for(this, BitbucketSCMNavigatorContext.class, BitbucketSCMSourceBuilder.class)); all.addAll(SCMSourceTrait._for(sourceDescriptor, BitbucketSCMSourceContext.class, null)); all.addAll(SCMSourceTrait._for(sourceDescriptor, null, BitbucketGitSCMBuilder.class)); all.addAll(SCMSourceTrait._for(sourceDescriptor, null, BitbucketHgSCMBuilder.class)); Set<SCMTraitDescriptor<?>> dedup = new HashSet<>(); for (Iterator<SCMTraitDescriptor<?>> iterator = all.iterator(); iterator.hasNext();) { SCMTraitDescriptor<?> d = iterator.next(); if (dedup.contains(d) || d instanceof MercurialBrowserSCMSourceTrait.DescriptorImpl || d instanceof GitBrowserSCMSourceTrait.DescriptorImpl) { // remove any we have seen already and ban the browser configuration as it will always be bitbucket iterator.remove(); } else { dedup.add(d); } } List<NamedArrayList<? extends SCMTraitDescriptor<?>>> result = new ArrayList<>(); NamedArrayList.select(all, "Repositories", it -> it instanceof SCMNavigatorTraitDescriptor, true, result); NamedArrayList.select(all, "Within repository", NamedArrayList.anyOf(NamedArrayList.withAnnotation(Discovery.class), NamedArrayList.withAnnotation(Selection.class)), true, result); int insertionPoint = result.size(); NamedArrayList.select(all, "Git", it -> GitSCM.class.isAssignableFrom(it.getScmClass()), true, result); NamedArrayList.select(all, "Mercurial", it -> MercurialSCM.class.isAssignableFrom(it.getScmClass()), true, result); NamedArrayList.select(all, "General", null, true, result, insertionPoint); return result; } public List<SCMTrait<?>> getTraitsDefaults() { return Arrays.<SCMTrait<?>>asList(new BranchDiscoveryTrait(true, false), new OriginPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.MERGE)), new ForkPullRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.MERGE), new ForkPullRequestDiscoveryTrait.TrustTeamForks())); } @NonNull @Override protected SCMSourceCategory[] createCategories() { return new SCMSourceCategory[] { new UncategorizedSCMSourceCategory( Messages._BitbucketSCMNavigator_UncategorizedSCMSourceCategory_DisplayName()) }; } static { IconSet.icons.addIcon(new Icon("icon-bitbucket-scm-navigator icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-scmnavigator.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-scm-navigator icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-scmnavigator.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-scm-navigator icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-scmnavigator.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-scm-navigator icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-scmnavigator.png", Icon.ICON_XLARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-logo icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-logo.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-logo icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-logo.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-logo icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-logo.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-logo icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-logo.png", Icon.ICON_XLARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository.png", Icon.ICON_XLARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-git icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository-git.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-git icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository-git.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-git icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository-git.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-git icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository-git.png", Icon.ICON_XLARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-hg icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-repository-hg.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-hg icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-repository-hg.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-hg icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-repository-hg.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-repo-hg icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-repository-hg.png", Icon.ICON_XLARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-branch icon-sm", "plugin/cloudbees-bitbucket-branch-source/images/16x16/bitbucket-branch.png", Icon.ICON_SMALL_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-branch icon-md", "plugin/cloudbees-bitbucket-branch-source/images/24x24/bitbucket-branch.png", Icon.ICON_MEDIUM_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-branch icon-lg", "plugin/cloudbees-bitbucket-branch-source/images/32x32/bitbucket-branch.png", Icon.ICON_LARGE_STYLE)); IconSet.icons.addIcon(new Icon("icon-bitbucket-branch icon-xlg", "plugin/cloudbees-bitbucket-branch-source/images/48x48/bitbucket-branch.png", Icon.ICON_XLARGE_STYLE)); } } private static class WitnessImpl implements SCMNavigatorRequest.Witness { private int count; private final BitbucketSCMNavigatorRequest request; private final TaskListener listener; public WitnessImpl(BitbucketSCMNavigatorRequest request, TaskListener listener) { this.request = request; this.listener = listener; } @Override public void record(@NonNull String name, boolean isMatch) { BitbucketRepository repository = this.request.getBitbucketRepository(name); if (isMatch) { listener.getLogger().format("Proposing %s%n", repository.getFullName()); count++; } else { listener.getLogger().format("Ignoring %s%n", repository.getFullName()); } } public int getCount() { return count; } } private class SourceFactory implements SCMNavigatorRequest.SourceLambda { private final BitbucketSCMNavigatorRequest request; public SourceFactory(BitbucketSCMNavigatorRequest request) { this.request = request; } @NonNull @Override public SCMSource create(@NonNull String projectName) throws IOException, InterruptedException { return new BitbucketSCMSourceBuilder(getId() + "::" + projectName, serverUrl, credentialsId, repoOwner, projectName).withRequest(request).build(); } } }