Skip to content

Commit f7b3a48

Browse files
committed
Merge remote-tracking branch 'upstream/master' into bugfix/JENKINS-62780
2 parents 7cf4f69 + e35b6c9 commit f7b3a48

File tree

23 files changed

+137
-71
lines changed

23 files changed

+137
-71
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ work
99
*.iml
1010
*.iws
1111
*.ipr
12+
13+
# VSCode
14+
.factorypath

docs/USER_GUIDE.adoc

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
[IMPORTANT]
44
=====================================================================
5-
On April 29th, 2019, link:https://developer.atlassian.com/cloud/bitbucket/bbc-gdpr-api-migration-guide/[Atlassian updated their suite APIs for both Bitbucket and JIRA] to comply with link:https://eugdpr.org[GDPR privacy requirements]. CloudBees has evaluated this change against the CloudBees BitBucket Branch Source plugin and the CloudBees JIRA plugin.
5+
On March 5th, 2020, link:https://confluence.atlassian.com/bitbucketserver/bitbucket-server-7-0-release-notes-990546638.html[Atlassian releases Bitbucket Server 7] which removed some undocumented features with regards to pull requests.
66
7-
. BitBucket Cloud Pipeline jobs created with "classic" Jenkins are unaffected.
8-
. There is a limited impact to Bitbucket Cloud Pipeline jobs created using Blue Ocean:
9-
.. If you are running Blue Ocean version 1.15.1 or earlier, you will be unable to create or edit BitBucket Cloud Pipeline jobs.
10-
.. Upgrading to Blue Ocean 1.16.0 or later fixes this issue.
11-
.. However, because "owner name" has been removed from the BitBucket REST API, when you edit Bitbucket Cloud Pipelines in the "classic" Jenkins UI, a UUID (instead of human-readable text) is displayed in the "owner" field of the Pipeline.
7+
. BitBucket Server Pipeline jobs can no longer perform a lightweight checkout of the Jenkinsfile if you are using the merge strategy for builds.
8+
.. BitBucket Server no longer stores the merged result of a PR to link:https://jira.atlassian.com/browse/BSERV-12284?focusedCommentId=2389584&[improve performance]
9+
.. BitBucket Server Pipeline jobs will automatically fallback to heavyweight checkout
10+
. BitBucket Server 7.x no longer automatically creates the required refs for pull requests
11+
.. For Bitbucket Server Pipeline jobs to function for pull requests on BitBucket Server 7.x you need to enable "Call Changes api" option in the plugin configuration
1212
13-
We *do not* anticipate any broader user impact. If you encounter a change in behavior that you believe is due to the BitBucket or JIRA API changes, please link:https://support.cloudbees.com/hc/en-us[contact CloudBees Support] for assistance.
1413
=====================================================================
1514

1615
[id=bitbucket-sect-intro]
@@ -23,6 +22,14 @@ as a multi-branch project source in two different ways:
2322

2423
IMPORTANT: This plugin is not compatible with versions of Bitbucket Server previous to 4.0.
2524

