Skip to content

[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 #930

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

Closed
wants to merge 2 commits into from
Closed
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
45 changes: 45 additions & 0 deletions docs/USER_GUIDE.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,51 @@ For branches and tags, the mirror sync event is used. Thus, at cloning time, the

image::images/screenshot-13.png[scaledwidth=90%]

[id=bitbucket-build-status]
== Bitbucket build status

When a new job build starts, the plugin send notifications to Bitbucket about the build status. An "In progress" notification is sent after complete the git checkout, another notification is sent at the end of the build, the sent value depends by the build result and the configuration given by the trait.

image::images/screenshot-15.png[scaledwidth=90%]

Follow a summary of all possible values:

[cols=3*,options=header]
|===
| Jenkins
| Bitbucket Cloud
| Bitbucket Data Center and Server

| https://javadoc.jenkins.io/hudson/model/Result.html#SUCCESS[SUCCESS]
| SUCCESSFUL
| SUCCESSFUL

| https://javadoc.jenkins.io/hudson/model/Result.html#UNSTABLE[UNSTABLE]
| configurable SUCCESSFUL or FAILED
| configurable SUCCESSFUL or FAILED

| https://javadoc.jenkins.io/hudson/model/Result.html#FAILURE[FAILURE]
| FAILED
| FAILED

| https://javadoc.jenkins.io/hudson/model/Result.html#NOT_BUILT[NOT_BUILT]
| configurable FAILED or STOPPED
| configurable FAILED or CANCELLED

| https://javadoc.jenkins.io/hudson/model/Result.html#ABORTED[ABORTED]
| configurable FAILED or STOPPED
| configurable FAILED or CANCELLED

| null
| INPROGRESS
| INPROGRESS
|===

The STOPPED status prevents merge checks on Cloud, CANCELLED status should prevents merge checks on Data Center

If this does not meet you need you can disable any notification to Bitbucket using the https://github.com/jenkinsci/skip-notifications-trait-plugin/[skip-notifications-trait-plugin] and provide notification about the build status yourself. This can be achieved via a curl shell command or by using build steps provided by the https://github.com/jenkinsci/bitbucket-build-status-notifier-plugin[bitbucket-build-status-notifier-plugin].


[id=bitbucket-misc-config]
== Miscellaneous configuration

Expand Down
Binary file added docs/images/screenshot-15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketCloudApiClient;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.Result;
Expand All @@ -44,6 +45,7 @@
import java.net.URL;
import jenkins.model.JenkinsLocationConfiguration;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadObserver;
import jenkins.scm.api.SCMRevision;
import jenkins.scm.api.SCMRevisionAction;
Expand Down Expand Up @@ -96,9 +98,12 @@
}
}

