Skip to content

fix: Cannot setup a pipeline #461

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

Merged
merged 1 commit into from
Aug 7, 2023
Merged
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
Expand Down Expand Up @@ -44,6 +45,7 @@

import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -86,14 +88,45 @@ public class TuleapSCMSource extends AbstractGitSCMSource {
private String credentialsId;
private TuleapAccessToken credentials;

@DataBoundConstructor
public TuleapSCMSource(TuleapProject project, TuleapGitRepository repository) {
this.repository = repository;
this.project = project;
this.projectId = String.valueOf(project.getId());
this.repositoryPath = repository.getPath();
}

@DataBoundConstructor
public TuleapSCMSource(String projectId, String repositoryPath) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is possible, I think it would be better if this constructor also populated this.project and this.projectId itself so that we are always sure of what we have.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I already thought to do that, but I think this is not a good idea because of the DataBoundConstructor, it does some automagic binding, and it may introduce some unwanted side effect which can be hard to debug

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the @DataBounConstructor only taking into account the arguments of the constructior ie projectId and repositoryPath ?

https://javadoc.jenkins.io/component/stapler/org/kohsuke/stapler/DataBoundConstructor.html

The matching is done by using the constructor parameter name

Or are your worried about something else that I don't see ? I haven't looked into this in quite a while...

this.projectId = projectId;
this.repositoryPath = repositoryPath;
}

private void setTuleapProject(TuleapAccessToken accessToken) throws IOException {
if (this.project == null) {
// Keep compatibility with TuleapProject which is only used by the old api
Optional<TuleapProject> project = TuleapClientCommandConfigurer.<Optional<TuleapProject>>newInstance(this.getApiBaseUri())
.withCredentials(accessToken)
.withCommand(new TuleapClientRawCmd.ProjectById(this.projectId))
.configure()
.call();
project.ifPresent(tuleapProject -> this.project = tuleapProject);
}
}

private void setTuleapRepository(TuleapAccessToken accessToken) throws IOException {
if (this.repository == null) {
// Keep compatibility with TuleapGitRepository which is only used by the old api
Optional<TuleapGitRepository> repo = TuleapClientCommandConfigurer
.<Stream<TuleapGitRepository>>newInstance(this.getApiBaseUri())
.withCredentials(accessToken)
.withCommand(new TuleapClientRawCmd.AllRepositoriesByProject(this.projectId))
.configure()
.call()
.distinct().filter(r -> r.getPath().equals(repositoryPath)).findFirst();
repo.ifPresent(repository -> this.repository = repository);
}
}