25+
[id=bitbucket-server-7]
26+
== BitBucket Server 7 compatibility
27+
28+
_BitBucket Server 7.x_ is supported but does no longer support lightweight checkout for pull requests when merge strategy is used for build.
29+
30+
IMPORTANT: In order to have the pull request process working the "Call Changes api" option must be
31+
enabled in _Manage Jenkins_ » _Configure System_
32+
2633
[id=bitbucket-scm-source]
2734
== Branches and pull requests auto-discovering
2835

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,7 @@ class Skip extends IOException {
641641
getPullRequestTitleCache()
642642
.put(pull.getId(), StringUtils.defaultString(pull.getTitle()));
643643
getPullRequestContributorCache().put(pull.getId(),
644-
// TODO get more details on the author
645-
new ContributorMetadataAction(pull.getAuthorLogin(), null, pull.getAuthorEmail()));
644+
new ContributorMetadataAction(pull.getAuthorIdentifier(), pull.getAuthorLogin(), pull.getAuthorEmail()));
646645
try {
647646
// We store resolved hashes here so to avoid resolving the commits multiple times
648647
for (final ChangeRequestCheckoutStrategy strategy : strategies.get(fork)) {

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,19 @@ public interface BitbucketPullRequest {
5252

5353
String getLink();
5454

55+
/**
56+
* Despite the name, this is a <em>display name</em> or <em>nickname</em> for the author, not a stable <em>username</em> for login.
57+
*/
5558
String getAuthorLogin();
5659

60+
/**
61+
* Not set in Cloud.
62+
*/
5763
String getAuthorEmail();
5864

65+
/**
66+
* Username or account identifier of the author.
67+
*/
5968
String getAuthorIdentifier();
6069

6170
List<BitbucketReviewer> getReviewers();

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/events/BitbucketCloudPullRequestEvent.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,22 @@ private void reconstructMissingData() {
7171
BitbucketPullRequestValueRepository source = this.pullRequest.getSource();
7272
if (source != null) {
7373
BitbucketCloudRepository sourceRepository = source.getRepository();
74-
if(sourceRepository != null) {
74+
if (sourceRepository != null) {
7575
if (sourceRepository.getScm() == null) {
7676
sourceRepository.setScm(repository.getScm());
7777
}
7878
if (sourceRepository.getOwner() == null) {
79-
if (sourceRepository.getOwnerName().equals(this.pullRequest.getAuthorLogin())) {
79+
if (!sourceRepository.getOwnerName().equals(repository.getOwnerName())) { // i.e., a fork
8080
BitbucketCloudRepositoryOwner owner = new BitbucketCloudRepositoryOwner();
81-
owner.setUsername(this.pullRequest.getAuthorLogin());
82-
owner.setDisplayName(this.pullRequest.getAuthorDisplayName());
83-
if (this.repository.isPrivate()) {
84-
sourceRepository.setPrivate(this.repository.isPrivate());
81+
owner.setUsername(sourceRepository.getOwnerName());
82+
owner.setDisplayName(this.pullRequest.getAuthorLogin());
83+
if (repository.isPrivate()) {
84+
sourceRepository.setPrivate(repository.isPrivate());
8585
}
8686
sourceRepository.setOwner(owner);
87-
} else if (sourceRepository.getOwnerName().equals(this.repository.getOwnerName())) {
88-
sourceRepository.setOwner(this.repository.getOwner());
89-
sourceRepository.setPrivate(this.repository.isPrivate());
87+
} else { // origin branch
88+
sourceRepository.setOwner(repository.getOwner());
89+
sourceRepository.setPrivate(repository.isPrivate());
9090
}
9191
}
9292
}
@@ -95,7 +95,7 @@ private void reconstructMissingData() {
9595
BitbucketCloudBranch sourceBranch = source.getBranch();
9696
BitbucketCommit sourceCommit = source.getCommit();
9797
if (sourceCommit != null
98-
&& sourceBranch != null) {
98+
&& sourceBranch != null) {
9999
if (sourceBranch.getRawNode() == null) {
100100
sourceBranch.setRawNode(source.getCommit().getHash());
101101
}
@@ -106,23 +106,23 @@ private void reconstructMissingData() {
106106
}
107107
BitbucketPullRequestValueDestination destination = this.pullRequest.getDestination();
108108
if (destination != null
109-
&& destination.getRepository() != null) {
109+
&& destination.getRepository() != null) {
110110
if (destination.getRepository().getScm() == null) {
111111
destination.getRepository().setScm(repository.getScm());
112112
}
113113
if (destination.getRepository().getOwner() == null
114-
&& destination.getRepository().getOwnerName()
115-
.equals(repository.getOwnerName())) {
114+
&& destination.getRepository().getOwnerName()
115+
.equals(repository.getOwnerName())) {
116116
destination.getRepository().setOwner(repository.getOwner());
117117
destination.getRepository().setPrivate(repository.isPrivate());
118118
}
119119
}
120120
if (destination != null
121-
&& destination.getCommit() != null
122-
&& destination.getBranch() != null
123-
&& destination.getBranch().getRawNode() == null) {
121+
&& destination.getCommit() != null
122+
&& destination.getBranch() != null
123+
&& destination.getBranch().getRawNode() == null) {
124124
destination.getBranch()
125-
.setRawNode(destination.getCommit().getHash());
125+
.setRawNode(destination.getCommit().getHash());
126126
}
127127
}
128128
}

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/client/pullrequest/BitbucketPullRequestValue.java

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public String getLink() {
8181

8282
@Override
8383
public String getAuthorLogin() {
84-
return author.username;
84+
return author.displayName;
8585
}
8686

8787
@Override
@@ -107,10 +107,6 @@ public void setAuthor(Author author) {
107107
this.author = author;
108108
}
109109

110-
public String getAuthorDisplayName() {
111-
return author.displayName;
112-
}
113-
114110
@Override
115111
@JsonInclude(JsonInclude.Include.NON_EMPTY)
116112
public List<BitbucketReviewer> getReviewers() {
@@ -155,27 +151,15 @@ public void setHref(String href) {
155151
public static class Author {
156152
@JsonProperty("account_id")
157153
private String identifier;
158-
private String username;
159-
@JsonProperty("display_name")
154+
@JsonProperty("nickname")
160155
private String displayName;
161156
public Author() {}
162-
public Author(String username) {
163-
this.username = username;
164-
}
165-
166-
public String getUsername() {
167-
return username;
168-
}
169-
170-
public void setUsername(String username) {
171-
this.username = username;
172-
}
173157

174158
public String getIdentifier() {
175159
return identifier;
176160
}
177161

178-
public void setIndentifier(String identifier) {
162+
public void setIdentifier(String identifier) {
179163
this.identifier = identifier;
180164
}
181165

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ public class BitbucketServerEndpoint extends AbstractBitbucketEndpoint {
8585
*/
8686
private boolean callCanMerge = true;
8787

88+
/**
89+
* Whether to always call the can diff api when retrieving pull requests.
90+
*/
91+
private boolean callChanges = true;
92+
8893
/**
8994
* @param displayName Optional name to use to describe the end-point.
9095
* @param serverUrl The URL of this Bitbucket Server
@@ -128,6 +133,15 @@ public void setCallCanMerge(boolean callCanMerge) {
128133
this.callCanMerge = callCanMerge;
129134
}
130135

136+
public boolean isCallChanges() {
137+
return callChanges;
138+
}
139+
140+
@DataBoundSetter
141+
public void setCallChanges(boolean callChanges) {
142+
this.callChanges = callChanges;
143+
}
144+
131145
/**
132146
* {@inheritDoc}
133147
*/

src/main/java/com/cloudbees/jenkins/plugins/bitbucket/filesystem/BitbucketSCMFileSystem.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ public SCMFileSystem build(@NonNull SCMSource source, @NonNull SCMHead head, @Ch
178178
} else if (pr.getCheckoutStrategy() == ChangeRequestCheckoutStrategy.HEAD) {
179179
ref = "pull-requests/" + pr.getId() + "/from";
180180
} else if (pr.getCheckoutStrategy() == ChangeRequestCheckoutStrategy.MERGE) {
181+
// No longer supported since bitbucket server v7, falls back to heavycheckout
181182
ref = "pull-requests/" + pr.getId() + "/merge";
182183
}
183184
} else if (head instanceof BitbucketTagSCMHead) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ public enum HookEventType {
111111
*/
112112
SERVER_PULL_REQUEST_REVIEWER_UPDATED("pr:reviewer:updated", NativeServerPullRequestHookProcessor.class),
113113

114+
/**
115+
* @see <a href="https://confluence.atlassian.com/bitbucketserver070/event-payload-996644369.html#Eventpayload-Sourcebranchupdated">Eventpayload-Sourcebranchupdated</a>
116+
* @since Bitbucket Server 7.0
117+
*/
118+
SERVER_PULL_REQUEST_FROM_REF_UPDATED("pr:from_ref_updated", NativeServerPullRequestHookProcessor.class),
119+
114120
/**
115121
* Sent when hitting the {@literal "Test connection"} button in Bitbucket Server. Apparently undocumented.
116122
*/

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public void process(HookEventType hookEvent, String payload, BitbucketType insta
8282
break;
8383
case SERVER_PULL_REQUEST_MODIFIED:
8484
case SERVER_PULL_REQUEST_REVIEWER_UPDATED:
85+
case SERVER_PULL_REQUEST_FROM_REF_UPDATED:
8586
eventType = SCMEvent.Type.UPDATED;
8687
break;
8788
default:

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public class WebhookConfiguration {
6565
HookEventType.SERVER_PULL_REQUEST_DECLINED.getKey(),
6666
HookEventType.SERVER_PULL_REQUEST_DELETED.getKey(),
6767
HookEventType.SERVER_PULL_REQUEST_MODIFIED.getKey(),
68-
HookEventType.SERVER_PULL_REQUEST_REVIEWER_UPDATED.getKey()
68+
HookEventType.SERVER_PULL_REQUEST_REVIEWER_UPDATED.getKey(),
69+
HookEventType.SERVER_PULL_REQUEST_FROM_REF_UPDATED.getKey()
6970
));
7071

7172
/**

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public class BitbucketServerAPIClient implements BitbucketApi {
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";
139+
private static final String API_PULL_REQUEST_CHANGES_PATH = API_REPOSITORY_PATH + "/pull-requests/{id}/changes{?start,limit}";
139140
static final String API_BROWSE_PATH = API_REPOSITORY_PATH + "/browse{/path*}{?at}";
140141
private static final String API_COMMITS_PATH = API_REPOSITORY_PATH + "/commits{/hash}";
141142
private static final String API_PROJECT_PATH = API_BASE_PATH + "/projects/{owner}";
@@ -339,11 +340,21 @@ private List<BitbucketServerPullRequest> getPullRequests(UriTemplate template)
339340
}
340341

341342
AbstractBitbucketEndpoint endpointConfig = BitbucketEndpointConfiguration.get().findEndpoint(baseURL);
342-
if (endpointConfig instanceof BitbucketServerEndpoint && ((BitbucketServerEndpoint)endpointConfig).isCallCanMerge()) {
343+
if (endpointConfig instanceof BitbucketServerEndpoint) {
344+
final BitbucketServerEndpoint endpoint = (BitbucketServerEndpoint) endpointConfig;
345+
if (!endpoint.isCallCanMerge() && !endpoint.isCallChanges()) {
346+
return pullRequests;
347+
}
348+
343349
// This is required for Bitbucket Server to update the refs/pull-requests/* references
344350
// See https://community.atlassian.com/t5/Bitbucket-questions/Change-pull-request-refs-after-Commit-instead-of-after-Approval/qaq-p/194702#M6829
345351
for (BitbucketServerPullRequest pullRequest : pullRequests) {
346-
pullRequest.setCanMerge(getPullRequestCanMergeById(Integer.parseInt(pullRequest.getId())));
352+
if (endpoint.isCallCanMerge()) {
353+
pullRequest.setCanMerge(getPullRequestCanMergeById(pullRequest.getId()));
354+
}
355+
if (endpoint.isCallChanges()) {
356+
callPullRequestChangesById(pullRequest.getId());
357+
}
347358
}
348359
}
349360

@@ -383,7 +394,17 @@ private void setupClosureForPRBranch(BitbucketServerPullRequest pr) {
383394
}
384395
}
385396

386-
private boolean getPullRequestCanMergeById(@NonNull Integer id) throws IOException {
397+
private void callPullRequestChangesById(@NonNull String id) throws IOException {
398+
String url = UriTemplate
399+
.fromTemplate(API_PULL_REQUEST_CHANGES_PATH)
400+
.set("owner", getUserCentricOwner())
401+
.set("repo", repositoryName)
402+
.set("id", id).set("limit", 1)
403+
.expand();
404+
getRequest(url);
405+
}
406+
407+
private boolean getPullRequestCanMergeById(@NonNull String id) throws IOException {
387408
String url = UriTemplate
388409
.fromTemplate(API_PULL_REQUEST_MERGE_PATH)
389410
.set("owner", getUserCentricOwner())

src/main/resources/com/cloudbees/jenkins/plugins/bitbucket/endpoints/BitbucketServerEndpoint/config-detail.jelly

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@
1010
<f:entry field="callCanMerge">
1111
<f:checkbox title="${%Call Can Merge}" default="true"/>
1212
</f:entry>
13+
<f:entry field="callChanges">
14+
<f:checkbox title="${%Call Changes api}" default="true"/>
15+
</f:entry>
1316
</j:jelly>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div>
2+
Always call the can changes api on pull requests to ensure the source code references are up to date (since BitBucket Server v7).
3+
</div>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ private static BitbucketPullRequestValue getPullRequest() {
200200
pr.setDestination(new BitbucketPullRequestValueDestination(repository, branch, commit));
201201

202202
pr.setId("23");
203-
pr.setAuthor(new BitbucketPullRequestValue.Author("amuniz"));
203+
pr.setAuthor(new BitbucketPullRequestValue.Author());
204204
pr.setLinks(new BitbucketPullRequestValue.Links("https://bitbucket.org/amuniz/test-repos/pull-requests/23"));
205205
return pr;
206206
}

0 commit comments

Comments
 (0)