private static void createStatus(@NonNull Run<?, ?> build, @NonNull TaskListener listener,
@NonNull BitbucketApi bitbucket, @NonNull String key, @NonNull String hash)
throws IOException, InterruptedException {
private static void createStatus(@NonNull Run<?, ?> build,
@NonNull TaskListener listener,
@NonNull BitbucketApi bitbucket,
@NonNull String key,
@NonNull String hash,
@Nullable String refName) throws IOException, InterruptedException {

final SCMSource source = SCMSource.SourceByItem.findSource(build.getParent());
if (!(source instanceof BitbucketSCMSource)) {
Expand Down Expand Up @@ -140,7 +145,7 @@
state = BitbucketBuildStatus.Status.FAILED;
} else if (Result.NOT_BUILT.equals(result)) {
statusDescription = StringUtils.defaultIfBlank(buildDescription, "This commit was not built (probably the build was skipped)");
if (context.disableNotificationForNotBuildJobs()) {
if (context.sendStopNotificationForNotBuildJobs()) {
// Bitbucket Cloud and Server support different build states.
state = (bitbucket instanceof BitbucketCloudApiClient) ? BitbucketBuildStatus.Status.STOPPED : BitbucketBuildStatus.Status.CANCELLED;
} else {
Expand All @@ -161,7 +166,11 @@

if (state != null) {
BitbucketChangesetCommentNotifier notifier = new BitbucketChangesetCommentNotifier(bitbucket);
notifier.buildStatus(new BitbucketBuildStatus(hash, statusDescription, state, url, key, name));
BitbucketBuildStatus buildStatus = new BitbucketBuildStatus(hash, statusDescription, state, url, key, name, refName);
buildStatus.setBuildDuration(build.getDuration());
buildStatus.setBuildNumber(build.getNumber());
// TODO testResults should be provided by an extension point that integrates JUnit or anything else plugin

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

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: testResults should be provided by an extension point that integrates JUnit or anything else plugin
notifier.buildStatus(buildStatus);
if (result != null) {
listener.getLogger().println("[Bitbucket] Build result notified");
}
Expand All @@ -180,33 +189,52 @@
BitbucketSCMSourceContext sourceContext = new BitbucketSCMSourceContext(null,
SCMHeadObserver.none()).withTraits(source.getTraits());
if (sourceContext.notificationsDisabled()) {
listener.getLogger().println("[Bitbucket] Notification is disabled by configuration");
return;
}
SCMRevision rev = SCMRevisionAction.getRevision(source, build);
if (rev == null) {
return;
}
String hash = getHash(rev);
if (hash == null) {
return;
}
boolean shareBuildKeyBetweenBranchAndPR = sourceContext
.filters().stream()
.anyMatch(ExcludeOriginPRBranchesSCMHeadFilter.class::isInstance);

String key;
String refName;
BitbucketApi bitbucket;
if (rev instanceof PullRequestSCMRevision) {
listener.getLogger().println("[Bitbucket] Notifying pull request build result");
PullRequestSCMHead head = (PullRequestSCMHead) rev.getHead();
key = getBuildKey(build, head.getOriginName(), shareBuildKeyBetweenBranchAndPR);
/*
* in case of pull request it's not clear at all how to value refname. The
* bitbucket documentation does not help. Using values like
* - refs/heads/PR-748;
* - refs/pull-requests/748,
* - refs/pull-requests/feature/test;
* causes the build status disappear from the web page.
* The only working value is null. If commit is used for two different
* pull requests than you will get status doubled in both PRs
*/
refName = null; // "refs/pull-requests/" + head.getBranchName();

Check warning on line 224 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 192-224 are not covered by tests
bitbucket = source.buildBitbucketClient(head);
} else {
listener.getLogger().println("[Bitbucket] Notifying commit build result");
key = getBuildKey(build, rev.getHead().getName(), shareBuildKeyBetweenBranchAndPR);
SCMHead head = rev.getHead();
key = getBuildKey(build, head.getName(), shareBuildKeyBetweenBranchAndPR);
if (rev instanceof BitbucketTagSCMRevision || head instanceof BitbucketTagSCMHead) {

Check warning on line 230 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 230 is only partially covered, 2 branches are missing
refName = "refs/tags/" + head.getName();

Check warning on line 231 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotifications.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 231 is not covered by tests
} else {
refName = "refs/heads/" + head.getName();
}
bitbucket = source.buildBitbucketClient();
}
createStatus(build, listener, bitbucket, key, hash);
createStatus(build, listener, bitbucket, key, hash, refName);
}

@CheckForNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.trait.SCMSourceContext;
Expand All @@ -40,20 +41,12 @@
*/
public class BitbucketBuildStatusNotificationsTrait extends SCMSourceTrait {

/**
* Should unstable builds be communicated as success to Bitbucket.
*/
private boolean sendSuccessNotificationForUnstableBuild;

/**
* Aborted jobs must be communicated as stopped to Bitbucket.
*/
private boolean sendStoppedNotificationForAbortBuild;

/**
* Should not build jobs be communicated to Bitbucket.
*/
private boolean disableNotificationForNotBuildJobs;
// seems that this attribute as been moved out to plugin skip-notifications-trait-plugin
@SuppressFBWarnings("UUF_UNUSED_FIELD")
private transient boolean disableNotifications;

/**
* Constructor.
Expand All @@ -72,6 +65,8 @@
}

/**
* Should unstable builds be communicated as success to Bitbucket.
*
* @return if unstable builds will be communicated as successful
*/
public boolean getSendSuccessNotificationForUnstableBuild() {
Expand All @@ -92,7 +87,7 @@
/**
* Return if aborted builds will be communicated as stopped.
*
* @return true will be comunicate to Bitbucket as Stopped/Cancelled build
* @return if will be communicated to Bitbucket as Stopped/Cancelled build
* failed otherwise.
*/
public boolean getSendStoppedNotificationForAbortBuild() {
Expand All @@ -105,7 +100,10 @@
}

/**
* @return if unstable builds will be communicated
* Should not build jobs be communicated as stopped.
*
* @return if will be communicated to Bitbucket as Stopped/Cancelled build
* failed otherwise.
*/
public boolean getDisableNotificationForNotBuildJobs() {
return this.disableNotificationForNotBuildJobs;
Expand All @@ -116,9 +114,11 @@
*/
@Override
protected void decorateContext(SCMSourceContext<?, ?> context) {
((BitbucketSCMSourceContext) context).withDisableNotificationForNotBuildJobs(getDisableNotificationForNotBuildJobs());
((BitbucketSCMSourceContext) context).withSendSuccessNotificationForUnstableBuild(getSendSuccessNotificationForUnstableBuild());
((BitbucketSCMSourceContext) context).withSendStoppedNotificationForAbortBuild(getSendStoppedNotificationForAbortBuild());
if (context instanceof BitbucketSCMSourceContext scmContext) {

Check warning on line 117 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketBuildStatusNotificationsTrait.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 117 is only partially covered, one branch is missing
scmContext.withSendStopNotificationForNotBuildJobs(getDisableNotificationForNotBuildJobs());
scmContext.withSendSuccessNotificationForUnstableBuild(getSendSuccessNotificationForUnstableBuild());
scmContext.withSendStoppedNotificationForAbortBuild(getSendStoppedNotificationForAbortBuild());
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ public class BitbucketSCMSourceContext extends SCMSourceContext<BitbucketSCMSour
private boolean sendStoppedNotificationForAbortBuild;

/**
* {@code false} if not built jobs should be send to Bitbucket.
* {@code false} if not built jobs will be communicated as stopped/cancelled.
*/
private boolean disableNotificationForNotBuildJobs;
private boolean sendStopNotificationForNotBuildJobs;

/**
* Constructor.
Expand Down Expand Up @@ -235,12 +235,12 @@ public final boolean sendSuccessNotificationForUnstableBuild() {
}

/**
* Returns {@code false} if not build jobs should be passed to Bitbucket.
* Returns if not build jobs should be passed as stopped/cancelled to Bitbucket.
*
* @return {@code false} if not build jobs should be passed to Bitbucket.
* @return if not build jobs should be passed as stopped to Bitbucket.
*/
public boolean disableNotificationForNotBuildJobs() {
return disableNotificationForNotBuildJobs;
public boolean sendStopNotificationForNotBuildJobs() {
return sendStopNotificationForNotBuildJobs;
}

/**
Expand Down Expand Up @@ -395,12 +395,12 @@ public final BitbucketSCMSourceContext withSendStoppedNotificationForAbortBuild(
/**
* Defines behaviour of not-built jobs in Bitbucket.
*
* @param disabled {@code false} to report not-built jobs to Bitbucket.
* @param sendStopped {@code false} to consider aborted builds as stopped when notifying Bitbucket.
* @return {@code this} for method chaining.
*/
@NonNull
public final BitbucketSCMSourceContext withDisableNotificationForNotBuildJobs(boolean disabled) {
this.disableNotificationForNotBuildJobs = disabled;
public final BitbucketSCMSourceContext withSendStopNotificationForNotBuildJobs(boolean sendStopped) {
this.sendStopNotificationForNotBuildJobs = sendStopped;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import com.fasterxml.jackson.annotation.JsonIgnore;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.apache.commons.codec.digest.DigestUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.DoNotUse;
Expand Down Expand Up @@ -87,17 +88,39 @@
*/
private String name;

/**
* The fully qualified git reference e.g. refs/heads/master.
*/
private String refname;

/**
* Duration of a completed build in milliseconds.
*/
private long buildDuration;

/**
* A unique identifier of this particular run.
*/
private int buildNumber;

// Used for marshalling/unmarshalling
@Restricted(DoNotUse.class)
public BitbucketBuildStatus() {}

public BitbucketBuildStatus(String hash, String description, Status state, String url, String key, String name) {
public BitbucketBuildStatus(String hash,
String description,
@NonNull Status state,
String url,
@NonNull String key,
String name,
@Nullable String refname) {
this.hash = hash;
this.description = description;
this.state = state;
this.url = url;
this.key = DigestUtils.md5Hex(key);
this.name = name;
this.refname = refname;
}

/**
Expand All @@ -112,6 +135,8 @@
this.url = other.url;
this.key = other.key;
this.name = other.name;
this.refname = other.refname;
this.buildDuration = other.buildDuration;
}

public String getHash() {
Expand Down Expand Up @@ -162,4 +187,28 @@
this.name = name;
}

public String getRefname() {
return refname;
}

public void setRefname(String refname) {
this.refname = refname;
}

Check warning on line 196 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/api/BitbucketBuildStatus.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 195-196 are not covered by tests

public long getBuildDuration() {
return buildDuration;
}

public void setBuildDuration(long buildDuration) {
this.buildDuration = buildDuration;
}

public int getBuildNumber() {
return buildNumber;
}

public void setBuildNumber(int buildNumber) {
this.buildNumber = buildNumber;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranch;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBranches;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerBuildStatus;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.branch.BitbucketServerCommit;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.mirror.BitbucketMirrorServerDescriptors;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.mirror.BitbucketMirroredRepositoryDescriptors;
Expand Down Expand Up @@ -144,7 +145,7 @@ public class BitbucketServerAPIClient extends AbstractBitbucketApi implements Bi
private static final String WEBHOOK_REPOSITORY_PATH = WEBHOOK_BASE_PATH + "/projects/{owner}/repos/{repo}/configurations";
private static final String WEBHOOK_REPOSITORY_CONFIG_PATH = WEBHOOK_REPOSITORY_PATH + "/{id}";

private static final String API_COMMIT_STATUS_PATH = "/rest/build-status/1.0/commits{/hash}";
private static final String API_COMMIT_STATUS_PATH = API_BASE_PATH + "/projects/{owner}/repos/{repo}/commits/{hash}/builds";

private static final String API_MIRRORS_FOR_REPO_PATH = "/rest/mirroring/1.0/repos/{id}/mirrors";
private static final String API_MIRRORS_PATH = "/rest/mirroring/1.0/mirrorServers";
Expand Down Expand Up @@ -509,10 +510,12 @@ public void postCommitComment(@NonNull String hash, @NonNull String comment) thr
*/
@Override
public void postBuildStatus(@NonNull BitbucketBuildStatus status) throws IOException, InterruptedException {
BitbucketBuildStatus newStatus = new BitbucketBuildStatus(status);
BitbucketServerBuildStatus newStatus = new BitbucketServerBuildStatus(status);
newStatus.setName(truncateMiddle(newStatus.getName(), 255));

String url = UriTemplate.fromTemplate(API_COMMIT_STATUS_PATH)
.set("owner", getUserCentricOwner())
.set("repo", repositoryName)
.set("hash", newStatus.getHash())
.expand();
postRequest(url, JsonParser.toJson(newStatus));
Expand Down
Loading
Loading