From 540ea0e17bca596250c3c0b19c1ce30e0431286f Mon Sep 17 00:00:00 2001 From: Sergiu Oprea Date: Mon, 12 May 2025 15:27:14 +0300 Subject: [PATCH] add disable toggle and user role configuration for rerun action --- .../github/CheckRunGHEventSubscriber.java | 84 +++++++++---- .../GitHubSCMSourceStatusChecksTrait.java | 23 ++++ .../GitHubStatusChecksConfigurations.java | 24 ++++ .../status/GitHubStatusChecksProperties.java | 8 ++ .../status/GitSCMStatusChecksExtension.java | 22 ++++ .../config.jelly | 8 ++ .../GitSCMStatusChecksExtension/config.jelly | 10 +- .../github/CheckRunGHEventSubscriberTest.java | 119 ++++++++++++++++-- .../GitHubStatusChecksPropertiesTest.java | 19 ++- 9 files changed, 274 insertions(+), 43 deletions(-) diff --git a/src/main/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriber.java b/src/main/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriber.java index 22ed35ed..b4ae33ca 100644 --- a/src/main/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriber.java +++ b/src/main/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriber.java @@ -29,10 +29,13 @@ import hudson.model.Job; import hudson.model.ParametersAction; import hudson.model.Run; +import hudson.model.User; import hudson.security.ACL; import hudson.security.ACLContext; import jenkins.model.ParameterizedJobMixIn; +import io.jenkins.plugins.checks.github.status.GitHubStatusChecksProperties; + import io.jenkins.plugins.util.JenkinsFacade; /** @@ -45,20 +48,23 @@ public class CheckRunGHEventSubscriber extends GHEventsSubscriber { private final JenkinsFacade jenkinsFacade; private final SCMFacade scmFacade; + private final GitHubStatusChecksProperties githubStatusChecksProperties; /** * Construct the subscriber. */ public CheckRunGHEventSubscriber() { - this(new JenkinsFacade(), new SCMFacade()); + this(new JenkinsFacade(), new SCMFacade(), new GitHubStatusChecksProperties()); } @VisibleForTesting - CheckRunGHEventSubscriber(final JenkinsFacade jenkinsFacade, final SCMFacade scmFacade) { + CheckRunGHEventSubscriber(final JenkinsFacade jenkinsFacade, final SCMFacade scmFacade, + final GitHubStatusChecksProperties githubStatusChecksProperties) { super(); this.jenkinsFacade = jenkinsFacade; this.scmFacade = scmFacade; + this.githubStatusChecksProperties = githubStatusChecksProperties; } @Override @@ -91,7 +97,43 @@ protected void onEvent(final GHSubscriberEvent event) { LOGGER.log(Level.INFO, "Received rerun request through GitHub checks API."); try (ACLContext ignored = ACL.as2(ACL.SYSTEM2)) { String branchName = payloadJSON.getJSONObject("check_run").getJSONObject("check_suite").optString("head_branch"); - scheduleRerun(checkRun, branchName); + final GHRepository repository = checkRun.getRepository(); + + Optional> optionalRun = jenkinsFacade.getBuild(checkRun.getCheckRun().getExternalId()); + if (optionalRun.isPresent()) { + Run run = optionalRun.get(); + Job job = run.getParent(); + boolean isDisableRerunAction = githubStatusChecksProperties.isDisableRerunAction(job); + String rerunActionRole = githubStatusChecksProperties.getRerunActionRole(job); + + if (!isDisableRerunAction) { + if (!rerunActionRole.isBlank()) { + User user = User.get(checkRun.getSender().getLogin()); + List userRoles = user.getAuthorities(); + if (userRoles.contains(rerunActionRole)) { + scheduleRerun(checkRun, branchName, run, job); + } else { + LOGGER.log( + Level.WARNING, + String.format( + "The user %s does not have the required %s role for the rerun action on job %s", + checkRun.getSender().getLogin(), + rerunActionRole, + jenkinsFacade.getFullNameOf(job) + ) + ); + } + } else { + scheduleRerun(checkRun, branchName, run, job); + } + } else { + LOGGER.log(Level.INFO, String.format("Rerun action is disabled for job %s", jenkinsFacade.getFullNameOf(job))); + } + } + else { + LOGGER.log(Level.WARNING, String.format("No build found for rerun request from repository: %s and id: %s", + repository.getFullName(), checkRun.getCheckRun().getExternalId()).replaceAll("[\r\n]", "")); + } } } catch (IOException | JSONException e) { @@ -99,34 +141,22 @@ protected void onEvent(final GHSubscriberEvent event) { } } - private void scheduleRerun(final GHEventPayload.CheckRun checkRun, final String branchName) { - final GHRepository repository = checkRun.getRepository(); - - Optional> optionalRun = jenkinsFacade.getBuild(checkRun.getCheckRun().getExternalId()); - if (optionalRun.isPresent()) { - Run run = optionalRun.get(); - Job job = run.getParent(); + private void scheduleRerun(final GHEventPayload.CheckRun checkRun, final String branchName, final Run run, final Job job) { + Cause cause = new GitHubChecksRerunActionCause(checkRun.getSender().getLogin(), branchName); - Cause cause = new GitHubChecksRerunActionCause(checkRun.getSender().getLogin(), branchName); + List actions = new ArrayList<>(); + actions.add(new CauseAction(cause)); - List actions = new ArrayList<>(); - actions.add(new CauseAction(cause)); - - ParametersAction paramAction = run.getAction(ParametersAction.class); - if (paramAction != null) { - actions.add(paramAction); - } + ParametersAction paramAction = run.getAction(ParametersAction.class); + if (paramAction != null) { + actions.add(paramAction); + } - ParameterizedJobMixIn.scheduleBuild2(job, 0, actions.toArray(new Action[0])); + ParameterizedJobMixIn.scheduleBuild2(job, 0, actions.toArray(new Action[0])); - LOGGER.log(Level.INFO, String.format("Scheduled rerun (build #%d) for job %s, requested by %s", - job.getNextBuildNumber(), jenkinsFacade.getFullNameOf(job), - checkRun.getSender().getLogin()).replaceAll("[\r\n]", "")); - } - else { - LOGGER.log(Level.WARNING, String.format("No build found for rerun request from repository: %s and id: %s", - repository.getFullName(), checkRun.getCheckRun().getExternalId()).replaceAll("[\r\n]", "")); - } + LOGGER.log(Level.INFO, String.format("Scheduled rerun (build #%d) for job %s, requested by %s", + job.getNextBuildNumber(), jenkinsFacade.getFullNameOf(job), + checkRun.getSender().getLogin()).replaceAll("[\r\n]", "")); } /** diff --git a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait.java b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait.java index 7abf52b8..10b9759b 100644 --- a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait.java +++ b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait.java @@ -30,6 +30,8 @@ public class GitHubSCMSourceStatusChecksTrait extends SCMSourceTrait implements private String name = "Jenkins"; private boolean suppressLogs = false; private boolean skipProgressUpdates = false; + private String rerunActionRole = ""; + private boolean disableRerunAction = false; /** * Constructor for stapler. @@ -79,10 +81,21 @@ public boolean isSuppressLogs() { return suppressLogs; } + @Override + public String getRerunActionRole() { + return rerunActionRole; + } + + @Override + public boolean isDisableRerunAction() { + return disableRerunAction; + } + @Override public boolean isSkipProgressUpdates() { return skipProgressUpdates; } + @DataBoundSetter public void setSkipProgressUpdates(final boolean skipProgressUpdates) { @@ -126,6 +139,16 @@ public void setSuppressLogs(final boolean suppressLogs) { this.suppressLogs = suppressLogs; } + @DataBoundSetter + public void setRerunActionRole(final String rerunActionRole) { + this.rerunActionRole = rerunActionRole; + } + + @DataBoundSetter + public void setDisableRerunAction(final boolean disableRerunAction) { + this.disableRerunAction = disableRerunAction; + } + @Override protected void decorateContext(final SCMSourceContext context) { if (isSkipNotifications() && context instanceof GitHubSCMSourceContext) { diff --git a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksConfigurations.java b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksConfigurations.java index f952d398..8086325a 100644 --- a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksConfigurations.java +++ b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksConfigurations.java @@ -32,6 +32,20 @@ public interface GitHubStatusChecksConfigurations { */ boolean isSuppressLogs(); + /** + * Defines whether a role is required to use the rerun action. + * + * @return the name of the role + */ + String getRerunActionRole(); + + /** + * Defines whether to disable rerun action. + * + * @return true to disable rerun action + */ + boolean isDisableRerunAction(); + /** * Returns whether to suppress progress updates from the {@code io.jenkins.plugins.checks.status.FlowExecutionAnalyzer}. * Queued, Checkout and Completed will still run but not 'onNewHead' @@ -62,6 +76,16 @@ public boolean isSuppressLogs() { return false; } + @Override + public String getRerunActionRole() { + return ""; + } + + @Override + public boolean isDisableRerunAction() { + return false; + } + @Override public boolean isSkipProgressUpdates() { return false; diff --git a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksProperties.java b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksProperties.java index 4425fb98..ab83d88b 100644 --- a/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksProperties.java +++ b/src/main/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksProperties.java @@ -64,6 +64,14 @@ public boolean isSuppressLogs(final Job job) { return getConfigurations(job).orElse(DEFAULT_CONFIGURATION).isSuppressLogs(); } + public String getRerunActionRole(final Job job) { + return getConfigurations(job).orElse(DEFAULT_CONFIGURATION).getRerunActionRole(); + } + + public boolean isDisableRerunAction(final Job job) { + return getConfigurations(job).orElse(DEFAULT_CONFIGURATION).isDisableRerunAction(); + } + @Override public boolean isSkipProgressUpdates(final Job job) { return getConfigurations(job).orElse(DEFAULT_CONFIGURATION).isSkipProgressUpdates(); diff --git a/src/main/java/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension.java b/src/main/java/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension.java index 8884dd39..11cd03ec 100644 --- a/src/main/java/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension.java +++ b/src/main/java/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension.java @@ -22,6 +22,8 @@ public class GitSCMStatusChecksExtension extends GitSCMExtension implements GitH private String name = "Jenkins"; private boolean suppressLogs; private boolean skipProgressUpdates = false; + private String rerunActionRole = ""; + private boolean disableRerunAction = false; /** * Constructor for stapler. @@ -51,6 +53,16 @@ public boolean isSuppressLogs() { return suppressLogs; } + @Override + public String getRerunActionRole() { + return rerunActionRole; + } + + @Override + public boolean isDisableRerunAction() { + return disableRerunAction; + } + @Override public boolean isSkipProgressUpdates() { return skipProgressUpdates; @@ -93,6 +105,16 @@ public void setSuppressLogs(final boolean suppressLogs) { this.suppressLogs = suppressLogs; } + @DataBoundSetter + public void setRerunActionRole(final String rerunActionRole) { + this.rerunActionRole = rerunActionRole; + } + + @DataBoundSetter + public void setDisableRerunAction(final boolean disableRerunAction) { + this.disableRerunAction = disableRerunAction; + } + /** * Descriptor implementation for {@link GitSCMStatusChecksExtension}. */ diff --git a/src/main/resources/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait/config.jelly b/src/main/resources/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait/config.jelly index f93172cd..3a52f646 100644 --- a/src/main/resources/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait/config.jelly +++ b/src/main/resources/io/jenkins/plugins/checks/github/status/GitHubSCMSourceStatusChecksTrait/config.jelly @@ -7,4 +7,12 @@ + + + + + + + + diff --git a/src/main/resources/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension/config.jelly b/src/main/resources/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension/config.jelly index 38e5e245..22e4d219 100644 --- a/src/main/resources/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension/config.jelly +++ b/src/main/resources/io/jenkins/plugins/checks/github/status/GitSCMStatusChecksExtension/config.jelly @@ -1,6 +1,14 @@ - + + + + + + + + + diff --git a/src/test/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriberTest.java b/src/test/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriberTest.java index 843b0705..dc2f12c8 100644 --- a/src/test/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriberTest.java +++ b/src/test/java/io/jenkins/plugins/checks/github/CheckRunGHEventSubscriberTest.java @@ -5,7 +5,9 @@ import hudson.model.ParametersAction; import hudson.model.Run; import hudson.model.StringParameterValue; +import hudson.model.User; import io.jenkins.plugins.util.JenkinsFacade; +import io.jenkins.plugins.checks.github.status.GitHubStatusChecksProperties; import org.apache.commons.io.FileUtils; import org.jenkinsci.plugins.github.extension.GHSubscriberEvent; import org.jenkinsci.plugins.github_branch_source.GitHubSCMSource; @@ -18,11 +20,16 @@ import java.nio.charset.StandardCharsets; import java.util.Optional; import java.util.logging.Level; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.mockito.MockedStatic; +import static org.mockito.Mockito.mockStatic; class CheckRunGHEventSubscriberTest { @@ -37,10 +44,11 @@ void shouldBeApplicableForJobWithGitHubSCMSource() { JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); SCMFacade scmFacade = mock(SCMFacade.class); GitHubSCMSource source = mock(GitHubSCMSource.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); when(scmFacade.findGitHubSCMSource(job)).thenReturn(Optional.of(source)); - assertThat(new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade).isApplicable(job)) + assertThat(new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties).isApplicable(job)) .isTrue(); } @@ -66,7 +74,7 @@ void shouldSubscribeToCheckRunEvent() { @Test void shouldProcessCheckRunEventWithRerequestedAction() throws IOException { try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { - new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class)) + new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class), mock(GitHubStatusChecksProperties.class)) .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); assertThat(logRecorder.getMessages().get(0)).contains("Received rerun request through GitHub checks API."); } @@ -75,7 +83,7 @@ void shouldProcessCheckRunEventWithRerequestedAction() throws IOException { @Test void shouldThrowExceptionWhenCheckSuitesMissingFromPayload() { assertThatThrownBy( - () -> new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class)) + () -> new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class), mock(GitHubStatusChecksProperties.class)) .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR_MISSING_CHECKSUITE))) .isInstanceOf(IllegalStateException.class) .hasMessageContaining("Could not parse check run event:"); @@ -84,7 +92,7 @@ void shouldThrowExceptionWhenCheckSuitesMissingFromPayload() { @Test void shouldIgnoreHeadBranchMissingFromPayload() throws IOException { try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { - new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class)) + new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class), mock(GitHubStatusChecksProperties.class)) .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR_MISSING_CHECKSUITE_HEAD_BRANCH)); assertThat(logRecorder.getMessages().get(0)).contains("Received rerun request through GitHub checks API."); } @@ -93,7 +101,7 @@ void shouldIgnoreHeadBranchMissingFromPayload() throws IOException { @Test void shouldIgnoreCheckRunEventWithoutRerequestedAction() throws IOException { try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.FINE).capture(1)) { - new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class)) + new CheckRunGHEventSubscriber(mock(JenkinsFacade.class), mock(SCMFacade.class), mock(GitHubStatusChecksProperties.class)) .onEvent(createEventWithRerunRequest("check-run-event-with-created-action.json")); assertThat(logRecorder.getMessages()).contains("Unsupported check run action: created"); } @@ -105,6 +113,7 @@ void shouldScheduleRerunForPR() throws IOException { Run run = mock(Run.class); JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); SCMFacade scmFacade = mock(SCMFacade.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); when(jenkinsFacade.getBuild("codingstyle/PR-1#2")).thenReturn(Optional.of(run)); when(jenkinsFacade.getFullNameOf(job)).thenReturn("codingstyle/PR-1"); @@ -114,9 +123,11 @@ void shouldScheduleRerunForPR() throws IOException { ); when(job.getNextBuildNumber()).thenReturn(1); when(job.getName()).thenReturn("PR-1"); + when(githubStatusChecksProperties.getRerunActionRole(job)).thenReturn(""); + when(githubStatusChecksProperties.isDisableRerunAction(job)).thenReturn(false); try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { - new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade) + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties) .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); assertThat(logRecorder.getMessages()) .contains("Scheduled rerun (build #1) for job codingstyle/PR-1, requested by XiongKezhi"); @@ -129,6 +140,7 @@ void shouldScheduleRerunForMaster() throws IOException { Run run = mock(Run.class); JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); SCMFacade scmFacade = mock(SCMFacade.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); when(jenkinsFacade.getBuild("codingstyle/master#8")).thenReturn(Optional.of(run)); when(jenkinsFacade.getFullNameOf(job)).thenReturn("codingstyle/master"); @@ -136,9 +148,11 @@ void shouldScheduleRerunForMaster() throws IOException { when(run.getAction(ParametersAction.class)).thenReturn(null); when(job.getNextBuildNumber()).thenReturn(1); when(job.getName()).thenReturn("master"); + when(githubStatusChecksProperties.getRerunActionRole(job)).thenReturn(""); + when(githubStatusChecksProperties.isDisableRerunAction(job)).thenReturn(false); try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { - new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade) + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties) .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_MASTER)); assertThat(logRecorder.getMessages()) .contains("Scheduled rerun (build #1) for job codingstyle/master, requested by XiongKezhi"); @@ -150,10 +164,96 @@ void shouldNotScheduleRerunWhenNoProperBuildFound() throws IOException { JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); when(jenkinsFacade.getBuild("codingstyle/PR-1#2")).thenReturn(Optional.empty()); - assertNoBuildIsScheduled(jenkinsFacade, mock(SCMFacade.class), + assertNoBuildIsScheduled(jenkinsFacade, mock(SCMFacade.class), mock(GitHubStatusChecksProperties.class), createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); } + @Test + void shouldNotScheduleRerunWhenDisabled() throws IOException { + Job job = mock(Job.class); + Run run = mock(Run.class); + JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); + SCMFacade scmFacade = mock(SCMFacade.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); + + when(jenkinsFacade.getBuild("codingstyle/PR-1#2")).thenReturn(Optional.of(run)); + when(jenkinsFacade.getFullNameOf(job)).thenReturn("codingstyle/PR-1"); + when(run.getParent()).thenReturn(job); + when(githubStatusChecksProperties.isDisableRerunAction(job)).thenReturn(true); + + try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties) + .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); + assertThat(logRecorder.getMessages()) + .contains("Rerun action is disabled for job codingstyle/PR-1"); + } + } + + @Test + void shouldScheduleRerunWhenUserHasRequiredRole() throws IOException { + Job job = mock(Job.class); + Run run = mock(Run.class); + JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); + SCMFacade scmFacade = mock(SCMFacade.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); + User user = mock(User.class); + List userRoles = new ArrayList<>(); + userRoles.add("test-role"); + + when(jenkinsFacade.getBuild("codingstyle/PR-1#2")).thenReturn(Optional.of(run)); + when(jenkinsFacade.getFullNameOf(job)).thenReturn("codingstyle/PR-1"); + when(run.getParent()).thenReturn(job); + when(run.getAction(ParametersAction.class)).thenReturn( + new ParametersAction(new StringParameterValue("test_key", "test_value")) + ); + when(job.getNextBuildNumber()).thenReturn(1); + when(job.getName()).thenReturn("PR-1"); + when(githubStatusChecksProperties.getRerunActionRole(job)).thenReturn("test-role"); + when(githubStatusChecksProperties.isDisableRerunAction(job)).thenReturn(false); + try(MockedStatic staticUser = mockStatic(User.class)) { + staticUser.when(() -> User.get("XiongKezhi")).thenReturn(user); + when(user.getAuthorities()).thenReturn(userRoles); + try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties) + .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); + assertThat(logRecorder.getMessages()) + .contains("Scheduled rerun (build #1) for job codingstyle/PR-1, requested by XiongKezhi"); + } + } + } + + @Test + void shouldNotScheduleRerunWhenUserDoesNotHaveRequiredRole() throws IOException { + Job job = mock(Job.class); + Run run = mock(Run.class); + JenkinsFacade jenkinsFacade = mock(JenkinsFacade.class); + SCMFacade scmFacade = mock(SCMFacade.class); + GitHubStatusChecksProperties githubStatusChecksProperties = mock(GitHubStatusChecksProperties.class); + User user = mock(User.class); + List userRoles = Collections.emptyList(); + + when(jenkinsFacade.getBuild("codingstyle/PR-1#2")).thenReturn(Optional.of(run)); + when(jenkinsFacade.getFullNameOf(job)).thenReturn("codingstyle/PR-1"); + when(run.getParent()).thenReturn(job); + when(run.getAction(ParametersAction.class)).thenReturn( + new ParametersAction(new StringParameterValue("test_key", "test_value")) + ); + when(job.getNextBuildNumber()).thenReturn(1); + when(job.getName()).thenReturn("PR-1"); + when(githubStatusChecksProperties.getRerunActionRole(job)).thenReturn("test-role"); + when(githubStatusChecksProperties.isDisableRerunAction(job)).thenReturn(false); + try(MockedStatic staticUser = mockStatic(User.class)) { + staticUser.when(() -> User.get("XiongKezhi")).thenReturn(user); + when(user.getAuthorities()).thenReturn(userRoles); + try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.INFO).capture(1)) { + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties) + .onEvent(createEventWithRerunRequest(RERUN_REQUEST_JSON_FOR_PR)); + assertThat(logRecorder.getMessages()) + .contains("The user XiongKezhi does not have the required test-role role for the rerun action on job codingstyle/PR-1"); + } + } + } + @Test void shouldContainsUserAndBranchInShortDescriptionOfGitHubChecksRerunActionCause() { CheckRunGHEventSubscriber.GitHubChecksRerunActionCause cause = @@ -171,9 +271,10 @@ void shouldHaveAccessibleBranchNameInGitHubChecksRerunActionCause() { } private static void assertNoBuildIsScheduled(final JenkinsFacade jenkinsFacade, final SCMFacade scmFacade, + final GitHubStatusChecksProperties githubStatusChecksProperties, final GHSubscriberEvent event) { try (LogRecorder logRecorder = new LogRecorder().record(CheckRunGHEventSubscriber.class.getName(), Level.WARNING).capture(1)) { - new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade).onEvent(event); + new CheckRunGHEventSubscriber(jenkinsFacade, scmFacade, githubStatusChecksProperties).onEvent(event); assertThat(logRecorder.getMessages()) .contains("No build found for rerun request from repository: XiongKezhi/codingstyle and id: codingstyle/PR-1#2"); } diff --git a/src/test/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksPropertiesTest.java b/src/test/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksPropertiesTest.java index 30a7e79a..67d5f2b1 100644 --- a/src/test/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksPropertiesTest.java +++ b/src/test/java/io/jenkins/plugins/checks/github/status/GitHubStatusChecksPropertiesTest.java @@ -30,9 +30,11 @@ void shouldUsePropertiesFromGitHubSCMSourceTrait() { trait.setSkip(true); trait.setUnstableBuildNeutral(true); trait.setSuppressLogs(true); + trait.setRerunActionRole("test-role"); + trait.setDisableRerunAction(true); assertJobWithStatusChecksProperties(job, new GitHubStatusChecksProperties(scmFacade), - true, "GitHub SCM Source", true, true, true); + true, "GitHub SCM Source", true, true, true, "test-role", true); } @Test @@ -50,9 +52,11 @@ void shouldUsePropertiesFromGitSCMExtension() { extension.setSkip(true); extension.setUnstableBuildNeutral(true); extension.setSuppressLogs(true); + extension.setRerunActionRole("test-role"); + extension.setDisableRerunAction(true); assertJobWithStatusChecksProperties(job, new GitHubStatusChecksProperties(scmFacade), - true, "Git SCM", true, true, true); + true, "Git SCM", true, true, true, "test-role", true); } @Test @@ -64,7 +68,7 @@ void shouldUseDefaultPropertiesWhenGitHubSCMSourceStatusChecksTraitIsNotAdded() when(scmFacade.findGitHubSCMSource(job)).thenReturn(Optional.of(source)); assertJobWithStatusChecksProperties(job, new GitHubStatusChecksProperties(scmFacade), - true, "Jenkins", false, false, false); + true, "Jenkins", false, false, false, "", false); } @Test @@ -78,7 +82,7 @@ void shouldUseDefaultPropertiesWhenGitSCMStatusChecksExtensionIsNotAdded() { when(scm.getExtensions()).thenReturn(extensionList); assertJobWithStatusChecksProperties(job, new GitHubStatusChecksProperties(scmFacade), - true, "Jenkins", false, false, false); + true, "Jenkins", false, false, false, "", false); } @Test @@ -89,18 +93,21 @@ void shouldNotApplicableToJobWithoutSupportedSCM() { when(scmFacade.findGitSCM(job)).thenReturn(Optional.empty()); when(scmFacade.findGitHubSCMSource(job)).thenReturn(Optional.empty()); assertJobWithStatusChecksProperties(job, new GitHubStatusChecksProperties(scmFacade), - false, "Jenkins", false, false, false); + false, "Jenkins", false, false, false, "", false); } private static void assertJobWithStatusChecksProperties(final Job job, final GitHubStatusChecksProperties properties, final boolean isApplicable, final String name, final boolean isSkip, final boolean isUnstableBuildNeutral, - final boolean isSuppressLogs) { + final boolean isSuppressLogs, final String rerunActionRole, + final boolean isDisableRerunAction) { assertThat(properties.isApplicable(job)).isEqualTo(isApplicable); assertThat(properties.getName(job)).isEqualTo(name); assertThat(properties.isSkipped(job)).isEqualTo(isSkip); assertThat(properties.isUnstableBuildNeutral(job)).isEqualTo(isUnstableBuildNeutral); assertThat(properties.isSuppressLogs(job)).isEqualTo(isSuppressLogs); + assertThat(properties.getRerunActionRole(job)).isEqualTo(rerunActionRole); + assertThat(properties.isDisableRerunAction(job)).isEqualTo(isDisableRerunAction); } }