Skip to content

Commit 217b447

Browse files
committed
[JENKINS-47213] Opening a PR when the branch is still building with "Exclude branches that are also filed as PRs" leads to a never ending build in BitBucket
Bitbucket enhance its build status API with a refname (Cloud) and ref (Server) property to indicate the owner's git reference. This can be used as a sort of filter in case multiple build statuses have been associated with the same commit. For example, this means that when browsing commits from the webpage, you will see all the build statuses associated with a commit (no scope); but, you should only see one when you land on the pull request webpage (scoped). NOTE: At the time of implementation once a build status is created, it cannot be deleted via the API. If refname is set, it cannot be set to null. The property appears to participate in some way in the primary key of the build status, multiple build statuses with the same key but different refnames are allowed, especially in the use case the update APIs return an HTTP 500 error. Available documentations: * https://developer.atlassian.com/server/bitbucket/rest/v903/api-group-builds-and-deployments/#api-api-latest-projects-projectkey-repos-repositoryslug-commits-commitid-builds-post * https://developer.atlassian.com/cloud/bitbucket/rest/api-group-commit-statuses/#api-repositories-workspace-repo-slug-commit-commit-statuses-build-post does not explain precisly how this property should be valued. This force me to set the property to null in case of pull request, otherwise, the build status disappear from the webpage and branch restriction and merge conditions will not work.
1 parent cb32459 commit 217b447

File tree

4 files changed

+72
-8
lines changed

4 files changed

+72
-8
lines changed

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

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketCloudApiClient;
3030
import edu.umd.cs.findbugs.annotations.CheckForNull;
3131
import edu.umd.cs.findbugs.annotations.NonNull;
32+
import edu.umd.cs.findbugs.annotations.Nullable;
3233
import hudson.Extension;
3334
import hudson.FilePath;
3435
import hudson.model.Result;
@@ -44,6 +45,7 @@
4445
import java.net.URL;
4546
import jenkins.model.JenkinsLocationConfiguration;
4647
import jenkins.plugins.git.AbstractGitSCMSource;
48+
import jenkins.scm.api.SCMHead;
4749
import jenkins.scm.api.SCMHeadObserver;
4850
import jenkins.scm.api.SCMRevision;
4951
import jenkins.scm.api.SCMRevisionAction;
@@ -96,9 +98,12 @@ static String checkURL(@NonNull String url, BitbucketApi bitbucket) {
9698
}
9799
}
98100

