package se.bjurr.sbcc; import static com.atlassian.bitbucket.permission.Permission.REPO_ADMIN; import static com.atlassian.bitbucket.user.UserType.SERVICE; import static java.lang.Boolean.TRUE; import static java.util.logging.Level.SEVERE; import static se.bjurr.sbcc.SbccPrinter.NL; import static se.bjurr.sbcc.settings.SbccSettings.sscSettings; import java.util.logging.Logger; import com.atlassian.applinks.api.ApplicationLinkService; import com.atlassian.bitbucket.auth.AuthenticationContext; import com.atlassian.bitbucket.hook.repository.RepositoryHookService; import com.atlassian.bitbucket.pull.PullRequest; import com.atlassian.bitbucket.repository.Repository; import com.atlassian.bitbucket.scm.pull.MergeRequest; import com.atlassian.bitbucket.scm.pull.MergeRequestCheck; import com.atlassian.bitbucket.setting.Settings; import com.atlassian.bitbucket.user.SecurityService; import com.atlassian.bitbucket.util.Operation; import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory; import com.google.common.annotations.VisibleForTesting; import se.bjurr.sbcc.commits.ChangeSetsService; import se.bjurr.sbcc.data.SbccVerificationResult; import se.bjurr.sbcc.settings.SbccSettings; public class SbccRepositoryMergeRequestCheck implements MergeRequestCheck { public static final String PR_REJECT_DEFAULT_MSG = "At least one commit in Pull Request is not ok"; private static final String HOOK_SETTINGS_KEY = "se.bjurr.sscc.sscc:pre-receive-repository-hook"; private static Logger logger = Logger.getLogger(SbccRepositoryMergeRequestCheck.class.getName()); private final ApplicationLinkService applicationLinkService; private final AuthenticationContext bitbucketAuthenticationContext; private ChangeSetsService changeSetService; private final RepositoryHookService repositoryHookService; private ResultsCallable resultsCallback = new ResultsCallable(); private final SbccUserAdminService sbccUserAdminService; private final SecurityService securityService; public SbccRepositoryMergeRequestCheck( ChangeSetsService changeSetService, AuthenticationContext bitbucketAuthenticationContext, ApplicationLinkService applicationLinkService, SbccUserAdminService sbccUserAdminService, PluginSettingsFactory pluginSettingsFactory, RepositoryHookService repositoryHookService, SecurityService securityService) { this.applicationLinkService = applicationLinkService; this.bitbucketAuthenticationContext = bitbucketAuthenticationContext; this.sbccUserAdminService = sbccUserAdminService; this.changeSetService = changeSetService; this.repositoryHookService = repositoryHookService; this.securityService = securityService; } @Override public void check(MergeRequest mergeRequest) { try { Repository repository = mergeRequest.getPullRequest().getToRef().getRepository(); Settings rawSettings = getSettings(repository, HOOK_SETTINGS_KEY); if (rawSettings == null) { logger.fine("No settings found for SBCC"); return; } SbccRenderer sbccRenderer = new SbccRenderer(this.bitbucketAuthenticationContext); SbccSettings settings = sscSettings(new RenderingSettings(rawSettings, sbccRenderer)); if (settings.isDryRun() || !settings.shouldCheckPullRequests() || settings.allowServiceUsers() && this.bitbucketAuthenticationContext.getCurrentUser().getType().equals(SERVICE)) { this.resultsCallback.report(TRUE, null, null); return; } PullRequest pullRequest = mergeRequest.getPullRequest(); RefChangeValidator sbccVerificationResult = new RefChangeValidator( pullRequest.getFromRef().getRepository(), pullRequest.getToRef().getRepository(), settings, this.changeSetService, this.bitbucketAuthenticationContext, sbccRenderer, this.applicationLinkService, this.sbccUserAdminService); SbccVerificationResult refChangeVerificationResults = new SbccVerificationResult(); sbccVerificationResult.validateRefChange(refChangeVerificationResults, pullRequest); boolean isAccepted = refChangeVerificationResults.isAccepted(); if (isAccepted) { this.resultsCallback.report(isAccepted, null, null); return; } String printOut = new SbccPrinter(settings, sbccRenderer) .printVerificationResults(refChangeVerificationResults) .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll(NL, "<br>\n"); String summary = settings.getShouldCheckPullRequestsMessage().or(PR_REJECT_DEFAULT_MSG); mergeRequest.veto(summary, printOut); this.resultsCallback.report(isAccepted, summary, printOut); } catch (Exception e) { logger.log(SEVERE, "", e); } } public void setChangesetsService(ChangeSetsService changeSetService) { this.changeSetService = changeSetService; } @VisibleForTesting public void setResultsCallback(ResultsCallable resultsCallback) { this.resultsCallback = resultsCallback; } private Settings getSettings(final Repository repository, final String settingsKey) throws Exception { return this.securityService .withPermission(REPO_ADMIN, "Retrieving settings") .call( new Operation<Settings, Exception>() { @Override public Settings perform() throws Exception { return SbccRepositoryMergeRequestCheck.this.repositoryHookService.getSettings( repository, settingsKey); } }); } }