Skip to content

Commit

Permalink
[JENKINS-53535] Make Bitbucket Server, Owner and repository environme…
Browse files Browse the repository at this point in the history
…nt variables for Bitbucket Team/Project based jobs (#949)

Add as environment variables some bitbucket useful informations like:
- repository name (slug);
- owner name (slug);
- project key;
- the server URL.
  • Loading branch information
nfalco79 authored Dec 29, 2024
1 parent 81c0685 commit 54952c4
Show file tree
Hide file tree
Showing 21 changed files with 194 additions and 35 deletions.
12 changes: 12 additions & 0 deletions docs/USER_GUIDE.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,18 @@ The STOPPED status prevents merge checks on Cloud, CANCELLED status should preve
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-env-var]
== Environment Variables

This plugin contribute to the enviroment with the following variables:

- BITBUCKET_REPOSITORY: the repository name/slug
- BITBUCKET_OWNER: the repository owner name/slug, in Bitbucket Cloud is the equivalent of workspace name
- BITBUCKET_PROJECT_KEY: the project key in which the repository is contained
- BITBUCKET_SERVER_URL: the Bitbucket server URL

These variables were added to allow users to easily integrate calls to Bitbucket's REST APIs into their own pipelines to implement own business logics.

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Util;
import hudson.model.Item;
import hudson.util.FormFillFailure;
Expand All @@ -29,6 +30,10 @@ public static boolean isCloud(BitbucketApi client) {
return client instanceof BitbucketCloudApiClient;
}

public static boolean isCloud(@NonNull String serverURL) {
return StringUtils.startsWith(serverURL, BitbucketCloudEndpoint.SERVER_URL);
}