@NonNull
@Override
protected List<Action> retrieveActions(@NonNull SCMHead head, @CheckForNull SCMHeadEvent event,
Expand All @@ -116,12 +149,14 @@ protected List<Action> retrieveActions(@CheckForNull SCMSourceEvent event, @NonN

@Override
protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer,
@CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException {
@CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException {
try (final TuleapSCMSourceRequest request = new TuleapSCMSourceContext(criteria, observer).withTraits(traits).newRequest(this, listener)) {
TuleapAccessToken credentials = lookupScanCredentials((Item) getOwner(), getApiBaseUri(),
getCredentialsId());
setCredentials(credentials);
setRemoteUrl(getGitBaseUri() + repositoryPath);
setTuleapProject(credentials);
setTuleapRepository(credentials);
if (request.isFetchBranches()) {
LOGGER.info(String.format("Fecthing branches for repository at %s", repositoryPath));
Stream<TuleapBranches> branches = TuleapClientCommandConfigurer.<Stream<TuleapBranches>>newInstance(getApiBaseUri())
Expand Down Expand Up @@ -164,8 +199,7 @@ public SCMSourceCriteria.Probe create(@NotNull TuleapBranchSCMHead head, @Nullab
if (isFork && !request.isRetrieveForkPullRequests()) {
request.listener().getLogger().format("PR id: %s is skipped, Pull Requests from fork are excluded %n", pullRequest.getId());
continue;
}
else if (!isFork && !request.isRetrieveOriginPullRequests()) {
} else if (!isFork && !request.isRetrieveOriginPullRequests()) {
request.listener().getLogger().format("PR id: %s is skipped, Pull Requests from same origin are excluded %n", pullRequest.getId());
continue;
}
Expand Down Expand Up @@ -340,7 +374,7 @@ public void setCredentials(TuleapAccessToken credentials) {
@NotNull
@Override
public SCM build(@NonNull SCMHead scmHead, @CheckForNull SCMRevision scmRevision) {
String repositoryUri = this.getGitBaseUri() + this.project.getShortname() + "/" +this.repository.getName();
String repositoryUri = this.getGitBaseUri() + this.project.getShortname() + "/" + this.repository.getName();
return new TuleapSCMBuilder(scmHead, scmRevision, remoteUrl, credentialsId, repositoryUri).withTraits(traits).build();
}

Expand Down Expand Up @@ -378,8 +412,7 @@ public String getCredentialsId() {
* Sets the credentials used to access the Tuleap REST API (also used as the default credentials for checking
* out sources.
*
* @param credentialsId
* the credentials used to access the Tuleap REST API
* @param credentialsId the credentials used to access the Tuleap REST API
* @since 2.2.0
*/
@DataBoundSetter
Expand Down Expand Up @@ -438,60 +471,102 @@ public List<SCMSourceTrait> getTraitsDefaults() {
@RequirePOST
@Restricted(NoExternalUse.class) // stapler
public FormValidation doCheckCredentialsId(@AncestorInPath Item item, @QueryParameter String apiUri,
@QueryParameter String credentialsId) {
@QueryParameter String credentialsId) {

return checkCredentials(item, apiUri, credentialsId);
}

@RequirePOST
public ListBoxModel doFillCredentialsIdItems(@CheckForNull @AncestorInPath Item context,
@QueryParameter String apiUri, @QueryParameter String credentialsId) {
@QueryParameter String apiUri, @QueryParameter String credentialsId) {
return listScanCredentials(context, apiUri, credentialsId, false);
}

@RequirePOST
@Restricted(NoExternalUse.class) // stapler
public FormValidation doCheckProjectId(
@QueryParameter String projectId) {
return TuleapSCMSourceFormValidator.doCheckProjectId(projectId);
}

@Restricted(NoExternalUse.class) // stapler
@SuppressWarnings("unused") // stapler
@RequirePOST
public ListBoxModel doFillProjectIdItems(@CheckForNull @AncestorInPath Item context,
@QueryParameter String projectId, @QueryParameter String credentialsId) throws IOException {
@QueryParameter String credentialsId) throws IOException {
if (credentialsId.isEmpty()) {
return new StandardListBoxModel().includeEmptyValue();
}

String apiUri = TuleapConfiguration.get().getApiBaseUrl();
final TuleapAccessToken credentials = lookupScanCredentials(context, apiUri, credentialsId);
ListBoxModel result = new ListBoxModel();
Optional<TuleapProject> project = TuleapClientCommandConfigurer.<Optional<TuleapProject>> newInstance(apiUri)
.withCredentials(credentials)
.withCommand(new TuleapClientRawCmd.ProjectById(projectId))
.configure()
.call();
if (project.isPresent()) {
ListBoxModel.Option newItem = new ListBoxModel.Option(project.get().getShortname(),
String.valueOf(project.get().getId()));
result.add(newItem);
ProjectApi projectApi = TuleapApiRetriever.getProjectApi();

StandardListBoxModel result = new StandardListBoxModel();
result.includeEmptyValue();
try {
projectApi.getUserProjects(credentials)
.forEach(project -> {
ListBoxModel.Option newItem = new ListBoxModel.Option(
project.getShortname(),
String.valueOf(project.getId()));
result.add(newItem);
});
} catch (RuntimeException e) {
result.clear();
result.includeEmptyValue();
LOGGER.log(Level.INFO, String.format("Project cannot be retrieved: %s", e.getCause().getMessage()));
}
return result;
}

@RequirePOST
@Restricted(NoExternalUse.class) // stapler
public FormValidation doCheckRepositoryPath(
@QueryParameter String repositoryPath) {
return TuleapSCMSourceFormValidator.doCheckRepositoryPath(repositoryPath);
}

@Restricted(NoExternalUse.class) // stapler
public ListBoxModel doFillRepositoryPathItems(@CheckForNull @AncestorInPath Item context,
@QueryParameter String projectId, @QueryParameter String credentialsId,
@QueryParameter String repositoryPath) throws IOException {
ListBoxModel result = new ListBoxModel();
@RequirePOST
public ListBoxModel doFillRepositoryPathItems(
@CheckForNull @AncestorInPath Item context,
@QueryParameter String projectId,
@QueryParameter String credentialsId
) {
final String apiBaseUrl = TuleapConfiguration.get().getApiBaseUrl();
TuleapAccessToken credentials = lookupScanCredentials(context, apiBaseUrl, credentialsId);
Optional<TuleapGitRepository> repo = TuleapClientCommandConfigurer
.<Stream<TuleapGitRepository>>newInstance(apiBaseUrl)
.withCredentials(credentials)
.withCommand(new TuleapClientRawCmd.AllRepositoriesByProject(projectId))
.configure()
.call()
.distinct().filter(r -> r.getPath().equals(repositoryPath)).findFirst();
if (repo.isPresent()) {
final ListBoxModel.Option newItem = new ListBoxModel.Option(repo.get().getName(), repo.get().getPath());
result.add(newItem);
ProjectApi projectApi = TuleapApiRetriever.getProjectApi();

StandardListBoxModel result = new StandardListBoxModel();
result.includeEmptyValue();

if (credentialsId.isEmpty()) {
return result;
}
if (projectId.isEmpty()) {
return result;
}

try {
List<GitRepository> repositories = projectApi.getGitRepositories(Integer.parseInt(projectId), credentials);
repositories.forEach(gitRepository -> {
final ListBoxModel.Option newItem = new ListBoxModel.Option(
gitRepository.getName(),
gitRepository.getPath());
result.add(newItem);
});
} catch (RuntimeException e) {
result.clear();
result.includeEmptyValue();
LOGGER.log(Level.INFO, String.format("Git repositories' project cannot be retrieved: %s", e.getCause().getMessage()));
}
return result;
}

@NonNull
@Override
protected SCMHeadCategory[] createCategories(){
protected SCMHeadCategory[] createCategories() {
return new SCMHeadCategory[]{
UncategorizedSCMHeadCategory.DEFAULT,
new ChangeRequestSCMHeadCategory(Messages._TuleapSCMSource_ChangeRequestCategory())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;

import hudson.util.FormValidation;

public class TuleapSCMSourceFormValidator {

public static FormValidation doCheckProjectId(
String projectId) {
if (projectId.isEmpty()) {
return FormValidation.error(Messages.TuleapSCMSource_aGitRepositoryIsRequiredError());
}
return FormValidation.ok();
}

public static FormValidation doCheckRepositoryPath(
String repositoryPath) {
if (repositoryPath.isEmpty()) {
return FormValidation.error(Messages.TuleapSCMSource_aGitRepositoryIsRequiredError());
}
return FormValidation.ok();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public static ListBoxModel listScanCredentials(@CheckForNull @AncestorInPath Ite
if (includeEmpty) {
model.includeEmptyValue();
}
return model.includeMatchingAs(
return model.includeEmptyValue().includeMatchingAs(
context instanceof Queue.Task ? ((Queue.Task) context).getDefaultAuthentication() : ACL.SYSTEM,
context, TuleapAccessToken.class, TuleapDomainRequirements(apiUri), allTuleapAccessTokenMatch());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.inject.Guice;
import com.google.inject.Injector;
import io.jenkins.plugins.tuleap_api.client.GitApi;
import io.jenkins.plugins.tuleap_api.client.ProjectApi;
import io.jenkins.plugins.tuleap_api.client.PullRequestApi;
import io.jenkins.plugins.tuleap_api.client.TuleapApiGuiceModule;

Expand All @@ -20,4 +21,8 @@ public static GitApi getGitApi() {
public static PullRequestApi getPullRequestApi() {
return getApiGuiceModule().getInstance(PullRequestApi.class);
}

public static ProjectApi getProjectApi() {
return getApiGuiceModule().getInstance(ProjectApi.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ TuleapCommitNotificationTrait.displayName=Notify build status to Tuleap
WildcardSCMSourceFilterTrait.displayName=Filter repositories by name (wildcards)

TuleapSCMSource.ChangeRequestCategory=Pull Requests
TuleapSCMSource.aTuleapProjectIsRequiredError=A Tuleap Project is required!
TuleapSCMSource.aGitRepositoryIsRequiredError=A git repository is required!
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package org.jenkinsci.plugins.tuleap_git_branch_source;


import hudson.util.FormValidation;
import org.junit.Test;

import static org.junit.Assert.assertEquals;

public class TuleapSCMSourceFormValidatorTest {

@Test
public void testTheFormValidationIsInErrorIfTheProjectIsEmpty() {
FormValidation result = TuleapSCMSourceFormValidator.doCheckProjectId("");
assertEquals(
FormValidation.Kind.ERROR,
result.kind
);
}

@Test
public void testTheFormValidationIsOkIfTheProjectIsGiven() {
FormValidation result = TuleapSCMSourceFormValidator.doCheckProjectId("152");
assertEquals(
FormValidation.Kind.OK,
result.kind
);
}

@Test
public void testTheFormValidationIsOkIfTheRepositoryPathIsEmpty() {
FormValidation result = TuleapSCMSourceFormValidator.doCheckRepositoryPath("");
assertEquals(
FormValidation.Kind.ERROR,
result.kind
);
}

@Test
public void testTheFormValidationIsOkIfTheRepositoryPathIsGiven() {
FormValidation result = TuleapSCMSourceFormValidator.doCheckProjectId("project/repo.git");
assertEquals(
FormValidation.Kind.OK,
result.kind
);
}
}