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

Add as environment variables some bitbucket useful informations like:
- repository name (slug);
- owner name (slug);
- project key;
- the server URL.
  • Loading branch information
nfalco79 committed Dec 29, 2024
1 parent 81c0685 commit ba76107
Show file tree
Hide file tree
Showing 20 changed files with 182 additions and 35 deletions.
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) {

Check warning on line 1049 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 1049 is only partially covered, one branch is missing
projectKey = project.getKey();
}
} catch (IOException | InterruptedException e) {
LOGGER.severe("Failure getting the project key of repository " + getRepository() + " : " + e.getMessage());

Check warning on line 1053 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/BitbucketSCMSource.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 1050-1053 are not covered by tests
}
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

Check warning on line 20 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/FallbackToOtherRepositoryGitSCMExtension.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: 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();

Check warning on line 52 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/FallbackToOtherRepositoryGitSCMExtension.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 52 is not covered by tests

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

Check warning on line 9 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/GitClientAuthenticatorExtension.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: 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)

Check warning on line 12 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/GitClientAuthenticatorExtension.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: 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());

Check warning on line 88 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/credentials/BitbucketOAuthAuthenticator.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 88 is not covered by tests
} 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;

Check warning

Code scanning / Jenkins Security Scan

Jenkins: Plaintext password storage Warning

Field should be reviewed whether it stores a password and is serialized to disk: 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) {

Check warning on line 15 in src/main/java/com/cloudbees/jenkins/plugins/bitbucket/impl/util/URLUtils.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 15 is only partially covered, one branch is missing
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 ba76107

Please sign in to comment.