From aa66886e07b57d7b350c262114a68f0608712f25 Mon Sep 17 00:00:00 2001 From: Michael Fitoussi <16884890+mifitous@users.noreply.github.com> Date: Fri, 30 Dec 2022 12:23:33 +0200 Subject: [PATCH] Discover MR from forked projects & Add build status name overwrite option (#257) Co-authored-by: Egor Abramov Co-authored-by: Irrandon <13981266+irrandon@users.noreply.github.com> Resolves https://github.com/jenkinsci/gitlab-branch-source-plugin/issues/167 --- .../BuildStatusNameCustomPartTrait.java | 15 + .../ForkMergeRequestDiscoveryTrait.java | 105 ++++--- .../GitLabMergeRequestSCMEvent.java | 96 +++--- .../GitLabMergeRequestTrigger.java | 7 + .../gitlabbranchsource/GitLabSCMSource.java | 284 +++++++++--------- .../GitLabSCMSourceContext.java | 31 +- .../WebhookListenerBuildConditionsTrait.java | 24 +- .../helpers/GitLabPipelineStatusNotifier.java | 257 +++++++++------- .../config.jelly | 3 + ...ml => help-buildStatusNameCustomPart.html} | 0 .../help-buildStatusNameOverwrite.html | 4 + .../BuildStatusNameCustomPartTrait/help.html | 3 + .../config.jelly | 3 + .../help-buildMRForksNotMirror.html | 7 + .../GitLabAvatarTrait/config.jelly | 2 +- .../TriggerMRCommentTrait/config.jelly | 2 +- .../GitLabPipelineStatusNotifierTest.java | 18 +- 17 files changed, 484 insertions(+), 377 deletions(-) rename src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/{help-buildStatusContextPrefix.html => help-buildStatusNameCustomPart.html} (100%) create mode 100644 src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameOverwrite.html create mode 100644 src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help.html create mode 100644 src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/help-buildMRForksNotMirror.html diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait.java index ee61d1c0..222c44fb 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait.java @@ -15,6 +15,8 @@ public class BuildStatusNameCustomPartTrait extends SCMSourceTrait { @NonNull private String buildStatusNameCustomPart = ""; + private boolean buildStatusNameOverwrite; + /** * Constructor for stapler. */ @@ -31,11 +33,17 @@ public void setBuildStatusNameCustomPart(@NonNull String buildStatusNameCustomPa this.buildStatusNameCustomPart = buildStatusNameCustomPart; } + @DataBoundSetter + public void setBuildStatusNameOverwrite(@NonNull Boolean buildStatusNameOverwrite) { + this.buildStatusNameOverwrite = buildStatusNameOverwrite; + } + @Override protected void decorateContext(SCMSourceContext context) { if (context instanceof GitLabSCMSourceContext) { GitLabSCMSourceContext ctx = (GitLabSCMSourceContext) context; ctx.withBuildStatusNameCustomPart(getBuildStatusNameCustomPart()); + ctx.withBuildStatusNameOverwrite(getBuildStatusNameOverwrite()); } } @@ -49,6 +57,13 @@ public String getBuildStatusNameCustomPart() { return buildStatusNameCustomPart; } + /** + * Getter method for the build status name overwrite + * + * @return build status name overwrite option + */ + public boolean getBuildStatusNameOverwrite() { return buildStatusNameOverwrite; } + /** * Our descriptor. */ diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait.java index 1e1efa6c..0849d4aa 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait.java @@ -25,9 +25,11 @@ import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; /** - * A {@link Discovery} trait for GitLab that will discover merge requests from forks of the + * A {@link Discovery} trait for GitLab that will discover merge requests from + * forks of the * project. */ public class ForkMergeRequestDiscoveryTrait extends SCMSourceTrait { @@ -40,35 +42,41 @@ public class ForkMergeRequestDiscoveryTrait extends SCMSourceTrait { * The authority. */ @NonNull - private final SCMHeadAuthority - trust; + private final SCMHeadAuthority trust; + /** + * Build MR for Forks that are not Mirrors + */ + private boolean buildMRForksNotMirror = false; /** * Constructor for stapler. * - * @param strategyId the strategy id. - * @param trust the authority to use. + * @param strategyId the strategy id. + * @param trust the authority to use. + * @param buildMRForksNotMirror the buildMRForksNotMirror flag */ @DataBoundConstructor public ForkMergeRequestDiscoveryTrait(int strategyId, - @NonNull SCMHeadAuthority trust) { + @NonNull SCMHeadAuthority trust, + boolean buildMRForksNotMirror) { this.strategyId = strategyId; this.trust = trust; + this.buildMRForksNotMirror = buildMRForksNotMirror; } /** * Constructor for programmatic instantiation. * - * @param strategies the {@link ChangeRequestCheckoutStrategy} instances. - * @param trust the authority. + * @param strategies the {@link ChangeRequestCheckoutStrategy} + * instances. + * @param trust the authority. + * @param buildMRForksNotMirror the buildMRForksNotMirror flag */ public ForkMergeRequestDiscoveryTrait(@NonNull Set strategies, - @NonNull SCMHeadAuthority trust) { + @NonNull SCMHeadAuthority trust, + boolean buildMRForksNotMirror) { this((strategies.contains(ChangeRequestCheckoutStrategy.MERGE) ? 1 : 0) - + (strategies.contains(ChangeRequestCheckoutStrategy.HEAD) ? 2 : 0), trust); + + (strategies.contains(ChangeRequestCheckoutStrategy.HEAD) ? 2 : 0), trust, buildMRForksNotMirror); } /** @@ -94,7 +102,7 @@ public Set getStrategies() { return EnumSet.of(ChangeRequestCheckoutStrategy.HEAD); case 3: return EnumSet - .of(ChangeRequestCheckoutStrategy.HEAD, ChangeRequestCheckoutStrategy.MERGE); + .of(ChangeRequestCheckoutStrategy.HEAD, ChangeRequestCheckoutStrategy.MERGE); default: return EnumSet.noneOf(ChangeRequestCheckoutStrategy.class); } @@ -106,11 +114,27 @@ public Set getStrategies() { * @return the authority. */ @NonNull - public SCMHeadAuthority - getTrust() { + public SCMHeadAuthority getTrust() { return trust; } + /** + * Gets the buildMRForksNotMirror + * + * @return true to build MR for Forks that are not Mirror + */ + public boolean getBuildMRForksNotMirror() { + return buildMRForksNotMirror; + } + + /** + * Setter for stapler to set the buildMRForksNotMirror + */ + @DataBoundSetter + public void setBuildMRForksNotMirror(boolean buildMRForksNotMirror) { + this.buildMRForksNotMirror = buildMRForksNotMirror; + } + /** * {@inheritDoc} */ @@ -120,6 +144,7 @@ protected void decorateContext(SCMSourceContext context) { ctx.wantForkMRs(true); ctx.withAuthority(trust); ctx.withForkMRStrategies(getStrategies()); + ctx.withBuildMRForksNotMirror(getBuildMRForksNotMirror()); } /** @@ -188,17 +213,18 @@ public ListBoxModel doFillStrategyIdItems() { @SuppressWarnings("unused") // stapler public List getTrustDescriptors() { return SCMHeadAuthority._for( - GitLabSCMSourceRequest.class, - MergeRequestSCMHead.class, - MergeRequestSCMRevision.class, - SCMHeadOrigin.Fork.class - ); + GitLabSCMSourceRequest.class, + MergeRequestSCMHead.class, + MergeRequestSCMRevision.class, + SCMHeadOrigin.Fork.class); } /** - * Returns the default trust for new instances of {@link ForkMergeRequestDiscoveryTrait}. + * Returns the default trust for new instances of + * {@link ForkMergeRequestDiscoveryTrait}. * - * @return the default trust for new instances of {@link ForkMergeRequestDiscoveryTrait}. + * @return the default trust for new instances of + * {@link ForkMergeRequestDiscoveryTrait}. */ @NonNull @SuppressWarnings("unused") // stapler @@ -207,12 +233,11 @@ public List getTrustDescriptors() { } } - /** * An {@link SCMHeadAuthority} that trusts nothing. */ public static class TrustNobody extends - SCMHeadAuthority { + SCMHeadAuthority { /** * Constructor. @@ -226,7 +251,7 @@ public TrustNobody() { */ @Override public boolean checkTrusted(@NonNull SCMSourceRequest request, - @NonNull ChangeRequestSCMHead2 head) { + @NonNull ChangeRequestSCMHead2 head) { return false; } @@ -242,7 +267,7 @@ public static class DescriptorImpl extends SCMHeadAuthorityDescriptor { */ @Override public boolean isApplicableToOrigin( - @NonNull Class originClass) { + @NonNull Class originClass) { return SCMHeadOrigin.Fork.class.isAssignableFrom(originClass); } @@ -255,7 +280,6 @@ public String getDisplayName() { return Messages.ForkMergeRequestDiscoveryTrait_nobodyDisplayName(); } - } } @@ -263,8 +287,8 @@ public String getDisplayName() { * An {@link SCMHeadAuthority} that trusts Members to the project. */ public static class TrustMembers - extends - SCMHeadAuthority { + extends + SCMHeadAuthority { /** * Constructor. @@ -278,7 +302,7 @@ public TrustMembers() { */ @Override protected boolean checkTrusted(@NonNull GitLabSCMSourceRequest request, - @NonNull MergeRequestSCMHead head) { + @NonNull MergeRequestSCMHead head) { if (head.getOrigin().equals(SCMHeadOrigin.DEFAULT)) { return false; } @@ -307,7 +331,7 @@ public String getDisplayName() { */ @Override public boolean isApplicableToOrigin( - @NonNull Class originClass) { + @NonNull Class originClass) { return SCMHeadOrigin.Fork.class.isAssignableFrom(originClass); } @@ -315,11 +339,12 @@ public boolean isApplicableToOrigin( } /** - * An {@link SCMHeadAuthority} that trusts those with required permission to the project. + * An {@link SCMHeadAuthority} that trusts those with required permission to the + * project. */ public static class TrustPermission - extends - SCMHeadAuthority { + extends + SCMHeadAuthority { /** * Constructor. @@ -333,7 +358,7 @@ public TrustPermission() { */ @Override protected boolean checkTrusted(@NonNull GitLabSCMSourceRequest request, - @NonNull MergeRequestSCMHead head) { + @NonNull MergeRequestSCMHead head) { if (!head.getOrigin().equals(SCMHeadOrigin.DEFAULT)) { AccessLevel permission = request.getPermission(head.getOriginOwner()); if (permission != null) { @@ -371,7 +396,7 @@ public String getDisplayName() { */ @Override public boolean isApplicableToOrigin( - @NonNull Class originClass) { + @NonNull Class originClass) { return SCMHeadOrigin.Fork.class.isAssignableFrom(originClass); } } @@ -381,7 +406,7 @@ public boolean isApplicableToOrigin( * An {@link SCMHeadAuthority} that trusts everyone. */ public static class TrustEveryone extends - SCMHeadAuthority { + SCMHeadAuthority { /** * Constructor. @@ -395,7 +420,7 @@ public TrustEveryone() { */ @Override protected boolean checkTrusted(@NonNull SCMSourceRequest request, - @NonNull ChangeRequestSCMHead2 head) { + @NonNull ChangeRequestSCMHead2 head) { return true; } @@ -420,7 +445,7 @@ public String getDisplayName() { */ @Override public boolean isApplicableToOrigin( - @NonNull Class originClass) { + @NonNull Class originClass) { return SCMHeadOrigin.Fork.class.isAssignableFrom(originClass); } } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java index 02cfc4f1..35225171 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestSCMEvent.java @@ -29,8 +29,8 @@ 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())) { + && mrEvent.getObjectAttributes().getCreatedAt() + .equals(mrEvent.getObjectAttributes().getUpdatedAt())) { return Type.CREATED; } return Type.UPDATED; @@ -42,34 +42,29 @@ public String descriptionFor(@NonNull SCMNavigator navigator) { if (state != null) { switch (state) { case "opened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " opened in project " + getPayload() - .getProject().getName(); case "reopened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " reopened in project " + getPayload() - .getProject().getName(); case "closed": return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " closed in project " + getPayload() - .getProject().getName(); + + " state:" + state + " action:" + getPayload().getObjectAttributes().getAction() + + " in project " + getPayload() + .getProject().getName(); } } return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " event in project " + getPayload().getProject() - .getName(); + + " event in project " + getPayload().getProject() + .getName(); } @Override public boolean isMatch(@NonNull GitLabSCMNavigator navigator) { return navigator.getNavigatorProjects() - .contains(getPayload().getProject().getPathWithNamespace()); + .contains(getPayload().getProject().getPathWithNamespace()); } @Override public boolean isMatch(@NonNull GitLabSCMSource source) { return getPayload().getObjectAttributes().getTargetProjectId() - .equals(source.getProjectId()); + .equals(source.getProjectId()); } @NonNull @@ -84,14 +79,10 @@ public String descriptionFor(@NonNull SCMSource source) { if (state != null) { switch (state) { case "opened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " opened"; case "reopened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " reopened"; case "closed": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " closed"; + return "Merge request !" + getPayload().getObjectAttributes().getIid() + " state:" + state + + " action:" + getPayload().getObjectAttributes().getAction(); } } return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; @@ -103,17 +94,12 @@ public String description() { if (state != null) { switch (state) { case "opened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " opened in project " + getPayload() - .getProject().getPathWithNamespace(); case "reopened": - return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " reopened in project " + getPayload() - .getProject().getPathWithNamespace(); case "closed": return "Merge request !" + getPayload().getObjectAttributes().getIid() - + " closed in project " + getPayload() - .getProject().getPathWithNamespace(); + + " state:" + state + " action:" + getPayload().getObjectAttributes().getAction() + + " in project " + getPayload() + .getProject().getPathWithNamespace(); } } return "Merge request !" + getPayload().getObjectAttributes().getIid() + " event"; @@ -124,43 +110,39 @@ public String description() { public Map headsFor(GitLabSCMSource source) { Map result = new HashMap<>(); try (GitLabSCMSourceRequest request = new GitLabSCMSourceContext(null, - SCMHeadObserver.none()) - .withTraits(source.getTraits()) - .newRequest(source, 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()); + .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() - ); + "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() - ) - )); + ? 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); diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java index 61a72b87..ae82f681 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabMergeRequestTrigger.java @@ -70,6 +70,13 @@ private boolean shouldBuild(MergeRequestEvent mrEvent, GitLabSCMSourceContext co getPayload().getObjectAttributes().getIid()); } + LOGGER.log(Level.FINEST, + "shouldBuild for MR-{0} will be set for action {1} based on pipeline configuration.", + new Object[] { + getPayload().getObjectAttributes().getIid(), + action + }); + if (action.equals("open")) { return context.alwaysBuildMROpen(); } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java index 5cb27e14..5b61df58 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSource.java @@ -268,18 +268,15 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list .getCommit().getId(); if (mr.getState().equals(Constants.MergeRequestState.OPENED.toString())) { listener.getLogger().format("Current revision of merge request #%s is %s%n", - h.getId(), mr.getSha()); + h.getId(), mr.getSha()); return new MergeRequestSCMRevision( - h, - new BranchSCMRevision( - h.getTarget(), - targetSha - ), - new BranchSCMRevision( - new BranchSCMHead(h.getOriginName()), - mr.getSha() - ) - ); + h, + new BranchSCMRevision( + h.getTarget(), + targetSha), + new BranchSCMRevision( + new BranchSCMHead(h.getOriginName()), + mr.getSha())); } else { listener.getLogger().format("Merge request #%s is CLOSED%n", h.getId()); return null; @@ -301,17 +298,18 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list @Override protected void retrieve(SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer, - SCMHeadEvent event, - @NonNull TaskListener listener) throws IOException, InterruptedException { + SCMHeadEvent event, + @NonNull TaskListener listener) throws IOException, InterruptedException { try { GitLabApi gitLabApi = apiBuilder(this.getOwner(), serverName); getGitlabProject(gitLabApi); setProjectId(gitlabProject.getId()); sshRemote = gitlabProject.getSshUrlToRepo(); httpRemote = gitlabProject.getHttpUrlToRepo(); - try (GitLabSCMSourceRequest request = new GitLabSCMSourceContext(criteria, observer) - .withTraits(getTraits()) - .newRequest(this, listener)) { + GitLabSCMSourceContext ctx = new GitLabSCMSourceContext(criteria, observer); + try (GitLabSCMSourceRequest request = ctx + .withTraits(getTraits()) + .newRequest(this, listener)) { request.setGitLabApi(gitLabApi); request.setProject(gitlabProject); request.setMembers(getMembers()); @@ -323,16 +321,26 @@ protected void retrieve(SCMSourceCriteria criteria, @NonNull SCMHeadObserver obs // If `forkedFromProject` is null it doesn't mean anything if (gitlabProject.getForkedFromProject() == null) { listener.getLogger() - .format( - "%nUnable to detect if it is a mirror or not still fetching MRs anyway...%n"); + .format( + "%nUnable to detect if it is a mirror or not still fetching MRs anyway...%n"); List mrs = gitLabApi.getMergeRequestApi() - .getMergeRequests(gitlabProject, Constants.MergeRequestState.OPENED); + .getMergeRequests(gitlabProject, Constants.MergeRequestState.OPENED); mrs = mrs.stream().filter(mr -> mr.getSourceProjectId() != null) - .collect(Collectors.toList()); + .collect(Collectors.toList()); + request.setMergeRequests(mrs); + } else if (ctx.buildMRForksNotMirror()) { + listener.getLogger() + .format( + "%nCollecting MRs for fork except those that target its upstream...%n"); + List mrs = gitLabApi.getMergeRequestApi() + .getMergeRequests(gitlabProject, Constants.MergeRequestState.OPENED); + mrs = mrs.stream().filter(mr -> mr.getSourceProjectId() != null + && !mr.getTargetProjectId().equals(gitlabProject.getForkedFromProject().getId())) + .collect(Collectors.toList()); request.setMergeRequests(mrs); } else { listener.getLogger() - .format("%nIgnoring merge requests as project is a mirror...%n"); + .format("%nIgnoring merge requests as project is a mirror...%n"); } } if (request.isFetchTags()) { @@ -347,34 +355,32 @@ protected void retrieve(SCMSourceCriteria criteria, @NonNull SCMHeadObserver obs String branchName = branch.getName(); String sha = branch.getCommit().getId(); listener.getLogger().format("%nChecking branch %s%n", - HyperlinkNote.encodeTo( - branchUriTemplate(gitlabProject.getWebUrl()) - .set("branch", splitPath(branchName)) - .expand(), - branchName - ) - ); + HyperlinkNote.encodeTo( + branchUriTemplate(gitlabProject.getWebUrl()) + .set("branch", splitPath(branchName)) + .expand(), + branchName)); if (request.process(new BranchSCMHead(branchName), - (SCMSourceRequest.RevisionLambda) head -> - new BranchSCMRevision(head, sha), - new SCMSourceRequest.ProbeLambda() { - @NonNull - @Override - public SCMSourceCriteria.Probe create(@NonNull BranchSCMHead head, - @Nullable BranchSCMRevision revision) - throws IOException { - return createProbe(head, revision); - } - }, - (SCMSourceRequest.Witness) (head, revision, isMatch) -> { - if (isMatch) { - listener.getLogger().format("Met criteria%n"); - } else { - listener.getLogger().format("Does not meet criteria%n"); - } - })) { + (SCMSourceRequest.RevisionLambda) head -> new BranchSCMRevision( + head, sha), + new SCMSourceRequest.ProbeLambda() { + @NonNull + @Override + public SCMSourceCriteria.Probe create(@NonNull BranchSCMHead head, + @Nullable BranchSCMRevision revision) + throws IOException { + return createProbe(head, revision); + } + }, + (SCMSourceRequest.Witness) (head, revision, isMatch) -> { + if (isMatch) { + listener.getLogger().format("Met criteria%n"); + } else { + listener.getLogger().format("Does not meet criteria%n"); + } + })) { listener.getLogger() - .format("%n%d branches were processed (query completed)%n", count); + .format("%n%d branches were processed (query completed)%n", count); return; } } @@ -386,103 +392,98 @@ public SCMSourceCriteria.Probe create(@NonNull BranchSCMHead head, HashMap forkMrSources = new HashMap<>(); for (MergeRequest mr : request.getMergeRequests()) { mergeRequestContributorCache.put(mr.getIid(), - new ContributorMetadataAction( - mr.getAuthor().getUsername(), - mr.getAuthor().getName(), - mr.getAuthor().getEmail() - )); + new ContributorMetadataAction( + mr.getAuthor().getUsername(), + mr.getAuthor().getName(), + mr.getAuthor().getEmail())); mergeRequestMetadataCache.put(mr.getIid(), - new ObjectMetadataAction( - mr.getTitle(), - mr.getDescription(), - mr.getWebUrl() - )); + new ObjectMetadataAction( + mr.getTitle(), + mr.getDescription(), + mr.getWebUrl())); count++; listener.getLogger().format("%nChecking merge request %s%n", - HyperlinkNote.encodeTo( - mergeRequestUriTemplate(gitlabProject.getWebUrl()) - .set("iid", mr.getIid()) - .expand(), - "!" + mr.getIid() - ) - ); + HyperlinkNote.encodeTo( + mergeRequestUriTemplate(gitlabProject.getWebUrl()) + .set("iid", mr.getIid()) + .expand(), + "!" + mr.getIid())); Map> strategies = request - .getMRStrategies(); + .getMRStrategies(); boolean fork = !mr.getSourceProjectId().equals(mr.getTargetProjectId()); String originOwner = mr.getAuthor().getUsername(); String originProjectPath = projectPath; if (fork && !forkMrSources.containsKey(mr.getSourceProjectId())) { - // This is a hack to get the path with namespace of source project for forked mrs + // This is a hack to get the path with namespace of source project for forked + // mrs originProjectPath = gitLabApi.getProjectApi() - .getProject(mr.getSourceProjectId()).getPathWithNamespace(); + .getProject(mr.getSourceProjectId()).getPathWithNamespace(); forkMrSources.put(mr.getSourceProjectId(), originProjectPath); } else if (fork) { originProjectPath = forkMrSources.get(mr.getSourceProjectId()); } String targetSha; try { - targetSha = gitLabApi.getRepositoryApi().getBranch(mr.getTargetProjectId(), mr.getTargetBranch()).getCommit().getId(); + targetSha = gitLabApi.getRepositoryApi() + .getBranch(mr.getTargetProjectId(), mr.getTargetBranch()).getCommit().getId(); } catch (Exception e) { - listener.getLogger().format("Failed getting TargetBranch from Merge Request: " + mr.getIid() + " (" + mr.getTitle() + ")%n%s", e); + listener.getLogger().format("Failed getting TargetBranch from Merge Request: " + mr.getIid() + + " (" + mr.getTitle() + ")%n%s", e); continue; } - LOGGER.log(Level.FINE, String.format("%s -> %s", originOwner, (request.isMember(originOwner) ? "Trusted" - : "Untrusted"))); + LOGGER.log(Level.FINE, + String.format("%s -> %s", originOwner, (request.isMember(originOwner) ? "Trusted" + : "Untrusted"))); for (ChangeRequestCheckoutStrategy strategy : strategies.get(fork)) { if (request.process(new MergeRequestSCMHead( "MR-" + mr.getIid() + (strategies.get(fork).size() > 1 ? "-" + strategy.name() - .toLowerCase(Locale.ENGLISH) : ""), + .toLowerCase(Locale.ENGLISH) : ""), mr.getIid(), new BranchSCMHead(mr.getTargetBranch()), strategy, fork - ? new SCMHeadOrigin.Fork(originProjectPath) - : SCMHeadOrigin.DEFAULT, + ? new SCMHeadOrigin.Fork(originProjectPath) + : SCMHeadOrigin.DEFAULT, originOwner, originProjectPath, mr.getSourceBranch(), - mr.getTitle() - ), - (SCMSourceRequest.RevisionLambda) head -> - new MergeRequestSCMRevision( - head, - new BranchSCMRevision( - head.getTarget(), - targetSha // Latest revision of target branch - ), - new BranchSCMRevision( - new BranchSCMHead(head.getOriginName()), - mr.getSha() - ) - ), - new SCMSourceRequest.ProbeLambda() { - @NonNull - @Override - public SCMSourceCriteria.Probe create( - @NonNull MergeRequestSCMHead head, - @Nullable MergeRequestSCMRevision revision) - throws IOException, InterruptedException { - boolean isTrusted = request.isTrusted(head); - if (!isTrusted) { - listener.getLogger() - .format("(not from a trusted source)%n"); + mr.getTitle()), + (SCMSourceRequest.RevisionLambda) head -> new MergeRequestSCMRevision( + head, + new BranchSCMRevision( + head.getTarget(), + targetSha // Latest revision of target branch + ), + new BranchSCMRevision( + new BranchSCMHead(head.getOriginName()), + mr.getSha())), + new SCMSourceRequest.ProbeLambda() { + @NonNull + @Override + public SCMSourceCriteria.Probe create( + @NonNull MergeRequestSCMHead head, + @Nullable MergeRequestSCMRevision revision) + throws IOException, InterruptedException { + boolean isTrusted = request.isTrusted(head); + if (!isTrusted) { + listener.getLogger() + .format("(not from a trusted source)%n"); + } + return createProbe(isTrusted ? head : head.getTarget(), + revision); } - return createProbe(isTrusted ? head : head.getTarget(), - revision); - } - }, - (SCMSourceRequest.Witness) (head, revision, isMatch) -> { - if (isMatch) { - listener.getLogger().format("Met criteria%n"); - } else { - listener.getLogger().format("Does not meet criteria%n"); - } - } - )) { + }, + (SCMSourceRequest.Witness) (head, revision, isMatch) -> { + if (isMatch) { + listener.getLogger().format("Met criteria%n"); + } else { + listener.getLogger().format("Does not meet criteria%n"); + } + })) { listener.getLogger() - .format( - "%n%d merge requests were processed (query completed)%n", - count); + .format( + "%n%d merge requests were processed (query completed)%n", + count); return; } } @@ -499,38 +500,36 @@ public SCMSourceCriteria.Probe create( Long tagDate = tag.getCommit().getCommittedDate().getTime(); String sha = tag.getCommit().getId(); listener.getLogger().format("%nChecking tag %s%n", - HyperlinkNote.encodeTo( - tagUriTemplate(gitlabProject.getWebUrl()) - .set("tag", splitPath(tag.getName())) - .expand(), - tag.getName() - ) - ); + HyperlinkNote.encodeTo( + tagUriTemplate(gitlabProject.getWebUrl()) + .set("tag", splitPath(tag.getName())) + .expand(), + tag.getName())); GitLabTagSCMHead head = new GitLabTagSCMHead(tagName, tagDate); if (request.process(head, new GitTagSCMRevision(head, sha), - new SCMSourceRequest.ProbeLambda() { - @NonNull - @Override - public SCMSourceCriteria.Probe create( - @NonNull GitLabTagSCMHead head, - @Nullable GitTagSCMRevision revision) - throws IOException { - return createProbe(head, revision); - } - }, (SCMSourceRequest.Witness) (head1, revision, isMatch) -> { - if (isMatch) { - listener.getLogger().format("Met criteria%n"); - } else { - listener.getLogger().format("Does not meet criteria%n"); - } - })) { + new SCMSourceRequest.ProbeLambda() { + @NonNull + @Override + public SCMSourceCriteria.Probe create( + @NonNull GitLabTagSCMHead head, + @Nullable GitTagSCMRevision revision) + throws IOException { + return createProbe(head, revision); + } + }, (SCMSourceRequest.Witness) (head1, revision, isMatch) -> { + if (isMatch) { + listener.getLogger().format("Met criteria%n"); + } else { + listener.getLogger().format("Does not meet criteria%n"); + } + })) { listener.getLogger() - .format("%n%d tags were processed (query completed)%n", count); + .format("%n%d tags were processed (query completed)%n", count); return; } } listener.getLogger() - .format("%n%d tags were processed (query completed)%n", count); + .format("%n%d tags were processed (query completed)%n", count); } } } catch (GitLabApiException e) { @@ -725,7 +724,7 @@ public void afterSave() { // the jenkins instance. if (server != null && server.isManageWebHooks()) { GitLabSCMSourceContext ctx = new GitLabSCMSourceContext(null, SCMHeadObserver.none()) - .withTraits(new GitLabSCMNavigatorContext().withTraits(traits).traits()); + .withTraits(new GitLabSCMNavigatorContext().withTraits(traits).traits()); GitLabHookRegistration webhookMode = ctx.webhookRegistration(); GitLabHookRegistration systemhookMode = ctx.systemhookRegistration(); GitLabHookCreator.register(this, webhookMode, systemhookMode); @@ -807,7 +806,8 @@ context, StandardUsernameCredentials.class, fromUri(getServerUrlFromName(serverN return result; } - public long getProjectId(@AncestorInPath SCMSourceOwner context, @QueryParameter String projectPath, @QueryParameter String serverName) { + public long getProjectId(@AncestorInPath SCMSourceOwner context, @QueryParameter String projectPath, + @QueryParameter String serverName) { List gitLabServers = GitLabServers.get().getServers(); if (gitLabServers.size() == 0) { return -1; @@ -899,7 +899,7 @@ public List getTraitsDefaults() { new BranchDiscoveryTrait(true, false), new OriginMergeRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.MERGE)), new ForkMergeRequestDiscoveryTrait(EnumSet.of(ChangeRequestCheckoutStrategy.MERGE), - new ForkMergeRequestDiscoveryTrait.TrustPermission()), + new ForkMergeRequestDiscoveryTrait.TrustPermission(), false), new WebhookListenerBuildConditionsTrait()); } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java index b9e2d283..ed6b553e 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMSourceContext.java @@ -31,6 +31,8 @@ public class GitLabSCMSourceContext @NonNull private GitLabHookRegistration systemhookRegistration = GitLabHookRegistration.SYSTEM; + private boolean buildMRForksNotMirror; + private boolean notificationsDisabled; private boolean logCommentEnabled; @@ -49,6 +51,8 @@ public class GitLabSCMSourceContext private String buildStatusNameCustomPart = ""; + private boolean buildStatusNameOverwrite; + private boolean alwaysBuildMROpen = true; private boolean alwaysBuildMRReOpen = true; @@ -108,6 +112,10 @@ public final GitLabHookRegistration systemhookRegistration() { return systemhookRegistration; } + public final boolean buildMRForksNotMirror() { + return buildMRForksNotMirror; + } + public final boolean notificationsDisabled() { return notificationsDisabled; } @@ -172,6 +180,10 @@ public final String getBuildStatusNameCustomPart() { return buildStatusNameCustomPart; } + public boolean getBuildStatusNameOverwrite() { + return buildStatusNameOverwrite; + } + @NonNull public GitLabSCMSourceContext wantBranches(boolean include) { wantBranches = wantBranches || include; @@ -222,6 +234,12 @@ public final GitLabSCMSourceContext systemhookRegistration(GitLabHookRegistratio return this; } + @NonNull + public final GitLabSCMSourceContext withBuildMRForksNotMirror(boolean disabled) { + this.buildMRForksNotMirror = disabled; + return this; + } + @NonNull public final GitLabSCMSourceContext withNotificationsDisabled(boolean disabled) { this.notificationsDisabled = disabled; @@ -273,6 +291,11 @@ public final GitLabSCMSourceContext withBuildStatusNameCustomPart(final String b return this; } + public final GitLabSCMSourceContext withBuildStatusNameOverwrite(final Boolean buildStatusNameOverwrite) { + this.buildStatusNameOverwrite = buildStatusNameOverwrite; + return this; + } + @NonNull @Override public GitLabSCMSourceRequest newRequest(@NonNull SCMSource source, @@ -290,22 +313,22 @@ public final GitLabSCMSourceContext withAlwaysBuildMRReOpen(boolean enabled) { return this; } - public final GitLabSCMSourceContext withalwaysIgnoreMRApproval(boolean enabled) { + public final GitLabSCMSourceContext withAlwaysIgnoreMRApproval(boolean enabled) { this.alwaysIgnoreMRApproval = enabled; return this; } - public final GitLabSCMSourceContext withalwaysIgnoreMRUnApproval(boolean enabled) { + public final GitLabSCMSourceContext withAlwaysIgnoreMRUnApproval(boolean enabled) { this.alwaysIgnoreMRUnApproval = enabled; return this; } - public final GitLabSCMSourceContext withalwaysIgnoreMRApproved(boolean enabled) { + public final GitLabSCMSourceContext withAlwaysIgnoreMRApproved(boolean enabled) { this.alwaysIgnoreMRApproved = enabled; return this; } - public final GitLabSCMSourceContext withalwaysIgnoreMRUnApproved(boolean enabled) { + public final GitLabSCMSourceContext withAlwaysIgnoreMRUnApproved(boolean enabled) { this.alwaysIgnoreMRUnApproved = enabled; return this; } diff --git a/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java b/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java index 93d1f0ca..f2e6908a 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/WebhookListenerBuildConditionsTrait.java @@ -61,10 +61,10 @@ protected void decorateContext(SCMSourceContext context) { GitLabSCMSourceContext ctx = (GitLabSCMSourceContext) context; ctx.withAlwaysBuildMROpen(getAlwaysBuildMROpen()) .withAlwaysBuildMRReOpen(getAlwaysBuildMRReOpen()) - .withalwaysIgnoreMRApproval(getalwaysIgnoreMRApproval()) - .withalwaysIgnoreMRUnApproval(getalwaysIgnoreMRUnApproval()) - .withalwaysIgnoreMRApproved(getalwaysIgnoreMRApproved()) - .withalwaysIgnoreMRUnApproved(getalwaysIgnoreMRUnApproved()) + .withAlwaysIgnoreMRApproval(getAlwaysIgnoreMRApproval()) + .withAlwaysIgnoreMRUnApproval(getAlwaysIgnoreMRUnApproval()) + .withAlwaysIgnoreMRApproved(getAlwaysIgnoreMRApproved()) + .withAlwaysIgnoreMRUnApproved(getAlwaysIgnoreMRUnApproved()) .withAlwaysIgnoreNonCodeRelatedUpdates(getAlwaysIgnoreNonCodeRelatedUpdates()); } } @@ -116,7 +116,7 @@ public boolean getAlwaysBuildMRReOpen() { * * @return false to run build on MR approval */ - public boolean getalwaysIgnoreMRApproval() { + public boolean getAlwaysIgnoreMRApproval() { return alwaysIgnoreMRApproval; } @@ -125,7 +125,7 @@ public boolean getalwaysIgnoreMRApproval() { * * @return false to run build on non-code related MR updates */ - public boolean getalwaysIgnoreMRUnApproval() { + public boolean getAlwaysIgnoreMRUnApproval() { return alwaysIgnoreMRUnApproval; } @@ -134,7 +134,7 @@ public boolean getalwaysIgnoreMRUnApproval() { * * @return false to run build on MR approved */ - public boolean getalwaysIgnoreMRApproved() { + public boolean getAlwaysIgnoreMRApproved() { return alwaysIgnoreMRApproved; } @@ -143,7 +143,7 @@ public boolean getalwaysIgnoreMRApproved() { * * @return false to run build on non-code related MR updates */ - public boolean getalwaysIgnoreMRUnApproved() { + public boolean getAlwaysIgnoreMRUnApproved() { return alwaysIgnoreMRUnApproved; } @@ -176,7 +176,7 @@ public void setAlwaysBuildMRReOpen(boolean alwaysBuildMRReOpen) { * Setter for stapler to set the alwaysIgnoreMRApproval of the WebhookListener */ @DataBoundSetter - public void setalwaysIgnoreMRApproval(boolean alwaysIgnoreMRApproval) { + public void setAlwaysIgnoreMRApproval(boolean alwaysIgnoreMRApproval) { this.alwaysIgnoreMRApproval = alwaysIgnoreMRApproval; } @@ -184,7 +184,7 @@ public void setalwaysIgnoreMRApproval(boolean alwaysIgnoreMRApproval) { * Setter for stapler to set the alwaysIgnoreMRUnApproval of the WebhookListener */ @DataBoundSetter - public void setalwaysIgnoreMRUnApproval(boolean alwaysIgnoreMRUnApproval) { + public void setAlwaysIgnoreMRUnApproval(boolean alwaysIgnoreMRUnApproval) { this.alwaysIgnoreMRUnApproval = alwaysIgnoreMRUnApproval; } @@ -192,7 +192,7 @@ public void setalwaysIgnoreMRUnApproval(boolean alwaysIgnoreMRUnApproval) { * Setter for stapler to set the alwaysIgnoreMRApproved of the WebhookListener */ @DataBoundSetter - public void setalwaysIgnoreMRApproved(boolean alwaysIgnoreMRApproved) { + public void setAlwaysIgnoreMRApproved(boolean alwaysIgnoreMRApproved) { this.alwaysIgnoreMRApproved = alwaysIgnoreMRApproved; } @@ -201,7 +201,7 @@ public void setalwaysIgnoreMRApproved(boolean alwaysIgnoreMRApproved) { * WebhookListener */ @DataBoundSetter - public void setalwaysIgnoreMRUnApproved(boolean alwaysIgnoreMRUnApproved) { + public void setAlwaysIgnoreMRUnApproved(boolean alwaysIgnoreMRUnApproved) { this.alwaysIgnoreMRUnApproved = alwaysIgnoreMRUnApproved; } 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 e2825b95..26fe8df9 100644 --- a/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java +++ b/src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifier.java @@ -1,11 +1,13 @@ package io.jenkins.plugins.gitlabbranchsource.helpers; import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.EnvVars; import hudson.Extension; import hudson.ExtensionList; import hudson.FilePath; import hudson.model.Computer; import hudson.model.Job; +import hudson.model.Node; import hudson.model.Queue; import hudson.model.Result; import hudson.model.Run; @@ -47,14 +49,16 @@ import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider; /** - * Publishes Build-Status to GitLab using separate threads so it does not block while sending - * messages TODO: Multi-Threading is easy to get wrong and wreak havoc. Check if there is no better + * Publishes Build-Status to GitLab using separate threads so it does not block + * while sending + * messages TODO: Multi-Threading is easy to get wrong and wreak havoc. Check if + * there is no better * way to do this built into Jenkins */ public class GitLabPipelineStatusNotifier { private static final Logger LOGGER = Logger - .getLogger(GitLabPipelineStatusNotifier.class.getName()); + .getLogger(GitLabPipelineStatusNotifier.class.getName()); static final String GITLAB_PIPELINE_STATUS_PREFIX = "jenkinsci"; @@ -71,9 +75,9 @@ private static String getRootUrl(Run build) { } private static GitLabSCMSourceContext getSourceContext(Run build, - GitLabSCMSource source) { + GitLabSCMSource source) { return new GitLabSCMSourceContext(null, SCMHeadObserver.none()) - .withTraits((source.getTraits())); + .withTraits((source.getTraits())); } private static GitLabSCMSource getSource(Run build) { @@ -84,15 +88,18 @@ private static GitLabSCMSource getSource(Run build) { return null; } - private static String getStatusName(final GitLabSCMSourceContext sourceContext, final Run build, final SCMRevision revision) { - return getStatusName(sourceContext, build.getFullDisplayName(), revision); + private static String getStatusName(final GitLabSCMSourceContext sourceContext, final Run build, + @NonNull EnvVars envVars, final SCMRevision revision) { + return getStatusName(sourceContext, build.getFullDisplayName(), revision, envVars); } - private static String getStatusName(final GitLabSCMSourceContext sourceContext, final Job job, final SCMRevision revision) { - return getStatusName(sourceContext, job.getFullDisplayName(), revision); + private static String getStatusName(final GitLabSCMSourceContext sourceContext, final Job job, + @NonNull EnvVars envVars, final SCMRevision revision) { + return getStatusName(sourceContext, job.getFullDisplayName(), revision, envVars); } - static String getStatusName(final GitLabSCMSourceContext sourceContext, final String fullDisplayName, final SCMRevision revision) { + static String getStatusName(final GitLabSCMSourceContext sourceContext, final String fullDisplayName, + final SCMRevision revision, @NonNull EnvVars envVars) { final String type; if (revision instanceof BranchSCMRevision) { type = "branch"; @@ -103,16 +110,19 @@ static String getStatusName(final GitLabSCMSourceContext sourceContext, final St } else { type = "UNKNOWN"; LOGGER.log(Level.WARNING, () -> "Unknown SCMRevision implementation " - + revision.getClass().getName() + ", append" + type + " to status name"); + + revision.getClass().getName() + ", append" + type + " to status name"); } - String customPrefix = sourceContext.getBuildStatusNameCustomPart(); - if (!customPrefix.isEmpty()) - { - customPrefix = customPrefix + GITLAB_PIPELINE_STATUS_DELIMITER; + String pipelinePrefix = sourceContext.getBuildStatusNameCustomPart().trim(); + if (!pipelinePrefix.isEmpty()) { + pipelinePrefix = envVars.expand(pipelinePrefix) + GITLAB_PIPELINE_STATUS_DELIMITER; + if (!sourceContext.getBuildStatusNameOverwrite()) { + pipelinePrefix = GITLAB_PIPELINE_STATUS_PREFIX + GITLAB_PIPELINE_STATUS_DELIMITER + pipelinePrefix; + } + } else { + pipelinePrefix = GITLAB_PIPELINE_STATUS_PREFIX + GITLAB_PIPELINE_STATUS_DELIMITER; } - - final String statusName = GITLAB_PIPELINE_STATUS_PREFIX + GITLAB_PIPELINE_STATUS_DELIMITER + customPrefix + type; + final String statusName = pipelinePrefix + type; LOGGER.log(Level.FINEST, () -> "Retrieved status name is: " + statusName); return statusName; } @@ -147,8 +157,8 @@ private static void logComment(Run build, TaskListener listener) { String url = getRootUrl(build); if (url.isEmpty()) { listener.getLogger().println( - "Can not determine Jenkins root URL. Comments are disabled until a root URL is" - + " configured in Jenkins global configuration."); + "Can not determine Jenkins root URL. Comments are disabled until a root URL is" + + " configured in Jenkins global configuration."); return; } Result result = build.getResult(); @@ -179,29 +189,35 @@ private static void logComment(Run build, TaskListener listener) { if (!sudoUsername.isEmpty()) { gitLabApi.sudo(sudoUsername); } - final String buildName = "**" + getStatusName(sourceContext, build, revision) + ":** "; - final String hash; - if (revision instanceof BranchSCMRevision) { - hash = ((BranchSCMRevision) revision).getHash(); - gitLabApi.getCommitsApi().addComment( - source.getProjectPath(), - hash, - symbol + buildName + note + suffix - ); - } else if (revision instanceof MergeRequestSCMRevision) { - MergeRequestSCMHead head = (MergeRequestSCMHead) revision.getHead(); - gitLabApi.getNotesApi().createMergeRequestNote( - source.getProjectPath(), - Long.valueOf(head.getId()), - symbol + buildName + note + suffix - ); - } else if (revision instanceof GitTagSCMRevision) { - hash = ((GitTagSCMRevision) revision).getHash(); - gitLabApi.getCommitsApi().addComment( - source.getProjectPath(), - hash, - symbol + buildName + note + suffix - ); + try { + EnvVars envVars = build.getEnvironment(listener); + final String buildName = "**" + getStatusName(sourceContext, build, envVars, revision) + ":** "; + final String hash; + if (revision instanceof BranchSCMRevision) { + hash = ((BranchSCMRevision) revision).getHash(); + gitLabApi.getCommitsApi().addComment( + source.getProjectPath(), + hash, + symbol + buildName + note + suffix); + } else if (revision instanceof MergeRequestSCMRevision) { + MergeRequestSCMHead head = (MergeRequestSCMHead) revision.getHead(); + gitLabApi.getNotesApi().createMergeRequestNote( + source.getProjectPath(), + Long.valueOf(head.getId()), + symbol + buildName + note + suffix); + } else if (revision instanceof GitTagSCMRevision) { + hash = ((GitTagSCMRevision) revision).getHash(); + gitLabApi.getCommitsApi().addComment( + source.getProjectPath(), + hash, + symbol + buildName + note + suffix); + } + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.INFO, + "Could not send status notification for " + build.getFullDisplayName() + + " to " + source + .getServerName(), + e); } } catch (GitLabApiException e) { LOGGER.log(Level.WARNING, "Exception caught:" + e, e); @@ -215,21 +231,21 @@ static Long getSourceProjectId(Job job, GitLabApi gitLabApi, String projectPath) LOGGER.log(Level.INFO, "Getting source project ID from MR"); Matcher m = MERGE_REQUEST_JOB_NAME_FORMAT.matcher(job.getName()); if (!m.matches()) { - LOGGER.log(Level.WARNING, String.format("Job name does not match expected format: [%s], [%s]", job.getName(), - MERGE_REQUEST_JOB_NAME_FORMAT.pattern())); + LOGGER.log(Level.WARNING, + String.format("Job name does not match expected format: [%s], [%s]", job.getName(), + MERGE_REQUEST_JOB_NAME_FORMAT.pattern())); return null; } Long mrId = Long.parseLong(m.group(1)); MergeRequest mr; try { - mr = gitLabApi.getMergeRequestApi().getMergeRequest( - projectPath, - mrId - ); + mr = gitLabApi.getMergeRequestApi().getMergeRequest( + projectPath, + mrId); } catch (GitLabApiException e) { - if(!e.getMessage().contains(("Cannot transition status"))) { - LOGGER.log(Level.WARNING, String.format("Exception caught: %s",e.getMessage())); + if (!e.getMessage().contains(("Cannot transition status"))) { + LOGGER.log(Level.WARNING, String.format("Exception caught: %s", e.getMessage())); } return null; } @@ -254,8 +270,8 @@ private static void sendNotifications(Run build, TaskListener listener) { String url = getRootUrl(build); if (url.isEmpty()) { listener.getLogger().println( - "Can not determine Jenkins root URL. Commit status notifications are disabled until a root URL is" - + " configured in Jenkins global configuration."); + "Can not determine Jenkins root URL. Commit status notifications are disabled until a root URL is" + + " configured in Jenkins global configuration."); return; } Result result = build.getResult(); @@ -291,58 +307,68 @@ private static void sendNotifications(Run build, TaskListener listener) { String hash; if (revision instanceof BranchSCMRevision) { listener.getLogger() - .format("[GitLab Pipeline Status] Notifying branch build status: %s %s%n", - status.getStatus(), status.getDescription()); + .format("[GitLab Pipeline Status] Notifying branch build status: %s %s%n", + status.getStatus(), status.getDescription()); hash = ((BranchSCMRevision) revision).getHash(); } else if (revision instanceof MergeRequestSCMRevision) { listener.getLogger() - .format("[GitLab Pipeline Status] Notifying merge request build status: %s %s%n", - status.getStatus(), status.getDescription()); + .format("[GitLab Pipeline Status] Notifying merge request build status: %s %s%n", + status.getStatus(), status.getDescription()); hash = ((MergeRequestSCMRevision) revision).getOrigin().getHash(); } else if (revision instanceof GitTagSCMRevision) { listener.getLogger() - .format("[GitLab Pipeline Status] Notifying tag build status: %s %s%n", - status.getStatus(), status.getDescription()); + .format("[GitLab Pipeline Status] Notifying tag build status: %s %s%n", + status.getStatus(), status.getDescription()); hash = ((GitTagSCMRevision) revision).getHash(); } else { return; } - status.setName(getStatusName(sourceContext, build, revision)); - status.setRef(getRevisionRef(revision)); - - final JobScheduledListener jsl = ExtensionList.lookup(QueueListener.class) - .get(JobScheduledListener.class); - if (jsl != null) { - // we are setting the status, so don't let the queue listener background thread change it to pending - synchronized (jsl.resolving) { - jsl.resolving.remove(build.getParent()); - } - } try { - GitLabApi gitLabApi = GitLabHelper.apiBuilder(build.getParent(), source.getServerName()); - LOGGER.log(Level.FINE, String.format("Notifiying commit: %s", hash)); - - if (revision instanceof MergeRequestSCMRevision) { - Long projectId = getSourceProjectId(build.getParent(), gitLabApi, source.getProjectPath()); - status.setRef(((MergeRequestSCMRevision) revision).getOrigin().getHead().getName()); - gitLabApi.getCommitsApi().addCommitStatus( - projectId, - hash, - state, - status); - } else { - gitLabApi.getCommitsApi().addCommitStatus( - source.getProjectPath(), - hash, - state, - status); + EnvVars envVars = build.getEnvironment(listener); + status.setName(getStatusName(sourceContext, build, envVars, revision)); + status.setRef(getRevisionRef(revision)); + + final JobScheduledListener jsl = ExtensionList.lookup(QueueListener.class) + .get(JobScheduledListener.class); + if (jsl != null) { + // we are setting the status, so don't let the queue listener background thread + // change it to pending + synchronized (jsl.resolving) { + jsl.resolving.remove(build.getParent()); + } } + try { + GitLabApi gitLabApi = GitLabHelper.apiBuilder(build.getParent(), source.getServerName()); + LOGGER.log(Level.FINE, String.format("Notifiying commit: %s", hash)); + + if (revision instanceof MergeRequestSCMRevision) { + Long projectId = getSourceProjectId(build.getParent(), gitLabApi, source.getProjectPath()); + status.setRef(((MergeRequestSCMRevision) revision).getOrigin().getHead().getName()); + gitLabApi.getCommitsApi().addCommitStatus( + projectId, + hash, + state, + status); + } else { + gitLabApi.getCommitsApi().addCommitStatus( + source.getProjectPath(), + hash, + state, + status); + } - listener.getLogger().format("[GitLab Pipeline Status] Notified%n"); - } catch (GitLabApiException e) { - if(!e.getMessage().contains(("Cannot transition status"))) { - LOGGER.log(Level.WARNING, String.format("Exception caught: %s",e.getMessage())); + listener.getLogger().format("[GitLab Pipeline Status] Notified%n"); + } catch (GitLabApiException e) { + if (!e.getMessage().contains(("Cannot transition status"))) { + LOGGER.log(Level.WARNING, String.format("Exception caught: %s", e.getMessage())); + } } + } catch (IOException | InterruptedException e) { + LOGGER.log(Level.INFO, + "Could not send status notification for " + build.getFullDisplayName() + + " to " + source + .getServerName(), + e); } } @@ -368,8 +394,8 @@ public void onEnterWaiting(final Queue.WaitingItem wi) { } final GitLabSCMSource source = (GitLabSCMSource) src; final GitLabSCMSourceContext sourceContext = new GitLabSCMSourceContext(null, - SCMHeadObserver.none()) - .withTraits((source.getTraits())); + SCMHeadObserver.none()) + .withTraits((source.getTraits())); if (sourceContext.notificationsDisabled()) { return; } @@ -384,26 +410,30 @@ public void onEnterWaiting(final Queue.WaitingItem wi) { // prevent delays in the queue when updating GitLab Computer.threadPoolForRemoting.submit(() -> { try (ACLContext ctx = ACL.as(Tasks.getAuthenticationOf(wi.task))) { + final TaskListener listener = new LogTaskListener(LOGGER, Level.INFO); final SCMRevision revision = source - .fetch(head, new LogTaskListener(LOGGER, Level.INFO)); + .fetch(head, listener); String hash; final CommitStatus status = new CommitStatus(); if (revision instanceof BranchSCMRevision) { LOGGER.log(Level.INFO, "Notifying branch pending build {0}", - job.getFullName()); + job.getFullName()); hash = ((BranchSCMRevision) revision).getHash(); } else if (revision instanceof MergeRequestSCMRevision) { LOGGER.log(Level.INFO, "Notifying merge request pending build {0}", - job.getFullName()); + job.getFullName()); hash = ((MergeRequestSCMRevision) revision).getOrigin().getHash(); } else if (revision instanceof GitTagSCMRevision) { LOGGER.log(Level.INFO, "Notifying tag pending build {0}", - job.getFullName()); + job.getFullName()); hash = ((GitTagSCMRevision) revision).getHash(); } else { return; } - status.setName(getStatusName(sourceContext, job, revision)); + Computer c = Computer.currentComputer(); + Node n = c == null ? null : c.getNode(); + EnvVars envVars = job.getEnvironment(n, listener); + status.setName(getStatusName(sourceContext, job, envVars, revision)); status.setRef(getRevisionRef(revision)); String url; @@ -425,8 +455,8 @@ public void onEnterWaiting(final Queue.WaitingItem wi) { if (!nonce.equals(resolving.get(job))) { // it's not our nonce, so drop LOGGER.log(Level.INFO, - "{0} has already started, skipping notification of queued", - job.getFullName()); + "{0} has already started, skipping notification of queued", + job.getFullName()); return; } // it is our nonce, so remove it @@ -437,29 +467,30 @@ public void onEnterWaiting(final Queue.WaitingItem wi) { Long projectId = getSourceProjectId(job, gitLabApi, source.getProjectPath()); status.setRef(((MergeRequestSCMRevision) revision).getOrigin().getHead().getName()); gitLabApi.getCommitsApi().addCommitStatus( - projectId, - hash, - state, - status); + projectId, + hash, + state, + status); } else { gitLabApi.getCommitsApi().addCommitStatus( - source.getProjectPath(), - hash, - state, - status); + source.getProjectPath(), + hash, + state, + status); } LOGGER.log(Level.INFO, "{0} Notified", job.getFullName()); } catch (GitLabApiException e) { - if(!e.getMessage().contains("Cannot transition status")) { + if (!e.getMessage().contains("Cannot transition status")) { LOGGER.log(Level.WARNING, String.format("Exception caught: %s", e.getMessage())); } } } catch (IOException | InterruptedException e) { LOGGER.log(Level.INFO, - "Could not send commit status notification for " + job.getFullName() - + " to " + source - .getServerName(), e); + "Could not send commit status notification for " + job.getFullName() + + " to " + source + .getServerName(), + e); } }); } @@ -471,8 +502,8 @@ public static class JobCheckOutListener extends SCMListener { @Override public void onCheckout(Run build, SCM scm, FilePath workspace, TaskListener listener, - File changelogFile, - SCMRevisionState pollingBaseline) { + File changelogFile, + SCMRevisionState pollingBaseline) { LOGGER.log(Level.FINE, String.format("SCMListener: Checkout > %s", build.getFullDisplayName())); sendNotifications(build, listener); } diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/config.jelly b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/config.jelly index 81fd330d..df194f88 100644 --- a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/config.jelly @@ -3,4 +3,7 @@ + + + diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusContextPrefix.html b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameCustomPart.html similarity index 100% rename from src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusContextPrefix.html rename to src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameCustomPart.html diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameOverwrite.html b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameOverwrite.html new file mode 100644 index 00000000..c0539272 --- /dev/null +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help-buildStatusNameOverwrite.html @@ -0,0 +1,4 @@ +
+ Overwrites the build status name including the jenkinsci default part.
+ Instead of 'jenkinsci/custom/branch' just 'custom/branch'. +
diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help.html b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help.html new file mode 100644 index 00000000..1be35d42 --- /dev/null +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/BuildStatusNameCustomPartTrait/help.html @@ -0,0 +1,3 @@ +
+ Customize the pipeline status name used by Jenkins +
\ No newline at end of file diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/config.jelly b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/config.jelly index e88d72e3..1620d3fd 100644 --- a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/config.jelly @@ -6,4 +6,7 @@ + + + diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/help-buildMRForksNotMirror.html b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/help-buildMRForksNotMirror.html new file mode 100644 index 00000000..65416eab --- /dev/null +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/ForkMergeRequestDiscoveryTrait/help-buildMRForksNotMirror.html @@ -0,0 +1,7 @@ +
+ Add discovery of merge requests where the origin project is a fork of a certain project, + but the target project is not the original forked project. + To be used in case one has a GitLab project which is a fork of another project from another team, in order to isolate artefacts and allow an MR flow. + This means using MRs inside that fork from branches in the fork back to the fork's default branch. + (Implements https://github.com/jenkinsci/gitlab-branch-source-plugin/issues/167) +
\ No newline at end of file diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/GitLabAvatarTrait/config.jelly b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/GitLabAvatarTrait/config.jelly index ce39c573..983a4303 100644 --- a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/GitLabAvatarTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/GitLabAvatarTrait/config.jelly @@ -1,6 +1,6 @@ - + diff --git a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/TriggerMRCommentTrait/config.jelly b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/TriggerMRCommentTrait/config.jelly index 85cc3eee..0fe954c9 100644 --- a/src/main/resources/io/jenkins/plugins/gitlabbranchsource/TriggerMRCommentTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/gitlabbranchsource/TriggerMRCommentTrait/config.jelly @@ -4,6 +4,6 @@ - + diff --git a/src/test/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifierTest.java b/src/test/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifierTest.java index a73cd694..4acdad82 100644 --- a/src/test/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifierTest.java +++ b/src/test/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabPipelineStatusNotifierTest.java @@ -31,7 +31,8 @@ public void should_set_branch_status_name() { BranchSCMHead head = new BranchSCMHead("head"); SCMRevision revision = new BranchSCMRevision(head, "hash"); - String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, null, revision); + String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, null, revision, + new hudson.EnvVars()); assertThat(statusName, is(GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_PREFIX + GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_DELIMITER @@ -49,7 +50,8 @@ public void should_set_merge_request_head_status_name() { BranchSCMRevision source = new BranchSCMRevision(new BranchSCMHead("source"), "source-hash"); SCMRevision revision = new MergeRequestSCMRevision(head, target, source); - String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, "head", revision); + String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, "head", revision, + new hudson.EnvVars()); assertThat(statusName, is(GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_PREFIX + GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_DELIMITER @@ -67,7 +69,8 @@ public void should_set_merge_request_merge_status_name() { BranchSCMRevision source = new BranchSCMRevision(new BranchSCMHead("source"), "source-hash"); SCMRevision revision = new MergeRequestSCMRevision(head, target, source); - String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, "merge", revision); + String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, "merge", revision, + new hudson.EnvVars()); assertThat(statusName, is(GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_PREFIX + GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_DELIMITER @@ -81,7 +84,8 @@ public void should_set_tag_status_name() { GitTagSCMHead head = new GitTagSCMHead("tagName", 0); SCMRevision revision = new GitTagSCMRevision(head, "tag-hash"); - String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, null, revision); + String statusName = GitLabPipelineStatusNotifier.getStatusName(sourceContext, null, revision, + new hudson.EnvVars()); assertThat(statusName, is(GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_PREFIX + GitLabPipelineStatusNotifier.GITLAB_PIPELINE_STATUS_DELIMITER @@ -135,7 +139,7 @@ public void should_get_mr_project_id() throws Exception { ItemGroup parent = Mockito.mock(ItemGroup.class); Mockito.when(parent.getFullName()).thenReturn("folder/project"); - Job job = new FreeStyleProject(parent, "MR-123"); + Job job = new FreeStyleProject(parent, "MR-123"); GitLabApi gitLabApi = Mockito.mock(GitLabApi.class); MergeRequestApi mrApi = Mockito.mock(MergeRequestApi.class); @@ -158,7 +162,7 @@ public void should_get_mr_project_id_projects_using_both_strategy_head() throws ItemGroup parent = Mockito.mock(ItemGroup.class); Mockito.when(parent.getFullName()).thenReturn("folder/project"); - Job job = new FreeStyleProject(parent, "MR-123-head"); + Job job = new FreeStyleProject(parent, "MR-123-head"); GitLabApi gitLabApi = Mockito.mock(GitLabApi.class); MergeRequestApi mrApi = Mockito.mock(MergeRequestApi.class); @@ -181,7 +185,7 @@ public void should_get_mr_project_id_projects_using_both_strategy_merge() throws ItemGroup parent = Mockito.mock(ItemGroup.class); Mockito.when(parent.getFullName()).thenReturn("folder/project"); - Job job = new FreeStyleProject(parent, "MR-123-merge"); + Job job = new FreeStyleProject(parent, "MR-123-merge"); GitLabApi gitLabApi = Mockito.mock(GitLabApi.class); MergeRequestApi mrApi = Mockito.mock(MergeRequestApi.class);