Skip to content

Commit df9dc06

Browse files
JENKINS-65909 request specific branches/tags (#479)
* JENKINS-65909 request specific branches * JENKINS-65909 request specific tags * JENKINS-65909 cache repository * JENKINS-65909 add missing test data * JENKINS-65909 remove obsolete test data Co-authored-by: Günter Grodotzki <[email protected]>
1 parent 20b81c1 commit df9dc06

13 files changed

+231
-21
lines changed

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

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
4242
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
4343
import com.cloudbees.jenkins.plugins.bitbucket.hooks.HasPullRequests;
44+
import com.cloudbees.jenkins.plugins.bitbucket.hooks.HasRefsChangedRequest;
4445
import com.cloudbees.plugins.credentials.CredentialsNameProvider;
4546
import com.cloudbees.plugins.credentials.CredentialsProvider;
4647
import com.cloudbees.plugins.credentials.common.StandardCredentials;
@@ -76,6 +77,7 @@
7677
import java.util.Locale;
7778
import java.util.Map;
7879
import java.util.Set;
80+
import java.util.TreeSet;
7981
import java.util.concurrent.ConcurrentHashMap;
8082
import java.util.logging.Level;
8183
import java.util.logging.Logger;
@@ -237,6 +239,12 @@ public class BitbucketSCMSource extends SCMSource {
237239
@CheckForNull
238240
private transient List<BitbucketHref> cloneLinks = null;
239241

242+
/**
243+
* The cache of the bitbucket repository.
244+
*/
245+
@CheckForNull
246+
private transient BitbucketRepository bitbucketRepository = null;
247+
240248
/**
241249
* Constructor.
242250
*
@@ -502,7 +510,7 @@ public boolean isAutoRegisterHook() {
502510

503511
public BitbucketRepositoryType getRepositoryType() throws IOException, InterruptedException {
504512
if (repositoryType == null) {
505-
BitbucketRepository r = buildBitbucketClient().getRepository();
513+
BitbucketRepository r = getBitbucketRepository();
506514
repositoryType = BitbucketRepositoryType.fromString(r.getScm());
507515
Map<String, List<BitbucketHref>> links = r.getLinks();
508516
if (links != null && links.containsKey("clone")) {
@@ -524,6 +532,13 @@ public BitbucketApi buildBitbucketClient(String repoOwner, String repository) {
524532
return BitbucketApiFactory.newInstance(getServerUrl(), authenticator(), repoOwner, repository);
525533
}
526534

535+
private BitbucketRepository getBitbucketRepository() throws IOException, InterruptedException {
536+
if(bitbucketRepository==null) {
537+
bitbucketRepository = buildBitbucketClient().getRepository();
538+
}
539+
return bitbucketRepository;
540+
}
541+
527542
@Override
528543
public void afterSave() {
529544
try {
@@ -575,6 +590,24 @@ protected Iterable<BitbucketPullRequest> create() {
575590
@Override
576591
protected Iterable<BitbucketBranch> create() {
577592
try {
593+
if (event != null) {
594+
Set<BitbucketBranch> branches = new TreeSet<>((a, b) -> a.getName().compareTo(b.getName()));
595+
if (event instanceof HasRefsChangedRequest) {
596+
HasRefsChangedRequest hasRefsChangedRequest = (HasRefsChangedRequest) event;
597+
hasRefsChangedRequest.getBranches(BitbucketSCMSource.this).forEach((b) -> branches.add(b));
598+
}
599+
600+
if (event instanceof HasPullRequests) {
601+
HasPullRequests hasPrEvent = (HasPullRequests) event;
602+
hasPrEvent.getPullRequests(BitbucketSCMSource.this).forEach((pr) -> {
603+
branches.add(pr.getSource().getBranch());
604+
branches.add(pr.getDestination().getBranch());
605+
});
606+
}
607+
608+
return branches;
609+
}
610+
578611
return (Iterable<BitbucketBranch>) buildBitbucketClient().getBranches();
579612
} catch (IOException | InterruptedException e) {
580613
throw new BitbucketSCMSource.WrappedException(e);
@@ -587,6 +620,11 @@ protected Iterable<BitbucketBranch> create() {
587620
@Override
588621
protected Iterable<BitbucketBranch> create() {
589622
try {
623+
if (event instanceof HasRefsChangedRequest) {
624+
HasRefsChangedRequest hasRefsChangedRequest = (HasRefsChangedRequest) event;
625+
return hasRefsChangedRequest.getTags(BitbucketSCMSource.this);
626+
}
627+
590628
return (Iterable<BitbucketBranch>) buildBitbucketClient().getTags();
591629
} catch (IOException | InterruptedException e) {
592630
throw new BitbucketSCMSource.WrappedException(e);
@@ -746,7 +784,7 @@ private void retrieveBranches(final BitbucketSCMSourceRequest request)
746784
request.listener().getLogger().println("Looking up " + fullName + " for branches");
747785

748786
final BitbucketApi bitbucket = buildBitbucketClient();
749-
Map<String, List<BitbucketHref>> links = bitbucket.getRepository().getLinks();
787+
Map<String, List<BitbucketHref>> links = getBitbucketRepository().getLinks();
750788
if (links != null && links.containsKey("clone")) {
751789
cloneLinks = links.get("clone");
752790
}
@@ -772,7 +810,7 @@ private void retrieveTags(final BitbucketSCMSourceRequest request) throws IOExce
772810
request.listener().getLogger().println("Looking up " + fullName + " for tags");
773811

774812
final BitbucketApi bitbucket = buildBitbucketClient();
775-
Map<String, List<BitbucketHref>> links = bitbucket.getRepository().getLinks();
813+
Map<String, List<BitbucketHref>> links = getBitbucketRepository().getLinks();
776814
if (links != null && links.containsKey("clone")) {
777815
cloneLinks = links.get("clone");
778816
}
@@ -795,9 +833,11 @@ private void retrieveTags(final BitbucketSCMSourceRequest request) throws IOExce
795833
@Override
796834
protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOException, InterruptedException {
797835
final BitbucketApi bitbucket = buildBitbucketClient();
798-
List<? extends BitbucketBranch> branches = bitbucket.getBranches();
799836
if (head instanceof PullRequestSCMHead) {
800837
PullRequestSCMHead h = (PullRequestSCMHead) head;
838+
List<BitbucketBranch> branches = new ArrayList<>();
839+
branches.addAll(bitbucket.getBranchesByFilterText(h.getBranchName()));
840+
branches.addAll(bitbucket.getBranchesByFilterText(h.getTarget().getName()));
801841
BitbucketCommit targetRevision = findCommit(h.getTarget().getName(), branches, listener);
802842
if (targetRevision == null) {
803843
LOGGER.log(Level.WARNING, "No branch found in {0}/{1} with name [{2}]",
@@ -806,9 +846,10 @@ protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOExc
806846
}
807847
BitbucketCommit sourceRevision;
808848
if (bitbucket instanceof BitbucketCloudApiClient) {
809-
branches = head.getOrigin() == SCMHeadOrigin.DEFAULT
810-
? branches
811-
: buildBitbucketClient(h).getBranches();
849+
if (head.getOrigin() != SCMHeadOrigin.DEFAULT) {
850+
branches.clear();
851+
branches.addAll(buildBitbucketClient(h).getBranchesByFilterText(h.getBranchName()));
852+
}
812853
sourceRevision = findCommit(h.getBranchName(), branches, listener);
813854
} else {
814855
try {
@@ -836,14 +877,15 @@ protected SCMRevision retrieve(SCMHead head, TaskListener listener) throws IOExc
836877
);
837878
} else if(head instanceof BitbucketTagSCMHead) {
838879
BitbucketTagSCMHead tagHead = (BitbucketTagSCMHead) head;
839-
List<? extends BitbucketBranch> tags = bitbucket.getTags();
880+
List<? extends BitbucketBranch> tags = bitbucket.getTagsByFilterText(head.getName());
840881
BitbucketCommit revision = findCommit(head.getName(), tags, listener);
841882
if (revision == null) {
842883
LOGGER.log(Level.WARNING, "No tag found in {0}/{1} with name [{2}]", new Object[] { repoOwner, repository, head.getName() });
843884
return null;
844885
}
845886
return new BitbucketTagSCMRevision(tagHead, revision);
846887
} else {
888+
List<? extends BitbucketBranch> branches = bitbucket.getBranchesByFilterText(head.getName());
847889
BitbucketCommit revision = findCommit(head.getName(), branches, listener);
848890
if (revision == null) {
849891
LOGGER.log(Level.WARNING, "No branch found in {0}/{1} with name [{2}]",
@@ -925,7 +967,7 @@ public SCM build(SCMHead head, SCMRevision revision) {
925967
if (cloneLinks == null) {
926968
BitbucketApi bitbucket = buildBitbucketClient();
927969
try {
928-
BitbucketRepository r = bitbucket.getRepository();
970+
BitbucketRepository r = getBitbucketRepository();
929971
Map<String, List<BitbucketHref>> links = r.getLinks();
930972
if (links != null && links.containsKey("clone")) {
931973
cloneLinks = links.get("clone");
@@ -1009,7 +1051,7 @@ protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event,
10091051
// TODO when we have support for trusted events, use the details from event if event was from trusted source
10101052
List<Action> result = new ArrayList<>();
10111053
final BitbucketApi bitbucket = buildBitbucketClient();
1012-
BitbucketRepository r = bitbucket.getRepository();
1054+
BitbucketRepository r = getBitbucketRepository();
10131055
Map<String, List<BitbucketHref>> links = r.getLinks();
10141056
if (links != null && links.containsKey("clone")) {
10151057
cloneLinks = links.get("clone");

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketApi.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,18 @@ boolean checkPathExists(@NonNull String branchOrHash, @NonNull String path)
146146
@NonNull
147147
List<? extends BitbucketBranch> getBranches() throws IOException, InterruptedException;
148148

149-
/**
149+
/**
150+
* Returns the branches in the repository matching the filter.
151+
*
152+
* @param filterText the text to match on
153+
* @return the list of branches in the repository.
154+
* @throws IOException if there was a network communications error.
155+
* @throws InterruptedException if interrupted while waiting on remote communications.
156+
*/
157+
@NonNull
158+
List<? extends BitbucketBranch> getBranchesByFilterText(String filterText) throws IOException, InterruptedException;
159+
160+
/**
150161
* Returns the tags in the repository.
151162
*
152163
* @return the list of tags in the repository.
@@ -156,6 +167,17 @@ boolean checkPathExists(@NonNull String branchOrHash, @NonNull String path)
156167
@NonNull
157168
List<? extends BitbucketBranch> getTags() throws IOException, InterruptedException;
158169

170+
/**
171+
* Returns the tags in the repository matching the filter.
172+
*
173+
* @param filterText the text to match on
174+
* @return the list of tags in the repository.
175+
* @throws IOException if there was a network communications error.
176+
* @throws InterruptedException if interrupted while waiting on remote communications.
177+
*/
178+
@NonNull
179+
List<? extends BitbucketBranch> getTagsByFilterText(String filterText) throws IOException, InterruptedException;
180+
159181
/**
160182
* Resolve the commit object given its hash.
161183
*

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/BitbucketCloudApiClient.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,36 @@ public List<BitbucketCloudBranch> getBranches() throws IOException, InterruptedE
488488
return getBranchesByRef("/refs/branches");
489489
}
490490

491+
/**
492+
* {@inheritDoc}
493+
*/
494+
@Override
495+
@NonNull
496+
public List<BitbucketCloudBranch> getBranchesByFilterText(String filterText) throws IOException, InterruptedException {
497+
ArrayList<BitbucketCloudBranch> branches = new ArrayList<>();
498+
for (BitbucketCloudBranch branch : getBranches()) {
499+
if (branch.getName().contains(filterText)) {
500+
branches.add(branch);
501+
}
502+
}
503+
return branches;
504+
}
505+
506+
/**
507+
* {@inheritDoc}
508+
*/
509+
@Override
510+
@NonNull
511+
public List<BitbucketCloudBranch> getTagsByFilterText(String filterText) throws IOException, InterruptedException {
512+
ArrayList<BitbucketCloudBranch> branches = new ArrayList<>();
513+
for (BitbucketCloudBranch branch : getTags()) {
514+
if (branch.getName().contains(filterText)) {
515+
branches.add(branch);
516+
}
517+
}
518+
return branches;
519+
}
520+
491521
public List<BitbucketCloudBranch> getBranchesByRef(String nodePath) throws IOException, InterruptedException {
492522
String url = UriTemplate.fromTemplate(REPO_URL_TEMPLATE + nodePath + "{?pagelen}")
493523
.set("owner", owner)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.cloudbees.jenkins.plugins.bitbucket.hooks;
2+
3+
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource;
4+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
5+
6+
public interface HasRefsChangedRequest {
7+
Iterable<BitbucketBranch> getBranches(BitbucketSCMSource src) throws InterruptedException;
8+
9+
Iterable<BitbucketBranch> getTags(BitbucketSCMSource src) throws InterruptedException;
10+
}

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@
3030
import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
3131
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMHead;
3232
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMRevision;
33+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBranch;
3334
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
3435
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
36+
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
3537
import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequest;
3638
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
3739
import com.cloudbees.jenkins.plugins.bitbucket.server.events.NativeServerRefsChangedEvent;
@@ -114,7 +116,7 @@ public void process(HookEventType hookEvent, String payload, BitbucketType insta
114116
}
115117
}
116118

117-
private static final class HeadEvent extends NativeServerHeadEvent<Collection<NativeServerRefsChangedEvent.Change>> implements HasPullRequests {
119+
private static final class HeadEvent extends NativeServerHeadEvent<Collection<NativeServerRefsChangedEvent.Change>> implements HasPullRequests, HasRefsChangedRequest {
118120
private final NativeServerRefsChangedEvent refsChangedEvent;
119121
private final Map<CacheKey, Map<String, BitbucketServerPullRequest>> cachedPullRequests = new HashMap<>();
120122

@@ -282,9 +284,30 @@ public Collection<BitbucketPullRequest> getPullRequests(BitbucketSCMSource src)
282284
Map<String, BitbucketServerPullRequest> prsForChange = getPullRequests(src, change);
283285
prs.addAll(prsForChange.values());
284286
}
285-
286287
return prs;
287288
}
289+
290+
@Override
291+
public Iterable<BitbucketBranch> getBranches(BitbucketSCMSource src) throws InterruptedException {
292+
List<BitbucketBranch> branches = new ArrayList<>();
293+
for (final NativeServerRefsChangedEvent.Change change : getPayload()) {
294+
if ("BRANCH".equals(change.getRef().getType())) {
295+
branches.add(new BitbucketServerBranch(change.getRef().getDisplayId(), change.getToHash()));
296+
}
297+
}
298+
return branches;
299+
}
300+
301+
@Override
302+
public Iterable<BitbucketBranch> getTags(BitbucketSCMSource src) throws InterruptedException {
303+
List<BitbucketBranch> branches = new ArrayList<>();
304+
for (final NativeServerRefsChangedEvent.Change change : getPayload()) {
305+
if ("TAG".equals(change.getRef().getType())) {
306+
branches.add(new BitbucketServerBranch(change.getRef().getDisplayId(), change.getToHash()));
307+
}
308+
}
309+
return branches;
310+
}
288311
}
289312

290313
private static final class CacheKey {

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/server/client/BitbucketServerAPIClient.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ public class BitbucketServerAPIClient implements BitbucketApi {
131131
private static final String API_REPOSITORIES_PATH = API_BASE_PATH + "/projects/{owner}/repos{?start,limit}";
132132
private static final String API_REPOSITORY_PATH = API_BASE_PATH + "/projects/{owner}/repos/{repo}";
133133
private static final String API_DEFAULT_BRANCH_PATH = API_REPOSITORY_PATH + "/branches/default";
134-
private static final String API_BRANCHES_PATH = API_REPOSITORY_PATH + "/branches{?start,limit}";
135-
private static final String API_TAGS_PATH = API_REPOSITORY_PATH + "/tags{?start,limit}";
134+
private static final String API_BRANCHES_PATH = API_REPOSITORY_PATH + "/branches{?start,limit,filterText}";
135+
private static final String API_TAGS_PATH = API_REPOSITORY_PATH + "/tags{?start,limit,filterText}";
136136
private static final String API_PULL_REQUESTS_PATH = API_REPOSITORY_PATH + "/pull-requests{?start,limit,at,direction,state}";
137137
private static final String API_PULL_REQUEST_PATH = API_REPOSITORY_PATH + "/pull-requests/{id}";
138138
private static final String API_PULL_REQUEST_MERGE_PATH = API_REPOSITORY_PATH + "/pull-requests/{id}/merge";
@@ -560,7 +560,16 @@ public String getDefaultBranch() throws IOException {
560560
@Override
561561
@NonNull
562562
public List<BitbucketServerBranch> getTags() throws IOException, InterruptedException {
563-
return getServerBranches(API_TAGS_PATH);
563+
return getServerBranches(API_TAGS_PATH, null);
564+
}
565+
566+
/**
567+
* {@inheritDoc}
568+
*/
569+
@Override
570+
@NonNull
571+
public List<BitbucketServerBranch> getTagsByFilterText(String filterText) throws IOException, InterruptedException {
572+
return getServerBranches(API_TAGS_PATH, filterText);
564573
}
565574

566575
/**
@@ -569,15 +578,28 @@ public List<BitbucketServerBranch> getTags() throws IOException, InterruptedExce
569578
@Override
570579
@NonNull
571580
public List<BitbucketServerBranch> getBranches() throws IOException, InterruptedException {
572-
return getServerBranches(API_BRANCHES_PATH);
581+
return getServerBranches(API_BRANCHES_PATH, null);
582+
}
583+
584+
/**
585+
* {@inheritDoc}
586+
*/
587+
@Override
588+
@NonNull
589+
public List<BitbucketServerBranch> getBranchesByFilterText(String filterText) throws IOException, InterruptedException {
590+
return getServerBranches(API_BRANCHES_PATH, filterText);
573591
}
574592

575-
private List<BitbucketServerBranch> getServerBranches(String apiPath) throws IOException, InterruptedException {
593+
private List<BitbucketServerBranch> getServerBranches(String apiPath, String filterText) throws IOException, InterruptedException {
576594
UriTemplate template = UriTemplate
577595
.fromTemplate(apiPath)
578596
.set("owner", getUserCentricOwner())
579597
.set("repo", repositoryName);
580598

599+
if (filterText != null) {
600+
template.set("filterText", filterText);
601+
}
602+
581603
List<BitbucketServerBranch> branches = getResources(template, BitbucketServerBranches.class);
582604
for (final BitbucketServerBranch branch : branches) {
583605
if (branch != null) {

src/test/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotificationsTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ private WorkflowMultiBranchProject prepareFirstCheckoutCompletedInvisibleActionT
110110
BitbucketBranch branch = Mockito.mock(BitbucketBranch.class);
111111
List<? extends BitbucketBranch> branchList = Collections.singletonList(branch);
112112
when(api.getBranches()).thenAnswer(new Returns(branchList));
113+
when(api.getBranchesByFilterText("master")).thenAnswer(new Returns(branchList));
113114
when(branch.getName()).thenReturn(branchName);
114115
when(branch.getRawNode()).thenReturn(sampleRepo.head());
115116
BitbucketCommit commit = Mockito.mock(BitbucketCommit.class);

0 commit comments

Comments
 (0)