package fr.techad.sonar; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jetbrains.annotations.NotNull; import org.sonar.api.batch.fs.InputComponent; import org.sonar.api.batch.fs.InputPath; import org.sonar.api.batch.postjob.PostJob; import org.sonar.api.batch.postjob.PostJobContext; import org.sonar.api.batch.postjob.PostJobDescriptor; import org.sonar.api.batch.postjob.issue.PostJobIssue; import org.sonar.api.config.Settings; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import fr.techad.sonar.gerrit.GerritFacade; import fr.techad.sonar.gerrit.factory.GerritFacadeFactory; import fr.techad.sonar.gerrit.review.ReviewFileComment; import fr.techad.sonar.gerrit.review.ReviewInput; import fr.techad.sonar.gerrit.review.ReviewLineComment; import fr.techad.sonar.gerrit.utils.ReviewUtils; import fr.techad.sonar.utils.MessageUtils; public class GerritPostJob implements PostJob { private static final Logger LOG = Loggers.get(GerritPostJob.class); private final Settings settings; private final GerritConfiguration gerritConfiguration; private List<String> gerritModifiedFiles; private GerritFacade gerritFacade; private ReviewUtils reviewUtils; private MessageUtils messageUtils; private ReviewInput reviewInput = ReviewHolder.getReviewInput(); public GerritPostJob(Settings settings, GerritConfiguration gerritConfiguration, GerritFacadeFactory gerritFacadeFactory, ReviewUtils reviewUtils, MessageUtils messageUtils) { LOG.debug("[GERRIT PLUGIN] Instanciating GerritPostJob"); this.settings = settings; this.gerritFacade = gerritFacadeFactory.getFacade(); this.gerritConfiguration = gerritConfiguration; this.reviewUtils = reviewUtils; this.messageUtils = messageUtils; } @Override public void describe(PostJobDescriptor descriptor) { descriptor.name("GERRIT PLUGIN"); descriptor.requireProperty(PropertyKey.GERRIT_CHANGE_ID); } @Override public void execute(PostJobContext postJobContext) { if (!gerritConfiguration.isEnabled()) { LOG.info("[GERRIT PLUGIN] PostJob : analysis has finished. Plugin is disabled. No actions taken."); return; } Map<InputPath, List<PostJobIssue>> issueMap = new HashMap<>(); for (PostJobIssue i : postJobContext.issues()) { InputComponent inputComponent = i.inputComponent(); if (inputComponent instanceof InputPath) { InputPath inputPath = (InputPath) inputComponent; List<PostJobIssue> l = issueMap.get(inputPath); if (l == null) { l = new ArrayList<>(); issueMap.put(inputPath, l); } l.add(i); } } for (Map.Entry<InputPath, List<PostJobIssue>> e : issueMap.entrySet()) { decorate(e.getKey(), postJobContext, e.getValue()); } try { LOG.info("[GERRIT PLUGIN] Analysis has finished. Sending results to Gerrit."); reviewInput.setMessage(messageUtils.createMessage(gerritConfiguration.getMessage(), settings)); LOG.debug("[GERRIT PLUGIN] Define message : {}", reviewInput.getMessage()); LOG.debug("[GERRIT PLUGIN] Number of comments : {}", reviewInput.size()); int maxLevel = reviewInput.maxLevelSeverity(); LOG.debug("[GERRIT PLUGIN] Configured threshold {}, max review level {}", gerritConfiguration.getThreshold(), reviewUtils.valueToThreshold(maxLevel)); if (reviewInput.isEmpty()) { LOG.debug("[GERRIT PLUGIN] No issues ! Vote {} for the label : {}", gerritConfiguration.getVoteNoIssue(), gerritConfiguration.getLabel()); reviewInput.setValueAndLabel(gerritConfiguration.getVoteNoIssue(), gerritConfiguration.getLabel()); } else if (maxLevel < reviewUtils.thresholdToValue(gerritConfiguration.getThreshold())) { LOG.debug("[GERRIT PLUGIN] Issues below threshold. Vote {} for the label : {}", gerritConfiguration.getVoteBelowThreshold(), gerritConfiguration.getLabel()); reviewInput.setValueAndLabel(gerritConfiguration.getVoteBelowThreshold(), gerritConfiguration.getLabel()); } else { LOG.debug("[GERRIT PLUGIN] Issues above threshold. Vote {} for the label : {}", gerritConfiguration.getVoteAboveThreshold(), gerritConfiguration.getLabel()); reviewInput.setValueAndLabel(gerritConfiguration.getVoteAboveThreshold(), gerritConfiguration.getLabel()); } LOG.debug("[GERRIT PLUGIN] Send review for ChangeId={}, RevisionId={}", gerritConfiguration.getChangeId(), gerritConfiguration.getRevisionId()); gerritFacade.setReview(reviewInput); } catch (GerritPluginException e) { LOG.error("[GERRIT PLUGIN] Error sending review to Gerrit", e); } } protected void decorate(InputPath resource, PostJobContext context, Collection<PostJobIssue> issues) { LOG.debug("[GERRIT PLUGIN] Decorate: {}", resource.relativePath()); if (!resource.file().isFile()) { LOG.debug("[GERRIT PLUGIN] {} is not a file", resource.relativePath()); return; } try { LOG.debug("[GERRIT PLUGIN] Start Sonar decoration for Gerrit"); assertOrFetchGerritModifiedFiles(); } catch (GerritPluginException e) { LOG.error("[GERRIT PLUGIN] Error getting Gerrit datas", e); } LOG.debug("[GERRIT PLUGIN] Look for in Gerrit if the file was under review, resource={}", resource); LOG.debug("[GERRIT PLUGIN] Look for in Gerrit if the file was under review, name={}", resource.relativePath()); LOG.debug("[GERRIT PLUGIN] Look for in Gerrit if the file was under review, key={}", resource.key()); String filename = getFileNameFromInputPath(resource); if (filename != null) { LOG.info("[GERRIT PLUGIN] Found a match between Sonar and Gerrit for {}: ", resource.relativePath(), filename); processFileResource(filename, issues); } } protected void assertOrFetchGerritModifiedFiles() throws GerritPluginException { if (gerritModifiedFiles != null) { return; } gerritModifiedFiles = gerritFacade.listFiles(); LOG.debug("[GERRIT PLUGIN] Modified files in gerrit : {}", gerritModifiedFiles); } protected ReviewLineComment issueToComment(PostJobIssue issue) { ReviewLineComment result = new ReviewLineComment(); result.setLine(issue.line()); result.setSeverity(reviewUtils.thresholdToValue(issue.severity().toString())); result.setMessage(messageUtils.createIssueMessage(gerritConfiguration.getIssueComment(), settings, issue)); LOG.debug("[GERRIT PLUGIN] issueToComment {}", result.toString()); return result; } protected void processFileResource(@NotNull String file, @NotNull Collection<PostJobIssue> issuable) { List<ReviewFileComment> comments = new ArrayList<ReviewFileComment>(); commentIssues(issuable, comments); if (!comments.isEmpty()) { reviewInput.addComments(file, comments); } } private void commentIssues(Collection<PostJobIssue> issues, List<ReviewFileComment> comments) { LOG.info("[GERRIT PLUGIN] Found {} issues", issues.size()); for (PostJobIssue issue : issues) { if (gerritConfiguration.shouldCommentNewIssuesOnly() && !issue.isNew()) { LOG.info( "[GERRIT PLUGIN] Issue is not new and only new one should be commented. Will not push back to Gerrit."); } else { comments.add(issueToComment(issue)); } } } private String getFileNameFromInputPath(InputPath resource) { String filename = null; if (gerritModifiedFiles.contains(resource.relativePath())) { LOG.info("[GERRIT PLUGIN] Found a match between Sonar and Gerrit for {}", resource.relativePath()); filename = resource.relativePath(); } else if (gerritModifiedFiles.contains(gerritFacade.parseFileName(resource.relativePath()))) { LOG.info("[GERRIT PLUGIN] Found a match between Sonar and Gerrit for {}", gerritFacade.parseFileName(resource.relativePath())); filename = gerritFacade.parseFileName(resource.relativePath()); } else { LOG.debug("[GERRIT PLUGIN] Parse the Gerrit List to look for the resource: {}", resource.relativePath()); // Loop on each item for (String fileGerrit : gerritModifiedFiles) { if (gerritFacade.parseFileName(fileGerrit).equals(resource.relativePath())) { filename = fileGerrit; break; } } } if (filename == null) { LOG.debug("[GERRIT PLUGIN] File '{}' was not found in the review list)", resource.relativePath()); LOG.debug("[GERRIT PLUGIN] Try to find with: '{}', '{}' and '{}'", resource.relativePath(), gerritFacade.parseFileName(resource.relativePath())); } return filename; } }