public static ListBoxModel getFromBitbucket(SCMSourceOwner context,
String serverUrl,
String credentialsId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepositoryProtocol;
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.endpoints.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
Expand Down Expand Up @@ -107,11 +106,11 @@ public BitbucketGitSCMBuilder(@NonNull BitbucketSCMSource scmSource, @NonNull SC
endpoint = new BitbucketServerEndpoint(null, serverURL, false, null);
}

String repositoryUrl = endpoint.getRepositoryUrl(scmSource.getRepoOwner(), scmSource.getRepository());
if (endpoint instanceof BitbucketCloudEndpoint) {
withBrowser(new BitbucketWeb(repositoryUrl));
String repositoryURL = endpoint.getRepositoryUrl(scmSource.getRepoOwner(), scmSource.getRepository());
if (BitbucketApiUtils.isCloud(endpoint.getServerUrl())) {
withBrowser(new BitbucketWeb(repositoryURL));
} else {
withBrowser(new BitbucketServer(repositoryUrl));
withBrowser(new BitbucketServer(repositoryURL));
}

// Test for protocol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMirroredRepository;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketMirroredRepositoryDescriptor;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketProject;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRequestException;
Expand All @@ -44,6 +45,8 @@
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.hooks.HasPullRequests;
import com.cloudbees.jenkins.plugins.bitbucket.impl.extension.BitbucketEnvVarExtension;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.URLUtils;
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
Expand Down Expand Up @@ -75,8 +78,6 @@
import hudson.util.ListBoxModel;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -128,6 +129,7 @@
import org.jenkinsci.Symbol;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.accmod.restrictions.ProtectedExternally;
import org.kohsuke.stapler.AncestorInPath;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
Expand Down Expand Up @@ -385,6 +387,7 @@ public List<SCMSourceTrait> getTraits() {
return Collections.unmodifiableList(traits);
}

@Override
@DataBoundSetter
public void setTraits(@CheckForNull List<SCMSourceTrait> traits) {
this.traits = new ArrayList<>(Util.fixNull(traits));
Expand Down Expand Up @@ -690,7 +693,7 @@ class Skip extends IOException {
boolean fork = !fullName.equalsIgnoreCase(pull.getSource().getRepository().getFullName());
String pullRepoOwner = pull.getSource().getRepository().getOwnerName();
String pullRepository = pull.getSource().getRepository().getRepositoryName();
final BitbucketApi pullBitbucket = fork && originBitbucket instanceof BitbucketCloudApiClient
final BitbucketApi pullBitbucket = fork && BitbucketApiUtils.isCloud(originBitbucket)
? BitbucketApiFactory.newInstance(
getServerUrl(),
authenticator(),
Expand Down Expand Up @@ -999,7 +1002,7 @@ private BitbucketCommit findPRDestinationCommit(BitbucketPullRequest pr, TaskLis
}

@Override
public SCM build(SCMHead head, SCMRevision revision) {
public SCM build(@NonNull SCMHead head, @CheckForNull SCMRevision revision) {
initCloneLinks();

String scmCredentialsId = credentialsId;
Expand Down Expand Up @@ -1027,26 +1030,38 @@ public SCM build(SCMHead head, SCMRevision revision) {
scmExtension = new GitClientAuthenticatorExtension(null);
}

String projectKey = getProjectKey();

return new BitbucketGitSCMBuilder(this, head, revision, scmCredentialsId)
.withExtension(scmExtension)
.withExtension(new BitbucketEnvVarExtension(getRepoOwner(), getRepository(), projectKey, getServerUrl()))
.withCloneLinks(primaryCloneLinks, mirrorCloneLinks)
.withTraits(traits)
.build();
}

@CheckForNull
@Restricted(ProtectedExternally.class)
protected String getProjectKey() {
String projectKey = null;
try {
BitbucketProject project = buildBitbucketClient().getRepository().getProject();
if (project != null) {
projectKey = project.getKey();
}
} catch (IOException | InterruptedException e) {
LOGGER.severe("Failure getting the project key of repository " + getRepository() + " : " + e.getMessage());
}
return projectKey;
}

private void setPrimaryCloneLinks(List<BitbucketHref> links) {
links.forEach(link -> {
if (StringUtils.startsWithIgnoreCase(link.getName(), "http")) {
try {
URL linkURL = new URL(link.getHref());
// Remove the username from URL because it will be set into the GIT_URL variable
// credentials used to clone or for push/pull could be different than this will cause a failure
// Restore the behaviour before mirror link feature.
URL cleanURL = new URL(linkURL.getProtocol(), linkURL.getHost(), linkURL.getPort(), linkURL.getFile());
link.setHref(cleanURL.toExternalForm());
} catch (MalformedURLException e) {
// do nothing, URL can not be parsed, leave as is
}
// Remove the username from URL because it will be set into the GIT_URL variable
// credentials used to clone or for push/pull could be different than this will cause a failure
// Restore the behaviour before mirror link feature.
link.setHref(URLUtils.removeAuthority(link.getHref()));
}
});
primaryCloneLinks = links;
Expand Down Expand Up @@ -1112,13 +1127,15 @@ protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event,
result.add(new BitbucketDefaultBranch(repoOwner, repository, defaultBranch));
}
UriTemplate template;
if (BitbucketCloudEndpoint.SERVER_URL.equals(getServerUrl())) {
if (BitbucketApiUtils.isCloud(getServerUrl())) {
template = UriTemplate.fromTemplate(getServerUrl() + CLOUD_REPO_TEMPLATE);
} else {
template = UriTemplate.fromTemplate(getServerUrl() + SERVER_REPO_TEMPLATE);
}
template.set("owner", repoOwner).set("repo", repository);
String url = template.expand();
String url = template
.set("owner", repoOwner)
.set("repo", repository)
.expand();
result.add(new BitbucketLink("icon-bitbucket-repo", url));
result.add(new ObjectMetadataAction(r.getRepositoryName(), null, url));
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import hudson.plugins.git.extensions.GitSCMExtension;
import java.net.URISyntaxException;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.jgit.transport.RefSpec;
import org.eclipse.jgit.transport.URIish;
import org.jenkinsci.plugins.gitclient.FetchCommand;
Expand All @@ -18,6 +17,7 @@
* If specified commit hashes are not found in repository then fetch
* specified branches from remote.
*/
//TODO be attention serialized in config.xml of the job as extension child of hudson.plugins.git.GitSCM. Provide a xml alias when move to package com.cloudbees.jenkins.plugins.bitbucket.impl.extension
public class FallbackToOtherRepositoryGitSCMExtension extends GitSCMExtension {

private final String cloneLink;
Expand Down Expand Up @@ -49,7 +49,7 @@ public Revision decorateRevisionToBuild(
String branch = branchWithHash.getBranch();
return new RefSpec("+refs/heads/" + branch + ":refs/remotes/" + remoteName + "/" + branch);
})
.collect(Collectors.toList());
.toList();

if (!refSpecs.isEmpty()) {
FetchCommand fetchCommand = git.fetch_();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import hudson.plugins.git.extensions.GitSCMExtension;
import org.jenkinsci.plugins.gitclient.GitClient;

// TODO be attention serialized in config.xml of the job as extension child of hudson.plugins.git.GitSCM. Provide a xml alias when move to package com.cloudbees.jenkins.plugins.bitbucket.impl.extension
public class GitClientAuthenticatorExtension extends GitSCMExtension {

// TODO remove this because it is serialized in config.xml with username and secret (password or token could change/expiry specially with OAuth2)
private final StandardUsernameCredentials credentials;

public GitClientAuthenticatorExtension(StandardUsernameCredentials credentials) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.client;

import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBuildStatus;
Expand All @@ -50,6 +49,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.client.repository.UserRoleInRepository;
import com.cloudbees.jenkins.plugins.bitbucket.credentials.BitbucketUsernamePasswordAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.filesystem.BitbucketSCMFile;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.internal.api.AbstractBitbucketApi;
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import com.damnhandy.uri.template.UriTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.client;

import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestEvent;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPushEvent;
import com.cloudbees.jenkins.plugins.bitbucket.client.events.BitbucketCloudPullRequestEvent;
import com.cloudbees.jenkins.plugins.bitbucket.client.events.BitbucketCloudPushEvent;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,7 @@ public void configureRequest(HttpRequest request) {
@Override
public StandardUsernameCredentials getCredentialsForSCM() {
try {
return new UsernamePasswordCredentialsImpl(
CredentialsScope.GLOBAL, getId(), null, "x-token-auth", getToken().getAccessToken());
return new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL, getId(), null, "x-token-auth", getToken().getAccessToken());
} catch (FormException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@

import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSource;
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSourceContext;
import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMHead;
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMRevision;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
import com.cloudbees.jenkins.plugins.bitbucket.server.events.NativeServerPullRequestEvent;
import com.google.common.base.Ascii;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketSCMSourceContext;
import com.cloudbees.jenkins.plugins.bitbucket.BitbucketTagSCMHead;
import com.cloudbees.jenkins.plugins.bitbucket.BranchSCMHead;
import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMHead;
import com.cloudbees.jenkins.plugins.bitbucket.PullRequestSCMRevision;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.pullrequest.BitbucketServerPullRequest;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.repository.BitbucketServerRepository;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.cloudbees.jenkins.plugins.bitbucket.impl.extension;

import com.cloudbees.jenkins.plugins.bitbucket.impl.util.URLUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.extensions.GitSCMExtension;
import java.util.Map;

public class BitbucketEnvVarExtension extends GitSCMExtension {

private final String owner;
private final String repository;
private final String projectKey;
private final String serverURL;

public BitbucketEnvVarExtension(@Nullable String owner, @NonNull String repository, @Nullable String projectKey, @NonNull String serverURL) {
this.owner = owner;
this.repository = repository;
this.projectKey = projectKey;
this.serverURL = URLUtils.removeAuthority(serverURL);
}

/**
* Contribute additional environment variables about the target branch.
* Since source branch could be from a forked repository, for which the
* credentials in use are not allowed to do nothing, is discarded.
*
* @param scm GitSCM used as reference
* @param env environment variables to be added
*/
@Override
public void populateEnvironmentVariables(GitSCM scm, Map<String, String> env) {
env.put("BITBUCKET_REPOSITORY", repository);
env.put("BITBUCKET_OWNER", owner);
env.put("BITBUCKET_PROJECT_KEY", projectKey);
env.put("BITBUCKET_SERVER_URL", serverURL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.cloudbees.jenkins.plugins.bitbucket;
package com.cloudbees.jenkins.plugins.bitbucket.impl.util;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.cloudbees.jenkins.plugins.bitbucket.impl.util;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.net.MalformedURLException;
import java.net.URL;

public final class URLUtils {

private URLUtils() {
}

@Nullable
public static String removeAuthority(@CheckForNull String url) {
if (url != null) {
try {
URL linkURL = new URL(url);
URL cleanURL = new URL(linkURL.getProtocol(), linkURL.getHost(), linkURL.getPort(), linkURL.getFile());
return cleanURL.toExternalForm();
} catch (MalformedURLException e) {
// do nothing, URL can not be parsed, leave as is
}
}
return url;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.server.client;

import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketAuthenticator;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketBuildStatus;
Expand All @@ -42,6 +41,7 @@
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketEndpointConfiguration;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketServerEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.filesystem.BitbucketSCMFile;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.internal.api.AbstractBitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerVersion;
import com.cloudbees.jenkins.plugins.bitbucket.server.BitbucketServerWebhookImplementation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
*/
package com.cloudbees.jenkins.plugins.bitbucket.server.client;

import com.cloudbees.jenkins.plugins.bitbucket.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPullRequestEvent;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketPushEvent;
import com.cloudbees.jenkins.plugins.bitbucket.impl.util.JsonParser;
import com.cloudbees.jenkins.plugins.bitbucket.server.events.BitbucketServerPullRequestEvent;
import com.cloudbees.jenkins.plugins.bitbucket.server.events.BitbucketServerPushEvent;
import edu.umd.cs.findbugs.annotations.CheckForNull;
Expand Down
Loading

0 comments on commit 54952c4

Please sign in to comment.