99-
private static void createStatus(@NonNull Run<?, ?> build, @NonNull TaskListener listener,
100-
@NonNull BitbucketApi bitbucket, @NonNull String key, @NonNull String hash)
101-
throws IOException, InterruptedException {
101+
private static void createStatus(@NonNull Run<?, ?> build,
102+
@NonNull TaskListener listener,
103+
@NonNull BitbucketApi bitbucket,
104+
@NonNull String key,
105+
@NonNull String hash,
106+
@Nullable String refName) throws IOException, InterruptedException {
102107

103108
final SCMSource source = SCMSource.SourceByItem.findSource(build.getParent());
104109
if (!(source instanceof BitbucketSCMSource)) {
@@ -161,7 +166,7 @@ private static void createStatus(@NonNull Run<?, ?> build, @NonNull TaskListener
161166

162167
if (state != null) {
163168
BitbucketChangesetCommentNotifier notifier = new BitbucketChangesetCommentNotifier(bitbucket);
164-
notifier.buildStatus(new BitbucketBuildStatus(hash, statusDescription, state, url, key, name));
169+
notifier.buildStatus(new BitbucketBuildStatus(hash, statusDescription, state, url, key, name, refName));
165170
if (result != null) {
166171
listener.getLogger().println("[Bitbucket] Build result notified");
167172
}
@@ -195,18 +200,32 @@ private static void sendNotifications(BitbucketSCMSource source, Run<?, ?> build
195200
.anyMatch(ExcludeOriginPRBranchesSCMHeadFilter.class::isInstance);
196201

197202
String key;
203+
String refName;
198204
BitbucketApi bitbucket;
199205
if (rev instanceof PullRequestSCMRevision) {
200206
listener.getLogger().println("[Bitbucket] Notifying pull request build result");
201207
PullRequestSCMHead head = (PullRequestSCMHead) rev.getHead();
202208
key = getBuildKey(build, head.getOriginName(), shareBuildKeyBetweenBranchAndPR);
209+
/*
210+
* in case of pull request it's not clear at all how to value refname. The
211+
* bitbucket documentation does not help and using values like
212+
* - refs/heads/PR-748;
213+
* - refs/pull-requests/748,
214+
* - refs/pull-requests/feature/test;
215+
* causes the build status disappear from the UI page.
216+
* The only working value is null. If commit is used for two different
217+
* pull requests than you will get double status in both PRs
218+
*/
219+
refName = null; // "refs/pull-requests/" + head.getBranchName();
203220
bitbucket = source.buildBitbucketClient(head);
204221
} else {
205222
listener.getLogger().println("[Bitbucket] Notifying commit build result");
206-
key = getBuildKey(build, rev.getHead().getName(), shareBuildKeyBetweenBranchAndPR);
223+
SCMHead head = rev.getHead();
224+
key = getBuildKey(build, head.getName(), shareBuildKeyBetweenBranchAndPR);
225+
refName = "refs/heads/" + head.getName();
207226
bitbucket = source.buildBitbucketClient();
208227
}
209-
createStatus(build, listener, bitbucket, key, hash);
228+
createStatus(build, listener, bitbucket, key, hash, refName);
210229
}
211230

212231
@CheckForNull

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
import com.fasterxml.jackson.annotation.JsonIgnore;
2727
import edu.umd.cs.findbugs.annotations.NonNull;
28+
import edu.umd.cs.findbugs.annotations.Nullable;
2829
import org.apache.commons.codec.digest.DigestUtils;
2930
import org.kohsuke.accmod.Restricted;
3031
import org.kohsuke.accmod.restrictions.DoNotUse;
@@ -87,17 +88,29 @@ public String toString() {
8788
*/
8889
private String name;
8990

91+
/**
92+
* The fully qualified git reference e.g. refs/heads/master.
93+
*/
94+
private String refname;
95+
9096
// Used for marshalling/unmarshalling
9197
@Restricted(DoNotUse.class)
9298
public BitbucketBuildStatus() {}
9399

94-
public BitbucketBuildStatus(String hash, String description, Status state, String url, String key, String name) {
100+
public BitbucketBuildStatus(String hash,
101+
String description,
102+
@NonNull Status state,
103+
String url,
104+
@NonNull String key,
105+
String name,
106+
@Nullable String refname) {
95107
this.hash = hash;
96108
this.description = description;
97109
this.state = state;
98110
this.url = url;
99111
this.key = DigestUtils.md5Hex(key);
100112
this.name = name;
113+
this.refname = refname;
101114
}
102115

103116
/**
@@ -112,6 +125,7 @@ public BitbucketBuildStatus(@NonNull BitbucketBuildStatus other) {
112125
this.url = other.url;
113126
this.key = other.key;
114127
this.name = other.name;
128+
this.refname = other.refname;
115129
}
116130

117131
public String getHash() {
@@ -162,4 +176,12 @@ public void setName(String name) {
162176
this.name = name;
163177
}
164178

179+
public String getRefname() {
180+
return refname;
181+
}
182+
183+
public void setRefname(String refname) {
184+
this.refname = refname;
185+
}
186+
165187
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
4848
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
4949
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranches;
50+
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBuildStatus;
5051
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerCommit;
5152
import com.cloudbees.jenkins.plugins.bitbucket.server.client.mirror.BitbucketMirrorServerDescriptors;
5253
import com.cloudbees.jenkins.plugins.bitbucket.server.client.mirror.BitbucketMirroredRepositoryDescriptors;
@@ -509,7 +510,7 @@ public void postCommitComment(@NonNull String hash, @NonNull String comment) thr
509510
*/
510511
@Override
511512
public void postBuildStatus(@NonNull BitbucketBuildStatus status) throws IOException, InterruptedException {
512-
BitbucketBuildStatus newStatus = new BitbucketBuildStatus(status);
513+
BitbucketServerBuildStatus newStatus = new BitbucketServerBuildStatus(status);
513514
newStatus.setName(truncateMiddle(newStatus.getName(), 255));
514515

515516
String url = UriTemplate.fromTemplate(API_COMMIT_STATUS_PATH)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.cloudbees.jenkins.plugins.bitbucket.server.client.branch;
2+
3+
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBuildStatus;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
public class BitbucketServerBuildStatus extends BitbucketBuildStatus {
7+
8+
/**
9+
* Copy constructor.
10+
*
11+
* @param other from copy to.
12+
*/
13+
public BitbucketServerBuildStatus(BitbucketBuildStatus other) {
14+
super(other);
15+
}
16+
17+
@JsonProperty("ref")
18+
@Override
19+
public String getRefname() {
20+
return super.getRefname();
21+
}
22+
}

0 commit comments

Comments
 (0)