From 62ab6fce865945ebbf0f5c2f7894c80551fe8888 Mon Sep 17 00:00:00 2001 From: Michael Fitoussi <16884890+mifitous@users.noreply.github.com> Date: Tue, 21 Feb 2023 11:04:57 +0200 Subject: [PATCH] Fix casting to GitLabMergeRequestTrigger and add more debug logs in Jenkins for plugin. Add option to alwaysIgnoreMRWorkInProgress (#266) --- .../AbstractGitLabSCMHeadEvent.java | 5 +- .../GitLabMergeRequestCommentTrigger.java | 52 ++-- .../GitLabMergeRequestSCMEvent.java | 266 ++++++++++-------- .../GitLabMergeRequestTrigger.java | 9 +- .../GitLabSCMNavigator.java | 167 +++++------ .../gitlabbranchsource/GitLabSCMSource.java | 2 +- .../GitLabSCMSourceContext.java | 11 + .../GitLabSCMSourceRequest.java | 126 +++++---- .../GitLabWebHookListener.java | 14 +- .../WebhookListenerBuildConditionsTrait.java | 26 +- .../helpers/GitLabAvatarCache.java | 96 ++++--- .../helpers/GitLabPipelineStatusNotifier.java | 3 +- .../action/GitlabAction.java | 9 +- .../servers/GitLabServer.java | 179 ++++++------ .../servers/GitLabServers.java | 47 ++-- .../GitLabPersonalAccessTokenCreator.java | 138 +++++---- .../config.jelly | 17 +- 17 files changed, 643 insertions(+), 524 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/AbstractGitLabSCMHeadEvent.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/AbstractGitLabSCMHeadEvent.java index 816a5611..40f8110a 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/AbstractGitLabSCMHeadEvent.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/AbstractGitLabSCMHeadEvent.java @@ -15,7 +15,7 @@ public abstract class AbstractGitLabSCMHeadEvent extends SCMHeadEvent { - private static final Logger LOGGER = Logger.getLogger(AbstractGitLabSCMHeadEvent.class.getName()); + public static final Logger LOGGER = Logger.getLogger(AbstractGitLabSCMHeadEvent.class.getName()); private static final Pattern NONE_HASH_PATTERN = Pattern.compile("^0+$"); @@ -30,7 +30,8 @@ static Type typeOf(E pushEvent) { } else if (hasBefore) { result = Type.REMOVED; } else { - LOGGER.warning("Received push event with both \"before\" and \"after\" set to non-existing revision. Assuming removal."); + LOGGER.warning( + "Received push event with both \"before\" and \"after\" set to non-existing revision. Assuming removal."); result = Type.REMOVED; } return result; diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestCommentTrigger.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestCommentTrigger.java index e1ce0b73..feaf3182 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestCommentTrigger.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestCommentTrigger.java @@ -18,8 +18,7 @@ public class GitLabMergeRequestCommentTrigger extends AbstractGitLabJobTrigger { - public static final Logger LOGGER = Logger - .getLogger(GitLabMergeRequestCommentTrigger.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabMergeRequestCommentTrigger.class.getName()); public GitLabMergeRequestCommentTrigger(NoteEvent payload) { super(payload); @@ -28,11 +27,11 @@ public GitLabMergeRequestCommentTrigger(NoteEvent payload) { @Override public void isMatch() { if (getPayload().getObjectAttributes().getNoteableType() - .equals(NoteEvent.NoteableType.MERGE_REQUEST)) { + .equals(NoteEvent.NoteableType.MERGE_REQUEST)) { Long mergeRequestId = getPayload().getMergeRequest().getIid(); final Pattern mergeRequestJobNamePattern = Pattern - .compile("^MR-" + mergeRequestId + "\\b.*$", - Pattern.CASE_INSENSITIVE); + .compile("^MR-" + mergeRequestId + "\\b.*$", + Pattern.CASE_INSENSITIVE); final String commentBody = getPayload().getObjectAttributes().getNote(); final String commentUrl = getPayload().getObjectAttributes().getUrl(); try (ACLContext ctx = ACL.as(ACL.SYSTEM)) { @@ -49,35 +48,33 @@ public void isMatch() { } GitLabSCMSource gitLabSCMSource = (GitLabSCMSource) source; final GitLabSCMSourceContext sourceContext = new GitLabSCMSourceContext( - null, SCMHeadObserver.none()) - .withTraits(gitLabSCMSource.getTraits()); + null, SCMHeadObserver.none()) + .withTraits(gitLabSCMSource.getTraits()); if (!sourceContext.mrCommentTriggerEnabled()) { continue; } - if (gitLabSCMSource.getProjectId() == getPayload().getMergeRequest() - .getTargetProjectId() && isTrustedMember(gitLabSCMSource, sourceContext.getOnlyTrustedMembersCanTrigger())) { + if (gitLabSCMSource.getProjectId() == getPayload().getMergeRequest().getTargetProjectId() + && isTrustedMember(gitLabSCMSource, sourceContext.getOnlyTrustedMembersCanTrigger())) { for (Job job : owner.getAllJobs()) { if (mergeRequestJobNamePattern.matcher(job.getName()).matches()) { String expectedCommentBody = sourceContext.getCommentBody(); Pattern pattern = Pattern.compile(expectedCommentBody, - Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + Pattern.CASE_INSENSITIVE | Pattern.DOTALL); if (commentBody == null || pattern.matcher(commentBody) - .matches()) { + .matches()) { ParameterizedJobMixIn.scheduleBuild2(job, 0, - new CauseAction( - new GitLabMergeRequestCommentCause(commentUrl, getPayload()))); + new CauseAction( + new GitLabMergeRequestCommentCause(commentUrl, getPayload()))); LOGGER.log(Level.INFO, - "Triggered build for {0} due to MR comment on {1}", - new Object[]{ - job.getFullName(), - getPayload().getProject().getPathWithNamespace() - } - ); + "Triggered build for {0} due to MR comment on {1}", + new Object[] { + job.getFullName(), + getPayload().getProject().getPathWithNamespace() + }); } else { LOGGER.log(Level.INFO, - "MR comment does not match the trigger build string ({0}) for {1}", - new Object[]{expectedCommentBody, job.getFullName()} - ); + "MR comment does not match the trigger build string ({0}) for {1}", + new Object[] { expectedCommentBody, job.getFullName() }); } break; } @@ -88,10 +85,9 @@ public void isMatch() { } if (!jobFound) { LOGGER.log(Level.INFO, "MR comment on {0} did not match any job", - new Object[]{ - getPayload().getProject().getPathWithNamespace() - } - ); + new Object[] { + getPayload().getProject().getPathWithNamespace() + }); } } } @@ -99,11 +95,11 @@ public void isMatch() { private boolean isTrustedMember(GitLabSCMSource gitLabSCMSource, boolean check) { // Return true if only trusted members can trigger option is not checked - if(!check) { + if (!check) { return true; } AccessLevel permission = gitLabSCMSource.getMembers() - .get(getPayload().getUser().getUsername()); + .get(getPayload().getUser().getUsername()); if (permission != null) { switch (permission) { case MAINTAINER: diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java index 35225171..c62f558a 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java @@ -18,141 +18,165 @@ import org.gitlab4j.api.webhook.MergeRequestEvent; public class GitLabMergeRequestSCMEvent extends AbstractGitLabSCMHeadEvent { - private static final Logger LOGGER = Logger.getLogger(GitLabMergeRequestSCMEvent.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabMergeRequestSCMEvent.class.getName()); - public GitLabMergeRequestSCMEvent(MergeRequestEvent mrEvent, String origin) { - super(typeOf(mrEvent), mrEvent, origin); - } + public GitLabMergeRequestSCMEvent(MergeRequestEvent mrEvent, String origin) { + super(typeOf(mrEvent), mrEvent, origin); + } - // TODO: Take care of "locked" state - private static Type typeOf(MergeRequestEvent mrEvent) { - if (mrEvent.getObjectAttributes().getState().equals("closed")) { - return Type.REMOVED; - } else if (mrEvent.getObjectAttributes().getState().equals("opened") - && mrEvent.getObjectAttributes().getCreatedAt() - .equals(mrEvent.getObjectAttributes().getUpdatedAt())) { - return Type.CREATED; + // TODO: Take care of "locked" state + private static Type typeOf(MergeRequestEvent mrEvent) { + if (mrEvent.getObjectAttributes().getState().equals("closed")) { + return Type.REMOVED; + } else if (mrEvent.getObjectAttributes().getState().equals("opened") + && mrEvent.getObjectAttributes().getCreatedAt() + .equals(mrEvent.getObjectAttributes().getUpdatedAt())) { + return Type.CREATED; + } + return Type.UPDATED; } - return Type.UPDATED; - } - @Override - public String descriptionFor(@NonNull SCMNavigator navigator) { - String state = getPayload().getObjectAttributes().getState(); - if (state != null) { - switch (state) { - case "opened": - case "reopened": - case "closed": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " state:" + state + " action:" + getPayload().getObjectAttributes().getAction() - + " in project " + getPayload() - .getProject().getName(); - } + @Override + public String descriptionFor(@NonNull SCMNavigator navigator) { + String state = getPayload().getObjectAttributes().getState(); + LOGGER.log(Level.FINE, "descriptionFor() event:{0} state:{1} action:{2} project:{3}", + new Object[] { + getPayload().getObjectAttributes().getIid(), + String.valueOf(state), + getPayload().getObjectAttributes().getAction(), + getPayload().getProject().getName() + }); + if (state != null) { + switch (state) { + case "opened": + case "reopened": + case "closed": + return "Merge request !" + getPayload().getObjectAttributes().getIid() + + " state:" + state + + " action:" + getPayload().getObjectAttributes().getAction() + + " in project " + getPayload().getProject().getName() + + " changes:" + getPayload().getChanges().toString(); + } + } + return "Merge request !" + getPayload().getObjectAttributes().getIid() + + " event in project " + getPayload().getProject().getName(); } - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " event in project " + getPayload().getProject() - .getName(); - } - @Override - public boolean isMatch(@NonNull GitLabSCMNavigator navigator) { - return navigator.getNavigatorProjects() - .contains(getPayload().getProject().getPathWithNamespace()); - } + @Override + public boolean isMatch(@NonNull GitLabSCMNavigator navigator) { + return navigator.getNavigatorProjects() + .contains(getPayload().getProject().getPathWithNamespace()); + } - @Override - public boolean isMatch(@NonNull GitLabSCMSource source) { - return getPayload().getObjectAttributes().getTargetProjectId() - .equals(source.getProjectId()); - } + @Override + public boolean isMatch(@NonNull GitLabSCMSource source) { + return getPayload().getObjectAttributes().getTargetProjectId() + .equals(source.getProjectId()); + } - @NonNull - @Override - public String getSourceName() { - return getPayload().getProject().getPathWithNamespace(); - } + @NonNull + @Override + public String getSourceName() { + return getPayload().getProject().getPathWithNamespace(); + } - @Override - public String descriptionFor(@NonNull SCMSource source) { - String state = getPayload().getObjectAttributes().getState(); - if (state != null) { - switch (state) { - case "opened": - case "reopened": - case "closed": - return "Merge request !" + getPayload().getObjectAttributes().getIid() + " state:" + state - + " action:" + getPayload().getObjectAttributes().getAction(); - } + @Override + public String descriptionFor(@NonNull SCMSource source) { + String state = getPayload().getObjectAttributes().getState(); + LOGGER.log(Level.FINE, "descriptionFor() event:{0} state:{1} action:{2}", + new Object[] { + getPayload().getObjectAttributes().getIid(), + String.valueOf(state), + getPayload().getObjectAttributes().getAction() + }); + if (state != null) { + switch (state) { + case "opened": + case "reopened": + case "closed": + return "Merge request !" + getPayload().getObjectAttributes().getIid() + + " state:" + state + + " action:" + getPayload().getObjectAttributes().getAction() + + " changes:" + getPayload().getChanges().toString(); + } + } + return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; } - return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; - } - @Override - public String description() { - String state = getPayload().getObjectAttributes().getState(); - if (state != null) { - switch (state) { - case "opened": - case "reopened": - case "closed": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " state:" + state + " action:" + getPayload().getObjectAttributes().getAction() - + " in project " + getPayload() - .getProject().getPathWithNamespace(); - } + @Override + public String description() { + String state = getPayload().getObjectAttributes().getState(); + LOGGER.log(Level.FINE, "description() event:{0} state:{1} action:{2} project:{3}", + new Object[] { + getPayload().getObjectAttributes().getIid(), + String.valueOf(state), + getPayload().getObjectAttributes().getAction(), + getPayload().getProject().getPathWithNamespace() + }); + if (state != null) { + switch (state) { + case "opened": + case "reopened": + case "closed": + return "Merge request !" + getPayload().getObjectAttributes().getIid() + + " state:" + state + + " action:" + getPayload().getObjectAttributes().getAction() + + " in project " + + getPayload().getProject().getPathWithNamespace() + + " changes:" + getPayload().getChanges().toString(); + } + } + return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; } - return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; - } - @NonNull - @Override - public Map headsFor(GitLabSCMSource source) { - Map result = new HashMap<>(); - try (GitLabSCMSourceRequest request = new GitLabSCMSourceContext(null, - SCMHeadObserver.none()) - .withTraits(source.getTraits()) - .newRequest(source, null)) { - MergeRequestEvent.ObjectAttributes m = getPayload().getObjectAttributes(); - Map> strategies = request.getMRStrategies(); - boolean fork = !getPayload().getObjectAttributes().getSourceProjectId() - .equals(getPayload().getObjectAttributes().getTargetProjectId()); - String originOwner = getPayload().getUser().getUsername(); - String originProjectPath = m.getSource().getPathWithNamespace(); - for (ChangeRequestCheckoutStrategy strategy : strategies.get(fork)) { - MergeRequestSCMHead h = new MergeRequestSCMHead( - "MR-" + m.getIid() + (strategies.get(fork).size() > 1 ? "-" + strategy.name() - .toLowerCase(Locale.ENGLISH) : ""), - m.getIid(), - new BranchSCMHead(m.getTargetBranch()), - strategy, - fork - ? new SCMHeadOrigin.Fork(originProjectPath) - : SCMHeadOrigin.DEFAULT, - originOwner, - originProjectPath, - m.getSourceBranch(), - m.getTitle()); - result.put(h, m.getState().equals("closed") - ? null - : new MergeRequestSCMRevision( - h, - new BranchSCMRevision( - h.getTarget(), - "HEAD"), - new BranchSCMRevision( - new BranchSCMHead(h.getOriginName()), - m.getLastCommit().getId()))); - } - } catch (IOException e) { - LOGGER.log(Level.SEVERE, "Exception caught: " + e, e); + @NonNull + @Override + public Map headsFor(GitLabSCMSource source) { + Map result = new HashMap<>(); + try (GitLabSCMSourceRequest request = new GitLabSCMSourceContext(null, + SCMHeadObserver.none()) + .withTraits(source.getTraits()) + .newRequest(source, null)) { + MergeRequestEvent.ObjectAttributes m = getPayload().getObjectAttributes(); + Map> strategies = request.getMRStrategies(); + boolean fork = !getPayload().getObjectAttributes().getSourceProjectId() + .equals(getPayload().getObjectAttributes().getTargetProjectId()); + String originOwner = getPayload().getUser().getUsername(); + String originProjectPath = m.getSource().getPathWithNamespace(); + for (ChangeRequestCheckoutStrategy strategy : strategies.get(fork)) { + MergeRequestSCMHead h = new MergeRequestSCMHead( + "MR-" + m.getIid() + (strategies.get(fork).size() > 1 + ? "-" + strategy.name().toLowerCase(Locale.ENGLISH) + : ""), + m.getIid(), + new BranchSCMHead(m.getTargetBranch()), + strategy, + fork ? new SCMHeadOrigin.Fork(originProjectPath) + : SCMHeadOrigin.DEFAULT, + originOwner, + originProjectPath, + m.getSourceBranch(), + m.getTitle()); + result.put(h, m.getState().equals("closed") + ? null + : new MergeRequestSCMRevision( + h, + new BranchSCMRevision( + h.getTarget(), + "HEAD"), + new BranchSCMRevision( + new BranchSCMHead(h.getOriginName()), + m.getLastCommit().getId()))); + } + } catch (IOException e) { + LOGGER.log(Level.SEVERE, "Exception caught: " + e, e); + } + return result; } - return result; - } - @Override - public GitLabWebHookCause getCause() { - return new GitLabWebHookCause().fromMergeRequest(getPayload()); - } + @Override + public GitLabWebHookCause getCause() { + return new GitLabWebHookCause().fromMergeRequest(getPayload()); + } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java index ae82f681..99efee30 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java @@ -9,8 +9,7 @@ public class GitLabMergeRequestTrigger extends GitLabMergeRequestSCMEvent { - public static final Logger LOGGER = Logger - .getLogger(GitLabMergeRequestTrigger.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabMergeRequestTrigger.class.getName()); public GitLabMergeRequestTrigger(MergeRequestEvent mrEvent, String origin) { super(mrEvent, origin); @@ -38,6 +37,12 @@ private boolean shouldBuild(MergeRequestEvent mrEvent, GitLabSCMSourceContext co String action = attributes.getAction(); boolean shouldBuild = true; + if (attributes.getWorkInProgress() && context.alwaysIgnoreMRWorkInProgress()) { + LOGGER.log(Level.FINE, "shouldBuild for MR-{0} set to false due to WorkInProgress=true.", + getPayload().getObjectAttributes().getIid()); + return false; + } + if (action != null) { if (action.equals("update") && context.alwaysIgnoreNonCodeRelatedUpdates()) { if (mrEvent.getChanges().getAssignees() != null) { diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMNavigator.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMNavigator.java index 0624d3bb..bfd6a414 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMNavigator.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMNavigator.java @@ -80,7 +80,7 @@ public class GitLabSCMNavigator extends SCMNavigator { - private static final Logger LOGGER = Logger.getLogger(GitLabSCMNavigator.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabSCMNavigator.class.getName()); /** * The owner of the projects to navigate. */ @@ -173,8 +173,10 @@ public List>> getTraits() { /** * Sets the behavioral traits that are applied to this navigator and any {@link - * GitLabSCMSource} instances it discovers. The new traits will take affect on the next - * navigation through any of the {@link #visitSources(SCMSourceObserver)} overloads or {@link + * GitLabSCMSource} instances it discovers. The new traits will take affect on + * the next + * navigation through any of the {@link #visitSources(SCMSourceObserver)} + * overloads or {@link * #visitSource(String, SCMSourceObserver)}. * * @param traits the new behavioral traits. @@ -205,8 +207,10 @@ private GitLabOwner getGitlabOwner(GitLabApi gitLabApi) { /** * Sets the behavioral traits that are applied to this navigator and any {@link - * GitLabSCMSource} instances it discovers. The new traits will take affect on the next - * navigation through any of the {@link #visitSources(SCMSourceObserver)} overloads or {@link + * GitLabSCMSource} instances it discovers. The new traits will take affect on + * the next + * navigation through any of the {@link #visitSources(SCMSourceObserver)} + * overloads or {@link * #visitSource(String, SCMSourceObserver)}. * * @param traits the new behavioral traits. @@ -225,9 +229,9 @@ protected String id() { @Override public void visitSources(@NonNull final SCMSourceObserver observer) - throws IOException, InterruptedException { + throws IOException, InterruptedException { GitLabSCMNavigatorContext context = new GitLabSCMNavigatorContext() - .withTraits(traits); + .withTraits(traits); try (GitLabSCMNavigatorRequest request = context.newRequest(this, observer)) { GitLabApi gitLabApi = apiBuilder(observer.getContext(), serverName); getGitlabOwner(gitLabApi); @@ -235,7 +239,7 @@ public void visitSources(@NonNull final SCMSourceObserver observer) if (gitlabOwner instanceof GitLabUser) { // Even returns the group projects owned by the user projects = gitLabApi.getProjectApi() - .getUserProjects(projectOwner, new ProjectFilter().withOwned(true)); + .getUserProjects(projectOwner, new ProjectFilter().withOwned(true)); } else { isGroup = true; GroupProjectsFilter groupProjectsFilter = new GroupProjectsFilter(); @@ -264,50 +268,49 @@ public void visitSources(@NonNull final SCMSourceObserver observer) getNavigatorProjects().add(projectPathWithNamespace); if (StringUtils.isEmpty(p.getDefaultBranch())) { observer.getListener().getLogger() - .format("%nIgnoring project with empty repository %s%n", - HyperlinkNote.encodeTo(p.getWebUrl(), p.getName())); + .format("%nIgnoring project with empty repository %s%n", + HyperlinkNote.encodeTo(p.getWebUrl(), p.getName())); continue; } if (p.getArchived() && context.isExcludeArchivedRepositories()) { observer.getListener().getLogger() - .format("%nIgnoring archived project %s%n", - HyperlinkNote.encodeTo(p.getWebUrl(), p.getName())); + .format("%nIgnoring archived project %s%n", + HyperlinkNote.encodeTo(p.getWebUrl(), p.getName())); continue; } observer.getListener().getLogger().format("%nChecking project %s%n", - HyperlinkNote.encodeTo(p.getWebUrl(), projectName)); + HyperlinkNote.encodeTo(p.getWebUrl(), projectName)); try { GitLabServer server = GitLabServers.get().findServer(serverName); if (webhookGitLabApi != null && webHookUrl != null) { observer.getListener().getLogger().format("Web hook %s%n", GitLabHookCreator - .createWebHookWhenMissing(webhookGitLabApi, projectPathWithNamespace, - webHookUrl, server.getSecretTokenAsPlainText())); + .createWebHookWhenMissing(webhookGitLabApi, projectPathWithNamespace, + webHookUrl, server.getSecretTokenAsPlainText())); } } catch (GitLabApiException e) { observer.getListener().getLogger() - .format("Cannot set web hook: %s%n", e.getReason()); + .format("Cannot set web hook: %s%n", e.getReason()); } if (request.process(projectName, - name -> new GitLabSCMSourceBuilder( - getId() + "::" + projectPathWithNamespace, - serverName, - credentialsId, - projectOwner, - projectPathWithNamespace, - name - ).withTraits(traits).build(), - null, - (Witness) (name, isMatch) -> { - if (isMatch) { - observer.getListener().getLogger() - .format("Proposing %s%n", name); - } else { - observer.getListener().getLogger().format("Ignoring %s%n", name); - } - })) { + name -> new GitLabSCMSourceBuilder( + getId() + "::" + projectPathWithNamespace, + serverName, + credentialsId, + projectOwner, + projectPathWithNamespace, + name).withTraits(traits).build(), + null, + (Witness) (name, isMatch) -> { + if (isMatch) { + observer.getListener().getLogger() + .format("Proposing %s%n", name); + } else { + observer.getListener().getLogger().format("Ignoring %s%n", name); + } + })) { observer.getListener().getLogger() - .format("%n%d projects were processed (query complete)%n", - count); + .format("%n%d projects were processed (query complete)%n", + count); return; } } @@ -319,7 +322,8 @@ public void visitSources(@NonNull final SCMSourceObserver observer) } @NonNull - private String getProjectName(GitLabApi gitLabApi, int projectNamingStrategy, Project project) throws URISyntaxException { + private String getProjectName(GitLabApi gitLabApi, int projectNamingStrategy, Project project) + throws URISyntaxException { String fullPath = project.getPathWithNamespace(); String projectName; switch (projectNamingStrategy) { @@ -331,7 +335,7 @@ private String getProjectName(GitLabApi gitLabApi, int projectNamingStrategy, Pr case 2: // Project name projectName = project.getNameWithNamespace() - .replace(String.format("%s / ", getGitlabOwner(gitLabApi).getFullName()), ""); + .replace(String.format("%s / ", getGitlabOwner(gitLabApi).getFullName()), ""); break; case 3: // Contextual project path @@ -354,9 +358,9 @@ private PersonalAccessToken getWebHookCredentials(SCMSourceOwner owner) { return null; } GitLabSCMNavigatorContext navigatorContext = new GitLabSCMNavigatorContext() - .withTraits(traits); + .withTraits(traits); GitLabSCMSourceContext ctx = new GitLabSCMSourceContext(null, SCMHeadObserver.none()) - .withTraits(navigatorContext.traits()); + .withTraits(navigatorContext.traits()); GitLabHookRegistration webhookMode = ctx.webhookRegistration(); switch (webhookMode) { case DISABLE: @@ -385,8 +389,8 @@ private PersonalAccessToken getWebHookCredentials(SCMSourceOwner owner) { @NonNull @Override protected List retrieveActions(@NonNull SCMNavigatorOwner owner, - SCMNavigatorEvent event, - @NonNull TaskListener listener) throws IOException, InterruptedException { + SCMNavigatorEvent event, + @NonNull TaskListener listener) throws IOException, InterruptedException { getGitlabOwner(owner); String fullName = gitlabOwner.getFullName(); String webUrl = gitlabOwner.getWebUrl(); @@ -397,10 +401,9 @@ protected List retrieveActions(@NonNull SCMNavigatorOwner owner, } List result = new ArrayList<>(); result.add(new ObjectMetadataAction( - Util.fixEmpty(fullName), - description, - webUrl) - ); + Util.fixEmpty(fullName), + description, + webUrl)); if (StringUtils.isNotBlank(avatarUrl)) { result.add(new GitLabAvatar(avatarUrl)); } @@ -409,8 +412,8 @@ protected List retrieveActions(@NonNull SCMNavigatorOwner owner, listener.getLogger().println("Web URL unspecified"); } else { listener.getLogger().printf("%s URL: %s%n", gitlabOwner.getWord(), - HyperlinkNote - .encodeTo(webUrl, StringUtils.defaultIfBlank(fullName, webUrl))); + HyperlinkNote + .encodeTo(webUrl, StringUtils.defaultIfBlank(fullName, webUrl))); } return result; } @@ -418,22 +421,21 @@ protected List retrieveActions(@NonNull SCMNavigatorOwner owner, @Override public void afterSave(@NonNull SCMNavigatorOwner owner) { GitLabSCMNavigatorContext navigatorContext = new GitLabSCMNavigatorContext() - .withTraits(traits); + .withTraits(traits); GitLabSCMSourceContext ctx = new GitLabSCMSourceContext(null, SCMHeadObserver.none()) - .withTraits(navigatorContext.traits()); + .withTraits(navigatorContext.traits()); GitLabHookRegistration systemhookMode = ctx.systemhookRegistration(); GitLabHookCreator.register(owner, this, systemhookMode); } public PersonalAccessToken credentials(SCMSourceOwner owner) { return CredentialsMatchers.firstOrNull( - lookupCredentials( - PersonalAccessToken.class, - owner, - Jenkins.getAuthentication(), - fromUri(getServerUrlFromName(serverName)).build() - ), credentials -> credentials instanceof PersonalAccessToken - ); + lookupCredentials( + PersonalAccessToken.class, + owner, + Jenkins.getAuthentication(), + fromUri(getServerUrlFromName(serverName)).build()), + credentials -> credentials instanceof PersonalAccessToken); } @Symbol("gitlab") @@ -444,8 +446,8 @@ public static class DescriptorImpl extends SCMNavigatorDescriptor implements Ico private GitLabSCMSource.DescriptorImpl delegate; public static FormValidation doCheckProjectOwner(@AncestorInPath SCMSourceOwner context, - @QueryParameter String projectOwner, - @QueryParameter String serverName) { + @QueryParameter String projectOwner, + @QueryParameter String serverName) { if (projectOwner.equals("")) { return FormValidation.ok(); } @@ -488,14 +490,13 @@ public String getIconFilePathPattern() { @Override public SCMNavigator newInstance(String name) { - GitLabSCMNavigator navigator = - new GitLabSCMNavigator(""); + GitLabSCMNavigator navigator = new GitLabSCMNavigator(""); navigator.setTraits(getTraitsDefaults()); return navigator; } public ListBoxModel doFillServerNameItems(@AncestorInPath SCMSourceOwner context, - @QueryParameter String serverName) { + @QueryParameter String serverName) { if (context == null) { if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { // must have admin if you want the list without a context @@ -515,8 +516,8 @@ public ListBoxModel doFillServerNameItems(@AncestorInPath SCMSourceOwner context } public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner context, - @QueryParameter String serverName, - @QueryParameter String credentialsId) { + @QueryParameter String serverName, + @QueryParameter String credentialsId) { StandardListBoxModel result = new StandardListBoxModel(); if (context == null) { if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { @@ -526,41 +527,41 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath SCMSourceOwner cont } } else { if (!context.hasPermission(Item.EXTENDED_READ) - && !context.hasPermission(CredentialsProvider.USE_ITEM)) { - // must be able to read the configuration or use the item credentials if you want the list + && !context.hasPermission(CredentialsProvider.USE_ITEM)) { + // must be able to read the configuration or use the item credentials if you + // want the list result.includeCurrentValue(credentialsId); return result; } } result.includeEmptyValue(); result.includeMatchingAs( - context instanceof Queue.Task - ? ((Queue.Task) context).getDefaultAuthentication() - : ACL.SYSTEM, - context, - StandardUsernameCredentials.class, - fromUri(getServerUrlFromName(serverName)).build(), - GitClient.CREDENTIALS_MATCHER - ); + context instanceof Queue.Task + ? ((Queue.Task) context).getDefaultAuthentication() + : ACL.SYSTEM, + context, + StandardUsernameCredentials.class, + fromUri(getServerUrlFromName(serverName)).build(), + GitClient.CREDENTIALS_MATCHER); return result; } @SuppressWarnings("unused") // jelly public List>> getTraitsDescriptorLists() { - GitLabSCMSource.DescriptorImpl sourceDescriptor = - Jenkins.get() + GitLabSCMSource.DescriptorImpl sourceDescriptor = Jenkins.get() .getDescriptorByType(GitLabSCMSource.DescriptorImpl.class); List> all = new ArrayList<>(); all.addAll(SCMNavigatorTrait - ._for(this, GitLabSCMNavigatorContext.class, GitLabSCMSourceBuilder.class)); + ._for(this, GitLabSCMNavigatorContext.class, GitLabSCMSourceBuilder.class)); all.addAll(SCMSourceTrait._for(sourceDescriptor, GitLabSCMSourceContext.class, null)); all.addAll(SCMSourceTrait._for(sourceDescriptor, null, GitLabSCMBuilder.class)); Set> dedup = new HashSet<>(); - for (Iterator> iterator = all.iterator(); iterator.hasNext(); ) { + for (Iterator> iterator = all.iterator(); iterator.hasNext();) { SCMTraitDescriptor d = iterator.next(); if (dedup.contains(d) - || d instanceof GitBrowserSCMSourceTrait.DescriptorImpl) { - // remove any we have seen already and ban the browser configuration as it will always be github + || d instanceof GitBrowserSCMSourceTrait.DescriptorImpl) { + // remove any we have seen already and ban the browser configuration as it will + // always be github iterator.remove(); } else { dedup.add(d); @@ -568,17 +569,17 @@ public List>> getTraitsDescriptor } List>> result = new ArrayList<>(); NamedArrayList - .select(all, "Projects", new NamedArrayList.Predicate>() { + .select(all, "Projects", new NamedArrayList.Predicate>() { @Override public boolean test(SCMTraitDescriptor scmTraitDescriptor) { return scmTraitDescriptor instanceof SCMNavigatorTraitDescriptor; } }, - true, result); + true, result); NamedArrayList.select(all, "Within project", NamedArrayList .anyOf(NamedArrayList.withAnnotation(Discovery.class), - NamedArrayList.withAnnotation(Selection.class)), - true, result); + NamedArrayList.withAnnotation(Selection.class)), + true, result); NamedArrayList.select(all, "Additional", null, true, result); return result; } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java index 5b61df58..ba295591 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java @@ -104,7 +104,7 @@ public class GitLabSCMSource extends AbstractGitSCMSource { - private static final Logger LOGGER = Logger.getLogger(GitLabSCMSource.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabSCMSource.class.getName()); private final String serverName; private final String projectOwner; private final String projectPath; diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java index ed6b553e..38824a50 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java @@ -67,6 +67,8 @@ public class GitLabSCMSourceContext private boolean alwaysIgnoreNonCodeRelatedUpdates = false; + private boolean alwaysIgnoreMRWorkInProgress = false; + public GitLabSCMSourceContext(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer) { super(criteria, observer); @@ -172,6 +174,10 @@ public boolean alwaysIgnoreNonCodeRelatedUpdates() { return alwaysIgnoreNonCodeRelatedUpdates; } + public boolean alwaysIgnoreMRWorkInProgress() { + return alwaysIgnoreMRWorkInProgress; + } + public final String getCommentBody() { return commentBody; } @@ -337,4 +343,9 @@ public final GitLabSCMSourceContext withAlwaysIgnoreNonCodeRelatedUpdates(boolea this.alwaysIgnoreNonCodeRelatedUpdates = enabled; return this; } + + public final GitLabSCMSourceContext withAlwaysIgnoreMRWorkInProgress(boolean enabled) { + this.alwaysIgnoreMRWorkInProgress = enabled; + return this; + } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceRequest.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceRequest.java index 98bebfa3..d656f196 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceRequest.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceRequest.java @@ -28,7 +28,7 @@ public class GitLabSCMSourceRequest extends SCMSourceRequest { - private static final Logger LOGGER = Logger.getLogger(GitLabSCMSourceRequest.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabSCMSourceRequest.class.getName()); /** * {@code true} if branch details need to be fetched. */ @@ -46,29 +46,34 @@ public class GitLabSCMSourceRequest extends SCMSourceRequest { */ private final boolean fetchForkMRs; /** - * The {@link ChangeRequestCheckoutStrategy} to create for each origin merge request. + * The {@link ChangeRequestCheckoutStrategy} to create for each origin merge + * request. */ @NonNull private final Set originMRStrategies; /** - * The {@link ChangeRequestCheckoutStrategy} to create for each fork merge request. + * The {@link ChangeRequestCheckoutStrategy} to create for each fork merge + * request. */ @NonNull private final Set forkMRStrategies; /** - * The set of merge request numbers that the request is scoped to or {@code null} if the request + * The set of merge request numbers that the request is scoped to or + * {@code null} if the request * is not limited. */ @CheckForNull private final Set requestedMergeRequestNumbers; /** - * The set of origin branch names that the request is scoped to or {@code null} if the request + * The set of origin branch names that the request is scoped to or {@code null} + * if the request * is not limited. */ @CheckForNull private final Set requestedOriginBranchNames; /** - * The set of tag names that the request is scoped to or {@code null} if the request is not + * The set of tag names that the request is scoped to or {@code null} if the + * request is not * limited. */ @CheckForNull @@ -106,23 +111,23 @@ public class GitLabSCMSourceRequest extends SCMSourceRequest { /** * Constructor. * - * @param source the source. - * @param context the context. + * @param source the source. + * @param context the context. * @param listener the listener. */ GitLabSCMSourceRequest(SCMSource source, GitLabSCMSourceContext context, - TaskListener listener) { + TaskListener listener) { super(source, context, listener); fetchBranches = context.wantBranches(); fetchTags = context.wantTags(); fetchOriginMRs = context.wantOriginMRs(); fetchForkMRs = context.wantForkMRs(); originMRStrategies = fetchOriginMRs && !context.originMRStrategies().isEmpty() - ? Collections.unmodifiableSet(EnumSet.copyOf(context.originMRStrategies())) - : Collections.emptySet(); + ? Collections.unmodifiableSet(EnumSet.copyOf(context.originMRStrategies())) + : Collections.emptySet(); forkMRStrategies = fetchForkMRs && !context.forkMRStrategies().isEmpty() - ? Collections.unmodifiableSet(EnumSet.copyOf(context.forkMRStrategies())) - : Collections.emptySet(); + ? Collections.unmodifiableSet(EnumSet.copyOf(context.forkMRStrategies())) + : Collections.emptySet(); Set includes = context.observer().getIncludes(); if (includes != null) { Set mergeRequestNumbers = new HashSet<>(includes.size()); @@ -196,9 +201,11 @@ public final boolean isFetchForkMRs() { } /** - * Returns the {@link ChangeRequestCheckoutStrategy} to create for each origin merge request. + * Returns the {@link ChangeRequestCheckoutStrategy} to create for each origin + * merge request. * - * @return the {@link ChangeRequestCheckoutStrategy} to create for each origin merge request. + * @return the {@link ChangeRequestCheckoutStrategy} to create for each origin + * merge request. */ @NonNull public final Set getOriginMRStrategies() { @@ -206,9 +213,11 @@ public final Set getOriginMRStrategies() { } /** - * Returns the {@link ChangeRequestCheckoutStrategy} to create for each fork merge request. + * Returns the {@link ChangeRequestCheckoutStrategy} to create for each fork + * merge request. * - * @return the {@link ChangeRequestCheckoutStrategy} to create for each fork merge request. + * @return the {@link ChangeRequestCheckoutStrategy} to create for each fork + * merge request. */ @NonNull public final Set getForkMRStrategies() { @@ -216,33 +225,39 @@ public final Set getForkMRStrategies() { } /** - * Returns the {@link ChangeRequestCheckoutStrategy} to create for merge requests of the + * Returns the {@link ChangeRequestCheckoutStrategy} to create for merge + * requests of the * specified type. * - * @param fork {@code true} to return strategies for the fork merge requests, {@code false} for - * origin merge requests. - * @return the {@link ChangeRequestCheckoutStrategy} to create for each merge request. + * @param fork {@code true} to return strategies for the fork merge requests, + * {@code false} for + * origin merge requests. + * @return the {@link ChangeRequestCheckoutStrategy} to create for each merge + * request. */ @NonNull public final Set getMRStrategies(boolean fork) { if (fork) { return fetchForkMRs ? getForkMRStrategies() - : Collections.emptySet(); + : Collections.emptySet(); } return fetchOriginMRs ? getOriginMRStrategies() - : Collections.emptySet(); + : Collections.emptySet(); } /** - * Returns the {@link ChangeRequestCheckoutStrategy} to create for each merge request. + * Returns the {@link ChangeRequestCheckoutStrategy} to create for each merge + * request. * - * @return a map of the {@link ChangeRequestCheckoutStrategy} to create for each merge request - * keyed by whether the strategy applies to forks or not ({@link Boolean#FALSE} is the key for - * origin merge requests) + * @return a map of the {@link ChangeRequestCheckoutStrategy} to create for each + * merge request + * keyed by whether the strategy applies to forks or not + * ({@link Boolean#FALSE} is the key for + * origin merge requests) */ public final Map> getMRStrategies() { Map> result = new HashMap<>(); - for (Boolean fork : new Boolean[]{Boolean.TRUE, Boolean.FALSE}) { + for (Boolean fork : new Boolean[] { Boolean.TRUE, Boolean.FALSE }) { result.put(fork, getMRStrategies(fork)); } return result; @@ -251,8 +266,9 @@ public final Map> getMRStrategies() /** * Returns requested merge request numbers. * - * @return the requested merge request numbers or {@code null} if the request was not scoped to - * a subset of merge requests. + * @return the requested merge request numbers or {@code null} if the request + * was not scoped to + * a subset of merge requests. */ @CheckForNull public final Set getRequestedMergeRequestNumbers() { @@ -262,8 +278,9 @@ public final Set getRequestedMergeRequestNumbers() { /** * Gets requested origin branch names. * - * @return the requested origin branch names or {@code null} if the request was not scoped to a - * subset of branches. + * @return the requested origin branch names or {@code null} if the request was + * not scoped to a + * subset of branches. */ @CheckForNull public final Set getRequestedOriginBranchNames() { @@ -273,8 +290,9 @@ public final Set getRequestedOriginBranchNames() { /** * Gets requested tag names. * - * @return the requested tag names or {@code null} if the request was not scoped to a subset of - * tags. + * @return the requested tag names or {@code null} if the request was not scoped + * to a subset of + * tags. */ @CheckForNull public final Set getRequestedTagNames() { @@ -282,12 +300,15 @@ public final Set getRequestedTagNames() { } /** - * Returns the merge request details or an empty list if either the request did not specify to - * {@link #isFetchMRs()} or if the merge request details have not been provided by {@link + * Returns the merge request details or an empty list if either the request did + * not specify to + * {@link #isFetchMRs()} or if the merge request details have not been provided + * by {@link * #setMergeRequests(Iterable)} yet. * - * @return the details of merge requests, may be limited by {@link #getRequestedMergeRequestNumbers()} - * or may be empty if not {@link #isFetchMRs()} + * @return the details of merge requests, may be limited by + * {@link #getRequestedMergeRequestNumbers()} + * or may be empty if not {@link #isFetchMRs()} */ @NonNull public Iterable getMergeRequests() { @@ -304,7 +325,8 @@ public void setMergeRequests(@CheckForNull Iterable mergeRequests) } /** - * Returns the branch details or an empty list if either the request did not specify to {@link + * Returns the branch details or an empty list if either the request did not + * specify to {@link * #isFetchBranches()} or if the branch details have not been provided by {@link * #setBranches(Iterable)} yet. * @@ -325,7 +347,8 @@ public final void setBranches(@CheckForNull Iterable branches) { } /** - * Returns the tag details or an empty list if either the request did not specify to {@link + * Returns the tag details or an empty list if either the request did not + * specify to {@link * #isFetchTags()} ()} or if the tag details have not been provided by {@link * #setTags(Iterable)} yet. * @@ -346,32 +369,36 @@ public final void setTags(@CheckForNull Iterable tags) { } /** - * Returns the Map of project {@link Member} or {@code null} if those details have not been + * Returns the Map of project {@link Member} or {@code null} if those details + * have not been * provided yet. * - * @return the Map of project {@link Member} or {@code null} if those details have not been - * provided yet. + * @return the Map of project {@link Member} or {@code null} if those details + * have not been + * provided yet. */ public final HashMap getMembers() { return members; } /** - * Provides the Map of project {@link Member} username and {@link AccessLevel} of the member. + * Provides the Map of project {@link Member} username and {@link AccessLevel} + * of the member. * - * @param members the Map of project {@link Member} username and {@link AccessLevel} of the - * member. + * @param members the Map of project {@link Member} username and + * {@link AccessLevel} of the + * member. */ public final void setMembers(@CheckForNull HashMap members) { this.members = members; } - /** * Returns the {@link GitLabApi} to use for the request. * - * @return the {@link GitLabApi} to use for the request or {@code null} if caller should - * establish their own. + * @return the {@link GitLabApi} to use for the request or {@code null} if + * caller should + * establish their own. */ @CheckForNull public GitLabApi getGitLabApi() { @@ -387,7 +414,6 @@ public void setGitLabApi(@CheckForNull GitLabApi gitLabApi) { this.gitLabApi = gitLabApi; } - /** * Returns the permissions of the supplied user. * diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabWebHookListener.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabWebHookListener.java index b310d3c1..ebcad673 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabWebHookListener.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabWebHookListener.java @@ -40,7 +40,7 @@ public void onNoteEvent(NoteEvent noteEvent) { @Override public void onMergeRequestEvent(MergeRequestEvent mrEvent) { LOGGER.log(Level.FINE, mrEvent.toString()); - GitLabMergeRequestSCMEvent trigger = new GitLabMergeRequestSCMEvent(mrEvent, origin); + GitLabMergeRequestTrigger trigger = new GitLabMergeRequestTrigger(mrEvent, origin); fireTrigger(trigger, mrEvent.getProject().getWebUrl()); } @@ -70,8 +70,8 @@ private void fireTrigger(final SCMHeadEvent trigger, final String projectUrl) private boolean findImmediateHookTrigger(@Nullable final GitLabServer projectServer) { if (projectServer == null) { LOGGER.log( - Level.WARNING, - "Falling back to no immediate trigger"); + Level.WARNING, + "Falling back to no immediate trigger"); return false; } @@ -81,8 +81,8 @@ private boolean findImmediateHookTrigger(@Nullable final GitLabServer projectSer private long findTriggerDelay(@Nullable final GitLabServer projectServer) { if (projectServer == null) { LOGGER.log( - Level.WARNING, - "Falling back to default trigger delay equal GitLab caching timeout"); + Level.WARNING, + "Falling back to default trigger delay equal GitLab caching timeout"); return GITLAB_CACHING_TIMEOUT; } @@ -95,13 +95,13 @@ private long findTriggerDelay(@Nullable final GitLabServer projectServer) { } private GitLabServer findProjectServer(final String projectUrl) { - for (GitLabServer server: GitLabServers.get().getServers()) { + for (GitLabServer server : GitLabServers.get().getServers()) { if (projectUrl.startsWith(server.getServerUrl())) { return server; } } LOGGER.log(Level.WARNING, - String.format("No GitLab server for project URL: %s", projectUrl)); + String.format("No GitLab server for project URL: %s", projectUrl)); return null; } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java index f2e6908a..46138ff2 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java @@ -47,6 +47,11 @@ public class WebhookListenerBuildConditionsTrait extends SCMSourceTrait { */ private boolean alwaysIgnoreNonCodeRelatedUpdates = false; + /** + * Always ignore webhook if MR is WIP + */ + private boolean alwaysIgnoreMRWorkInProgress = false; + /** * Constructor for stapler. */ @@ -65,7 +70,8 @@ protected void decorateContext(SCMSourceContext context) { .withAlwaysIgnoreMRUnApproval(getAlwaysIgnoreMRUnApproval()) .withAlwaysIgnoreMRApproved(getAlwaysIgnoreMRApproved()) .withAlwaysIgnoreMRUnApproved(getAlwaysIgnoreMRUnApproved()) - .withAlwaysIgnoreNonCodeRelatedUpdates(getAlwaysIgnoreNonCodeRelatedUpdates()); + .withAlwaysIgnoreNonCodeRelatedUpdates(getAlwaysIgnoreNonCodeRelatedUpdates()) + .withAlwaysIgnoreMRWorkInProgress(getAlwaysIgnoreMRWorkInProgress()); } } @@ -156,6 +162,15 @@ public boolean getAlwaysIgnoreNonCodeRelatedUpdates() { return alwaysIgnoreNonCodeRelatedUpdates; } + /** + * Do not run build on MR WIP + * + * @return false to run build on non-code related MR updates + */ + public boolean getAlwaysIgnoreMRWorkInProgress() { + return alwaysIgnoreMRWorkInProgress; + } + /** * Setter for stapler to set the alwaysBuildMROpen of the WebhookListener */ @@ -213,4 +228,13 @@ public void setAlwaysIgnoreMRUnApproved(boolean alwaysIgnoreMRUnApproved) { public void setAlwaysIgnoreNonCodeRelatedUpdates(boolean alwaysIgnoreNonCodeRelatedUpdates) { this.alwaysIgnoreNonCodeRelatedUpdates = alwaysIgnoreNonCodeRelatedUpdates; } + + /** + * Setter for stapler to set the alwaysIgnoreMRWorkInProgress of the + * WebhookListener + */ + @DataBoundSetter + public void setAlwaysIgnoreMRWorkInProgress(boolean alwaysIgnoreMRWorkInProgress) { + this.alwaysIgnoreMRWorkInProgress = alwaysIgnoreMRWorkInProgress; + } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabAvatarCache.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabAvatarCache.java index a534135f..df798305 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabAvatarCache.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabAvatarCache.java @@ -53,14 +53,15 @@ import static java.awt.RenderingHints.VALUE_INTERPOLATION_BICUBIC; /** - * An avatar cache that will serve URLs that have been recently registered through {@link #buildUrl(String, String)} + * An avatar cache that will serve URLs that have been recently registered + * through {@link #buildUrl(String, String)} */ @Extension public class GitLabAvatarCache implements UnprotectedRootAction { /** * Our logger. */ - private static final Logger LOGGER = Logger.getLogger(GitLabSCMNavigator.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabSCMNavigator.class.getName()); /** * The cache of entries. Unused entries will be removed over time. */ @@ -69,15 +70,15 @@ public class GitLabAvatarCache implements UnprotectedRootAction { * A background thread pool to refresh images. */ private final ExecutorService service = new ThreadPoolExecutor(0, 4, - 1L, TimeUnit.SECONDS, new SynchronousQueue(), - new NamingThreadFactory(new DaemonThreadFactory(), getClass().getName()) - ); + 1L, TimeUnit.SECONDS, new SynchronousQueue(), + new NamingThreadFactory(new DaemonThreadFactory(), getClass().getName())); /** * The lock to ensure we prevent concurrent requests for the same URL. */ private final Object serviceLock = new Object(); /** - * The iterator that searches for unused entries. The search is amortized over every access. + * The iterator that searches for unused entries. The search is amortized over + * every access. */ private Iterator> iterator = null; @@ -104,13 +105,13 @@ public static String buildUrl(String url, String size) { // seed the cache instance.getCacheEntry(key, url); return UriTemplate.buildFromTemplate(j.getRootUrlFromRequest()) - .literal(instance.getUrlName()) - .path("key") - .query("size") - .build() - .set("key", key) - .set("size", size) - .expand(); + .literal(instance.getUrlName()) + .path("key") + .query("size") + .build() + .set("key", key) + .set("size", size) + .expand(); } private static BufferedImage scaleImage(BufferedImage src, int size) { @@ -127,12 +128,14 @@ private static BufferedImage scaleImage(BufferedImage src, int size) { } boolean flushSrc = false; if (newWidth <= src.getWidth() * 6 / 7 && newHeight <= src.getWidth() * 6 / 7) { - // when scaling down, you get better image quality if you scale down in multiple rounds + // when scaling down, you get better image quality if you scale down in multiple + // rounds // see https://community.oracle.com/docs/DOC-983611 // we scale each round by 6/7 = ~85% as this gives nicer looking images int curWidth = src.getWidth(); int curHeight = src.getHeight(); - // we want to break the rounds and do the final round and centre when the src image is this size + // we want to break the rounds and do the final round and centre when the src + // image is this size final int penultimateSize = size * 7 / 6; while (true) { curWidth = curWidth - curWidth / 7; @@ -144,7 +147,8 @@ private static BufferedImage scaleImage(BufferedImage src, int size) { BufferedImage tmp = new BufferedImage(curWidth, curHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g = tmp.createGraphics(); try { - // important, if we don't set these two hints then scaling will not work headless + // important, if we don't set these two hints then scaling will not work + // headless g.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY); g.scale(((double) curWidth) / src.getWidth(), ((double) curHeight) / src.getHeight()); @@ -162,7 +166,8 @@ private static BufferedImage scaleImage(BufferedImage src, int size) { BufferedImage tmp = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB); Graphics2D g = tmp.createGraphics(); try { - // important, if we don't set these two hints then scaling will not work headless + // important, if we don't set these two hints then scaling will not work + // headless g.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BICUBIC); g.setRenderingHint(KEY_ALPHA_INTERPOLATION, VALUE_ALPHA_INTERPOLATION_QUALITY); g.scale(((double) newWidth) / src.getWidth(), ((double) newHeight) / src.getHeight()); @@ -178,7 +183,8 @@ private static BufferedImage scaleImage(BufferedImage src, int size) { } /** - * Generates a consistent (for any given seed) 5x5 symmetric pixel avatar that should be unique but recognizable. + * Generates a consistent (for any given seed) 5x5 symmetric pixel avatar that + * should be unique but recognizable. * * @param seed the seed. * @param size the size. @@ -198,10 +204,12 @@ private static BufferedImage generateAvatar(String seed, int size) { Graphics2D g = canvas.createGraphics(); try { // we want the colour in the range 16-245 to prevent pure white and pure black - // 0xdf == 1101111 so we throw away the 32 place and add in 16 to give 16 on either side + // 0xdf == 1101111 so we throw away the 32 place and add in 16 to give 16 on + // either side g.setColor(new Color(bytes[0] & 0xdf + 16, bytes[1] & 0xdf + 16, bytes[2] & 0xdf + 16)); int pSize = size / 5; - // likely there will be some remainder from dividing by 5, so half the remainder will be used + // likely there will be some remainder from dividing by 5, so half the remainder + // will be used // as an offset to centre the image int pOffset = (size - pSize * 5) / 2; for (int y = 0; y < 5; y++) { @@ -274,30 +282,30 @@ public HttpResponse doDynamic(StaplerRequest req, @QueryParameter String size) { final CacheEntry avatar = getCacheEntry(key, null); if (avatar == null || !(avatar.url.startsWith("http://") || avatar.url.startsWith("https://"))) { // we will generate avatars if the URL is not HTTP based - // since the url string will not magically turn itself into a HTTP url this avatar is immutable + // since the url string will not magically turn itself into a HTTP url this + // avatar is immutable return new ImageResponse( - generateAvatar(avatar == null ? "" : avatar.url, targetSize), - true, - System.currentTimeMillis(), - "max-age=365000000, immutable, public" - ); + generateAvatar(avatar == null ? "" : avatar.url, targetSize), + true, + System.currentTimeMillis(), + "max-age=365000000, immutable, public"); } if (avatar.pending() && avatar.image == null) { - // serve a temporary avatar until we get the remote one, no caching as we could have the real deal + // serve a temporary avatar until we get the remote one, no caching as we could + // have the real deal // real soon now return new ImageResponse( - generateAvatar(avatar.url, targetSize), - true, - -1L, - "no-cache, public" - ); + generateAvatar(avatar.url, targetSize), + true, + -1L, + "no-cache, public"); } long since = req.getDateHeader("If-Modified-Since"); if (avatar.lastModified <= since) { return new HttpResponse() { @Override public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) - throws IOException, ServletException { + throws IOException, ServletException { rsp.addDateHeader("Last-Modified", avatar.lastModified); rsp.addHeader("Cache-control", "max-age=3600, public"); rsp.sendError(HttpServletResponse.SC_NOT_MODIFIED); @@ -307,11 +315,10 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod if (avatar.image == null) { // we can retry in an hour return new ImageResponse( - generateAvatar(avatar.url, targetSize), - true, - -1L, - "max-age=3600, public" - ); + generateAvatar(avatar.url, targetSize), + true, + -1L, + "max-age=3600, public"); } BufferedImage image = avatar.image; @@ -327,8 +334,10 @@ public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object nod * Retrieves the entry from the cache. * * @param key the cache key. - * @param url the URL to fetch if the entry is missing or {@code null} to perform a read-only check. - * @return the entry or {@code null} if a read-only check found no matching entry. + * @param url the URL to fetch if the entry is missing or {@code null} to + * perform a read-only check. + * @return the entry or {@code null} if a read-only check found no matching + * entry. */ @Nullable private CacheEntry getCacheEntry(@NonNull final String key, @Nullable final String url) { @@ -466,7 +475,7 @@ public ImageResponse(BufferedImage image, boolean flushImage, long lastModified, @Override public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) - throws IOException, ServletException { + throws IOException, ServletException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ImageIO.write(image, "png", bos); @@ -511,7 +520,7 @@ public CacheEntry call() throws Exception { // if we don't know the length then 8k is what we will use length = length > 0 ? Math.min(16384, length) : 8192; try (InputStream is = connection.getInputStream(); - BufferedInputStream bis = new BufferedInputStream(is, length)) { + BufferedInputStream bis = new BufferedInputStream(is, length)) { BufferedImage image = ImageIO.read(bis); if (image == null) { return new CacheEntry(url); @@ -528,8 +537,7 @@ public CacheEntry call() throws Exception { long end = System.nanoTime(); long duration = TimeUnit.NANOSECONDS.toMillis(end - start); LOGGER.log(duration > 250 ? Level.INFO : Level.FINE, "Avatar lookup of {0} took {1}ms", - new Object[]{url, duration} - ); + new Object[] { url, duration }); } } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java index 26fe8df9..9fa801eb 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java @@ -57,8 +57,7 @@ */ public class GitLabPipelineStatusNotifier { - private static final Logger LOGGER = Logger - .getLogger(GitLabPipelineStatusNotifier.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabPipelineStatusNotifier.class.getName()); static final String GITLAB_PIPELINE_STATUS_PREFIX = "jenkinsci"; diff --git a/src/main/java/io/jenkins/plugins/gitlabserverconfig/action/GitlabAction.java b/src/main/java/io/jenkins/plugins/gitlabserverconfig/action/GitlabAction.java index 08b7950a..022ecc1a 100644 --- a/src/main/java/io/jenkins/plugins/gitlabserverconfig/action/GitlabAction.java +++ b/src/main/java/io/jenkins/plugins/gitlabserverconfig/action/GitlabAction.java @@ -30,7 +30,7 @@ @Extension @Restricted(NoExternalUse.class) public class GitlabAction implements RootAction { - private static final Logger LOGGER = Logger.getLogger(GitlabAction.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitlabAction.class.getName()); @RequirePOST public HttpResponse doServerList() { @@ -51,8 +51,8 @@ public HttpResponse doServerList() { @RequirePOST public HttpResponse doProjectList(@AncestorInPath SCMSourceOwner context, - @QueryParameter String server, - @QueryParameter String owner) { + @QueryParameter String server, + @QueryParameter String owner) { if (!Jenkins.get().hasPermission(Jenkins.ADMINISTER)) { return HttpResponses.errorJSON("no permission to get Gitlab server list"); } @@ -66,7 +66,7 @@ public HttpResponse doProjectList(@AncestorInPath SCMSourceOwner context, GitLabApi gitLabApi = GitLabHelper.apiBuilder(context, server); try { for (Project project : gitLabApi.getProjectApi().getUserProjects(owner, - new ProjectFilter().withOwned(true))) { + new ProjectFilter().withOwned(true))) { servers.add(project.getPathWithNamespace()); } } catch (GitLabApiException e) { @@ -84,7 +84,6 @@ public HttpResponse doProjectList(@AncestorInPath SCMSourceOwner context, return HttpResponses.okJSON(servers); } - @CheckForNull @Override public String getIconFileName() { diff --git a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java index 70b98852..dd011500 100644 --- a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java +++ b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java @@ -52,7 +52,7 @@ public class GitLabServer extends AbstractDescribableImpl { * The credentials matcher for GitLab Personal Access Token */ public static final CredentialsMatcher CREDENTIALS_MATCHER = CredentialsMatchers - .instanceOf(PersonalAccessToken.class); + .instanceOf(PersonalAccessToken.class); /** * Default name for community SaaS version server */ @@ -62,10 +62,11 @@ public class GitLabServer extends AbstractDescribableImpl { */ public static final String GITLAB_SERVER_URL = "https://gitlab.com"; /** - * Used as default token value if no any credentials found by given credentialsId. + * Used as default token value if no any credentials found by given + * credentialsId. */ public final static String EMPTY_TOKEN = ""; - private static final Logger LOGGER = Logger.getLogger(GitLabServer.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabServer.class.getName()); private static final SecureRandom RANDOM = new SecureRandom(); /** * Length of unique random numeric name for server @@ -76,11 +77,11 @@ public class GitLabServer extends AbstractDescribableImpl { * Common prefixes that we should remove when inferring a display name. */ private static final String[] COMMON_PREFIX_HOSTNAMES = { - "git.", - "gitlab.", - "vcs.", - "scm.", - "source." + "git.", + "gitlab.", + "vcs.", + "scm.", + "source." }; /** @@ -96,25 +97,29 @@ public class GitLabServer extends AbstractDescribableImpl { private final String serverUrl; /** - * {@code true} if and only if Jenkins is supposed to auto-manage web hooks for this end-point. + * {@code true} if and only if Jenkins is supposed to auto-manage web hooks for + * this end-point. */ private boolean manageWebHooks; /** - * {@code true} if and only if Jenkins is supposed to auto-manage system hooks for this + * {@code true} if and only if Jenkins is supposed to auto-manage system hooks + * for this * end-point. */ private boolean manageSystemHooks; /** - * The {@link PersonalAccessToken#getId()} of the credentials to use for auto-management of + * The {@link PersonalAccessToken#getId()} of the credentials to use for + * auto-management of * hooks. */ @NonNull private String credentialsId; /** - * The Jenkins root URL to use in Gitlab hooks, instead of {@link Jenkins#getRootUrl()}. + * The Jenkins root URL to use in Gitlab hooks, instead of + * {@link Jenkins#getRootUrl()}. * Useful when the main public Jenkins URL can't be accessed from Gitlab. */ private String hooksRootUrl; @@ -138,19 +143,21 @@ public class GitLabServer extends AbstractDescribableImpl { /** * Data Bound Constructor for only mandatory parameter serverUrl * - * @param serverUrl The URL of this GitLab Server - * @param name A unique name to use to describe the end-point, if empty replaced with a random - * name - * @param credentialsId The {@link PersonalAccessToken#getId()} of the credentials to use for - * GitLab Server Authentication to access GitLab APIs + * @param serverUrl The URL of this GitLab Server + * @param name A unique name to use to describe the end-point, if empty + * replaced with a random + * name + * @param credentialsId The {@link PersonalAccessToken#getId()} of the + * credentials to use for + * GitLab Server Authentication to access GitLab APIs */ @DataBoundConstructor public GitLabServer(@NonNull String serverUrl, @NonNull String name, - @NonNull String credentialsId) { + @NonNull String credentialsId) { this.serverUrl = defaultIfBlank(StringUtils.trim(serverUrl), GITLAB_SERVER_URL); this.name = StringUtils.isBlank(name) - ? getRandomName() - : StringUtils.trim(name); + ? getRandomName() + : StringUtils.trim(name); this.credentialsId = credentialsId; } @@ -161,7 +168,7 @@ public GitLabServer(@NonNull String serverUrl, @NonNull String name, */ private String getRandomName() { return String.format("%s-%s", SCMName.fromUrl(this.serverUrl, COMMON_PREFIX_HOSTNAMES), - RandomStringUtils.randomNumeric(SHORT_NAME_LENGTH)); + RandomStringUtils.randomNumeric(SHORT_NAME_LENGTH)); } /** @@ -181,9 +188,11 @@ public String getServerUrl() { } /** - * Returns {@code true} if Jenkins is supposed to auto-manage web hooks for this end-point. + * Returns {@code true} if Jenkins is supposed to auto-manage web hooks for this + * end-point. * - * @return {@code true} if Jenkins is supposed to auto-manage web hooks for this end-point. + * @return {@code true} if Jenkins is supposed to auto-manage web hooks for this + * end-point. */ public boolean isManageWebHooks() { return manageWebHooks; @@ -192,8 +201,9 @@ public boolean isManageWebHooks() { /** * Data Bound Setter for auto management of web hooks * - * @param manageWebHooks {@code true} if and only if Jenkins is supposed to auto-manage web - * hooks for this end-point. + * @param manageWebHooks {@code true} if and only if Jenkins is supposed to + * auto-manage web + * hooks for this end-point. */ @DataBoundSetter public void setManageWebHooks(boolean manageWebHooks) { @@ -201,9 +211,11 @@ public void setManageWebHooks(boolean manageWebHooks) { } /** - * Returns {@code true} if Jenkins is supposed to auto-manage system hooks for this end-point. + * Returns {@code true} if Jenkins is supposed to auto-manage system hooks for + * this end-point. * - * @return {@code true} if Jenkins is supposed to auto-manage system hooks for this end-point. + * @return {@code true} if Jenkins is supposed to auto-manage system hooks for + * this end-point. */ public boolean isManageSystemHooks() { return manageSystemHooks; @@ -212,8 +224,9 @@ public boolean isManageSystemHooks() { /** * Data Bound Setter for auto management of system hooks * - * @param manageSystemHooks {@code true} if and only if Jenkins is supposed to auto-manage - * system hooks for this end-point. + * @param manageSystemHooks {@code true} if and only if Jenkins is supposed to + * auto-manage + * system hooks for this end-point. */ @DataBoundSetter public void setManageSystemHooks(boolean manageSystemHooks) { @@ -221,11 +234,13 @@ public void setManageSystemHooks(boolean manageSystemHooks) { } /** - * Returns The {@link PersonalAccessToken#getId()} of the credentials to use for GitLab Server + * Returns The {@link PersonalAccessToken#getId()} of the credentials to use for + * GitLab Server * Authentication to access GitLab APIs. * - * @return The {@link PersonalAccessToken#getId()} of the credentials to use for GitLab Server - * Authentication to access GitLab APIs. + * @return The {@link PersonalAccessToken#getId()} of the credentials to use for + * GitLab Server + * Authentication to access GitLab APIs. */ @NonNull public String getCredentialsId() { @@ -244,17 +259,18 @@ public PersonalAccessToken getCredentials(AccessControlled context) { } else { context.checkPermission(CredentialsProvider.USE_OWN); } - return StringUtils.isBlank(credentialsId) ? null : CredentialsMatchers.firstOrNull( lookupCredentials( - PersonalAccessToken.class, - jenkins, - ACL.SYSTEM, - fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL)).build() - ), withId(credentialsId)); + return StringUtils.isBlank(credentialsId) ? null + : CredentialsMatchers.firstOrNull(lookupCredentials( + PersonalAccessToken.class, + jenkins, + ACL.SYSTEM, + fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL)).build()), withId(credentialsId)); } /** - * @param hooksRootUrl a custom root URL, to be used in hooks instead of {@link Jenkins#getRootUrl()}. - * Set to {@code null} for default behavior. + * @param hooksRootUrl a custom root URL, to be used in hooks instead of + * {@link Jenkins#getRootUrl()}. + * Set to {@code null} for default behavior. */ @DataBoundSetter public void setHooksRootUrl(String hooksRootUrl) { @@ -262,8 +278,9 @@ public void setHooksRootUrl(String hooksRootUrl) { } /** - * @return the custom root URL, to be used in hooks instead of {@link Jenkins#getRootUrl()}. - * Can be either a root URL with its trailing slash, or {@code null}. + * @return the custom root URL, to be used in hooks instead of + * {@link Jenkins#getRootUrl()}. + * Can be either a root URL with its trailing slash, or {@code null}. */ @CheckForNull public String getHooksRootUrl() { @@ -277,7 +294,7 @@ public void setSecretToken(Secret token) { // TODO: Use some UI element to trigger (what is the best way?) private void generateSecretToken() { - byte[] random = new byte[16]; // 16x8=128bit worth of randomness, since we use md5 digest as the API token + byte[] random = new byte[16]; // 16x8=128bit worth of randomness, since we use md5 digest as the API token RANDOM.nextBytes(random); this.secretToken = Secret.decrypt(Util.toHexString(random)); } @@ -306,7 +323,7 @@ public String getSecretTokenAsPlainText() { * GitLab Web Hook trigger. * * @return {@code true} if Jenkins should trigger a build immediately on a - * GitLab Web Hook trigger. + * GitLab Web Hook trigger. */ public boolean isImmediateHookTrigger() { return immediateHookTrigger; @@ -315,8 +332,9 @@ public boolean isImmediateHookTrigger() { /** * Data Bound Setter for immediate build on a GitLab Web Hook trigger. * - * @param immediateHookTrigger {@code true} if and only if Jenkins should trigger a build immediately on a - * GitLab Web Hook trigger. + * @param immediateHookTrigger {@code true} if and only if Jenkins should + * trigger a build immediately on a + * GitLab Web Hook trigger. */ @DataBoundSetter public void setImmediateHookTrigger(boolean immediateHookTrigger) { @@ -327,8 +345,10 @@ public void setImmediateHookTrigger(boolean immediateHookTrigger) { * Data Bound Setter for web hook trigger delay * * @param hookTriggerDelay Delay to be used for GitLab Web Hook build triggers. - * Set to {@code null} to use delay equal to GitLab cache timeout, which - * will avoid builds being not triggered due to GitLab caching. + * Set to {@code null} to use delay equal to GitLab + * cache timeout, which + * will avoid builds being not triggered due to GitLab + * caching. */ @DataBoundSetter public void setHookTriggerDelay(String hookTriggerDelay) { @@ -341,8 +361,9 @@ public void setHookTriggerDelay(String hookTriggerDelay) { /** * @return Delay to be used for GitLab Web Hook build triggers. - * Can be either a root URL with its trailing slash, or {@code null}. - * Can be {@code null} to request delay to be equal to GitLab cache timeout. + * Can be either a root URL with its trailing slash, or {@code null}. + * Can be {@code null} to request delay to be equal to GitLab cache + * timeout. */ @CheckForNull public Integer getHookTriggerDelay() { @@ -435,7 +456,7 @@ public String getDisplayName() { @Restricted(DoNotUse.class) @SuppressWarnings("unused") public FormValidation doTestConnection(@QueryParameter String serverUrl, - @QueryParameter String credentialsId) { + @QueryParameter String credentialsId) { PersonalAccessToken credentials = getCredentials(serverUrl, credentialsId); String privateToken = ""; if (credentials != null) { @@ -445,18 +466,18 @@ public FormValidation doTestConnection(@QueryParameter String serverUrl, GitLabApi gitLabApi = new GitLabApi(serverUrl, EMPTY_TOKEN, null, getProxyConfig(serverUrl)); try { /* - In order to validate a GitLab Server without personal access token, - we are fetching 1 project from the GitLab Server. If no project exists, - it returns an empty list. If no server exists at the specified endpoint, - it raises GitLabAPIException. + * In order to validate a GitLab Server without personal access token, + * we are fetching 1 project from the GitLab Server. If no project exists, + * it returns an empty list. If no server exists at the specified endpoint, + * it raises GitLabAPIException. */ gitLabApi.getProjectApi().getProjects(1, 1); return FormValidation.ok("Valid GitLab Server but no credentials specified"); } catch (GitLabApiException e) { LOGGER.log(Level.SEVERE, "Invalid GitLab Server Url"); return FormValidation - .errorWithMarkup(Messages - .GitLabServer_credentialsNotResolved(Util.escape(credentialsId))); + .errorWithMarkup(Messages + .GitLabServer_credentialsNotResolved(Util.escape(credentialsId))); } } else { @@ -464,15 +485,15 @@ public FormValidation doTestConnection(@QueryParameter String serverUrl, try { User user = gitLabApi.getUserApi().getCurrentUser(); LOGGER.log(Level.FINEST, String - .format("Connection established with the GitLab Server for %s", - user.getUsername())); + .format("Connection established with the GitLab Server for %s", + user.getUsername())); return FormValidation - .ok(String.format("Credentials verified for user %s", user.getUsername())); + .ok(String.format("Credentials verified for user %s", user.getUsername())); } catch (GitLabApiException e) { LOGGER.log(Level.SEVERE, - "Failed to connect with GitLab Server - %s", e.getMessage()); + "Failed to connect with GitLab Server - %s", e.getMessage()); return FormValidation.error(e, - Messages.GitLabServer_failedValidation(Util.escape(e.getMessage()))); + Messages.GitLabServer_failedValidation(Util.escape(e.getMessage()))); } } } @@ -480,41 +501,39 @@ public FormValidation doTestConnection(@QueryParameter String serverUrl, /** * Stapler form completion. * - * @param serverUrl the server URL. + * @param serverUrl the server URL. * @param credentialsId the credentials Id * @return the available credentials. */ @Restricted(NoExternalUse.class) // stapler @SuppressWarnings("unused") public ListBoxModel doFillCredentialsIdItems(@QueryParameter String serverUrl, - @QueryParameter String credentialsId) { + @QueryParameter String credentialsId) { Jenkins jenkins = Jenkins.get(); if (!jenkins.hasPermission(Jenkins.ADMINISTER)) { return new StandardListBoxModel().includeCurrentValue(credentialsId); } return new StandardListBoxModel() - .includeEmptyValue() - .includeMatchingAs(ACL.SYSTEM, - jenkins, - StandardCredentials.class, - fromUri(serverUrl).build(), - CREDENTIALS_MATCHER - ); + .includeEmptyValue() + .includeMatchingAs(ACL.SYSTEM, + jenkins, + StandardCredentials.class, + fromUri(serverUrl).build(), + CREDENTIALS_MATCHER); } private PersonalAccessToken getCredentials(String serverUrl, String credentialsId) { Jenkins jenkins = Jenkins.get(); jenkins.checkPermission(Jenkins.ADMINISTER); - return StringUtils.isBlank(credentialsId) ? null : CredentialsMatchers.firstOrNull( - lookupCredentials( - PersonalAccessToken.class, - jenkins, - ACL.SYSTEM, - fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL)).build()), - withId(credentialsId) - ); + return StringUtils.isBlank(credentialsId) ? null + : CredentialsMatchers.firstOrNull( + lookupCredentials( + PersonalAccessToken.class, + jenkins, + ACL.SYSTEM, + fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL)).build()), + withId(credentialsId)); } - } } diff --git a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServers.java b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServers.java index 6c8b0f78..93c227eb 100644 --- a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServers.java +++ b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServers.java @@ -29,10 +29,11 @@ @Extension public class GitLabServers extends GlobalConfiguration implements PersistentDescriptor { - private static final Logger LOGGER = Logger.getLogger(GitLabServers.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabServers.class.getName()); /** - * The list of {@link GitLabServer}, this is subject to the constraint that there can only ever + * The list of {@link GitLabServer}, this is subject to the constraint that + * there can only ever * be one entry for each {@link GitLabServer#getServerUrl()}. */ private List servers; @@ -50,11 +51,11 @@ public static GitLabServers get() { * Helper function to get predicate to filter servers based on their names * * @param keyExtractor the Function to filter - * @param In this case it is server + * @param In this case it is server * @return a predicate to filter servers list */ private static Predicate distinctByKey( - Function keyExtractor) { + Function keyExtractor) { Map seen = new ConcurrentHashMap<>(); return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; } @@ -70,8 +71,8 @@ public ListBoxModel getServerItems() { String serverUrl = server.getServerUrl(); String serverName = server.getName(); // serverName or name or displayName result.add( - StringUtils.isBlank(serverName) ? serverUrl : serverName + " (" + serverUrl + ")", - serverName); + StringUtils.isBlank(serverName) ? serverUrl : serverName + " (" + serverUrl + ")", + serverName); } return result; } @@ -85,9 +86,10 @@ public ListBoxModel getServerItems() { public List getServers() { if (servers == null || servers.isEmpty()) { servers = new ArrayList<>(); - // Don't really need to create this manually. Having a default one makes it be easier for a new user + // Don't really need to create this manually. Having a default one makes it be + // easier for a new user servers.add(new GitLabServer(GitLabServer.GITLAB_SERVER_URL, - GitLabServer.GITLAB_SERVER_DEFAULT_NAME, "")); + GitLabServer.GITLAB_SERVER_DEFAULT_NAME, "")); } return Collections.unmodifiableList(servers); } @@ -100,7 +102,7 @@ public List getServers() { public void setServers(@CheckForNull List servers) { Jenkins.get().checkPermission(Jenkins.ADMINISTER); this.servers = fixNull(servers).stream() - .filter(distinctByKey(GitLabServer::getName)).collect(Collectors.toList()); + .filter(distinctByKey(GitLabServer::getName)).collect(Collectors.toList()); save(); } @@ -117,7 +119,7 @@ public String getDisplayName() { */ public List actions() { return Collections - .singletonList(Jenkins.get().getDescriptor(GitLabPersonalAccessTokenCreator.class)); + .singletonList(Jenkins.get().getDescriptor(GitLabPersonalAccessTokenCreator.class)); } /** @@ -129,9 +131,9 @@ public List actions() { public boolean addServer(@NonNull GitLabServer server) { List servers = new ArrayList<>(getServers()); GitLabServer gitLabServer = servers.stream() - .filter(s -> s.getName().equals(server.getName())) - .findAny() - .orElse(null); + .filter(s -> s.getName().equals(server.getName())) + .findAny() + .orElse(null); if (gitLabServer != null) { return false; } @@ -141,7 +143,8 @@ public boolean addServer(@NonNull GitLabServer server) { } /** - * Updates an existing endpoint (or adds if missing) Checks if the GitLab Server name is + * Updates an existing endpoint (or adds if missing) Checks if the GitLab Server + * name is * matched * * @param server the server to update. @@ -153,8 +156,8 @@ public boolean updateServer(@NonNull GitLabServer server) { return false; } servers = servers.stream() - .map(oldServer -> oldServer.getName().equals(server.getName()) ? server : oldServer) - .collect(Collectors.toList()); + .map(oldServer -> oldServer.getName().equals(server.getName()) ? server : oldServer) + .collect(Collectors.toList()); setServers(servers); return true; } @@ -175,18 +178,20 @@ public boolean removeServer(@CheckForNull String name) { } /** - * Checks to see if the supplied server URL is defined in the global configuration. + * Checks to see if the supplied server URL is defined in the global + * configuration. * * @param serverName the server url to check. - * @return the global configuration for the specified server url or {@code null} if not defined. + * @return the global configuration for the specified server url or {@code null} + * if not defined. */ @CheckForNull public GitLabServer findServer(@CheckForNull String serverName) { List servers = new ArrayList<>(getServers()); return servers.stream() - .filter(server -> server.getName().equals(serverName)) - .findAny() - .orElse(null); + .filter(server -> server.getName().equals(serverName)) + .findAny() + .orElse(null); } } diff --git a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/helpers/GitLabPersonalAccessTokenCreator.java b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/helpers/GitLabPersonalAccessTokenCreator.java index 5da7e481..fb1ea7c9 100644 --- a/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/helpers/GitLabPersonalAccessTokenCreator.java +++ b/src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/helpers/GitLabPersonalAccessTokenCreator.java @@ -47,17 +47,16 @@ @Extension public class GitLabPersonalAccessTokenCreator extends - Descriptor implements - Describable { + Descriptor implements + Describable { - private static final Logger LOGGER = Logger.getLogger(GitLabPersonalAccessTokenCreator.class.getName()); + public static final Logger LOGGER = Logger.getLogger(GitLabPersonalAccessTokenCreator.class.getName()); private static final List GL_PLUGIN_REQUIRED_SCOPE = ImmutableList.of( - AccessTokenUtils.Scope.API, - AccessTokenUtils.Scope.READ_REGISTRY, - AccessTokenUtils.Scope.READ_USER, - AccessTokenUtils.Scope.READ_REPOSITORY - ); + AccessTokenUtils.Scope.API, + AccessTokenUtils.Scope.READ_REGISTRY, + AccessTokenUtils.Scope.READ_USER, + AccessTokenUtils.Scope.READ_REPOSITORY); public GitLabPersonalAccessTokenCreator() { super(GitLabPersonalAccessTokenCreator.class); @@ -81,33 +80,32 @@ public String getDisplayName() { @SuppressWarnings("unused") public ListBoxModel doFillCredentialsIdItems(@QueryParameter String serverUrl, - @QueryParameter String credentialsId) { + @QueryParameter String credentialsId) { Jenkins jenkins = Jenkins.get(); if (!jenkins.hasPermission(Jenkins.ADMINISTER)) { return new StandardListBoxModel().includeCurrentValue(credentialsId); } return new StandardUsernameListBoxModel() - .includeEmptyValue() - .includeMatchingAs( - ACL.SYSTEM, - jenkins, - StandardUsernamePasswordCredentials.class, - fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build(), - CredentialsMatchers.always() - ).includeMatchingAs( - Jenkins.getAuthentication(), - jenkins, - StandardUsernamePasswordCredentials.class, - fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build(), - CredentialsMatchers.always() - ); + .includeEmptyValue() + .includeMatchingAs( + ACL.SYSTEM, + jenkins, + StandardUsernamePasswordCredentials.class, + fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build(), + CredentialsMatchers.always()) + .includeMatchingAs( + Jenkins.getAuthentication(), + jenkins, + StandardUsernamePasswordCredentials.class, + fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build(), + CredentialsMatchers.always()); } @SuppressWarnings("unused") @RequirePOST public FormValidation doCreateTokenByCredentials( - @QueryParameter String serverUrl, - @QueryParameter String credentialsId) { + @QueryParameter String serverUrl, + @QueryParameter String credentialsId) { Jenkins jenkins = Jenkins.get(); jenkins.checkPermission(Jenkins.ADMINISTER); @@ -116,19 +114,19 @@ public FormValidation doCreateTokenByCredentials( } StandardUsernamePasswordCredentials credentials = firstOrNull(lookupCredentials( - StandardUsernamePasswordCredentials.class, - jenkins, - ACL.SYSTEM, - fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build()), - withId(credentialsId)); - - if (credentials == null) { - credentials = firstOrNull(lookupCredentials( StandardUsernamePasswordCredentials.class, jenkins, - Jenkins.getAuthentication(), + ACL.SYSTEM, fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build()), withId(credentialsId)); + + if (credentials == null) { + credentials = firstOrNull(lookupCredentials( + StandardUsernamePasswordCredentials.class, + jenkins, + Jenkins.getAuthentication(), + fromUri(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)).build()), + withId(credentialsId)); } if (Objects.isNull(credentials)) { @@ -137,12 +135,11 @@ public FormValidation doCreateTokenByCredentials( try { String tokenName = UUID.randomUUID().toString(); String token = AccessTokenUtils.createPersonalAccessToken( - defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL), - credentials.getUsername(), - Secret.toString(credentials.getPassword()), - tokenName, - GL_PLUGIN_REQUIRED_SCOPE - ); + defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL), + credentials.getUsername(), + Secret.toString(credentials.getPassword()), + tokenName, + GL_PLUGIN_REQUIRED_SCOPE); tokenName = getShortName(tokenName); createCredentials(serverUrl, token, credentials.getUsername(), tokenName); return FormValidation.ok("Created credentials with id %s ", tokenName); @@ -154,71 +151,72 @@ public FormValidation doCreateTokenByCredentials( @SuppressWarnings("unused") @RequirePOST public FormValidation doCreateTokenByPassword( - @QueryParameter String serverUrl, - @QueryParameter String login, - @QueryParameter String password) { + @QueryParameter String serverUrl, + @QueryParameter String login, + @QueryParameter String password) { Jenkins.get().checkPermission(Jenkins.ADMINISTER); try { String tokenName = UUID.randomUUID().toString(); String token = AccessTokenUtils.createPersonalAccessToken( - defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL), - login, - password, - tokenName, - GL_PLUGIN_REQUIRED_SCOPE - ); + defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL), + login, + password, + tokenName, + GL_PLUGIN_REQUIRED_SCOPE); tokenName = getShortName(tokenName); createCredentials(serverUrl, token, login, tokenName); return FormValidation.ok( - "Created credentials with id %s", tokenName - ); + "Created credentials with id %s", tokenName); } catch (GitLabApiException e) { return FormValidation - .error(e, "Can't create GL token for %s - %s", login, e.getMessage()); + .error(e, "Can't create GL token for %s - %s", login, e.getMessage()); } } /** - * Creates {@link io.jenkins.plugins.gitlabserverconfig.credentials.PersonalAccessToken} with + * Creates + * {@link io.jenkins.plugins.gitlabserverconfig.credentials.PersonalAccessToken} + * with * previously created GitLab Personal Access Token. * - * @param serverUrl to add to domain with host and scheme requirement from this url - * @param token GitLab Personal Access Token - * @param username used to add to description of newly created credentials + * @param serverUrl to add to domain with host and scheme requirement from this + * url + * @param token GitLab Personal Access Token + * @param username used to add to description of newly created credentials * @see #saveCredentials(String, PersonalAccessToken) */ private void createCredentials(@Nullable String serverUrl, String token, String username, - String tokenName) { + String tokenName) { String url = defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL); String description = String - .format("Auto Generated by %s server for %s user", url, username); + .format("Auto Generated by %s server for %s user", url, username); PersonalAccessToken credentials = new PersonalAccessTokenImpl( - CredentialsScope.GLOBAL, - tokenName, - description, - token - ); + CredentialsScope.GLOBAL, + tokenName, + description, + token); saveCredentials(url, credentials); } /** - * Saves given credentials in jenkins for domain extracted from server url Adds them to domain - * extracted from server url (will be generated if no any exists before). Domain will have + * Saves given credentials in jenkins for domain extracted from server url Adds + * them to domain + * extracted from server url (will be generated if no any exists before). Domain + * will have * domain requirements consists of scheme and host from serverUrl arg * - * @param serverUrl to extract (and create if no any) domain + * @param serverUrl to extract (and create if no any) domain * @param credentials to save credentials */ private void saveCredentials(String serverUrl, final PersonalAccessToken credentials) { URI serverUri = URI.create(defaultIfBlank(serverUrl, GitLabServer.GITLAB_SERVER_URL)); List specifications = asList( - new SchemeSpecification(serverUri.getScheme()), - new HostnameSpecification(serverUri.getHost(), null) - ); + new SchemeSpecification(serverUri.getScheme()), + new HostnameSpecification(serverUri.getHost(), null)); final Domain domain = new Domain(serverUri.getHost(), "GitLab domain (autogenerated)", - specifications); + specifications); try (ACLContext acl = ACL.as(ACL.SYSTEM)) { new SystemCredentialsProvider.StoreImpl().addDomain(domain, credentials); } catch (IOException e) { diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait/config.jelly b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait/config.jelly index 81646fda..40157435 100644 --- a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait/config.jelly @@ -1,24 +1,27 @@ - + - + - + - + - + - + - + + + +