/** * Copyright Promptu Systems Corporation 2016. All rights reserved. */ package bugzilla.rest; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.httpclient.HttpException; import com.perforce.p4java.core.IChangelistSummary; import bugzilla.BugMention; import p4.ChangeListener; /** * A listener of p4 changes that looks for changes related to bugs. When such a change is detected the p4 comment and a link to the * change is added to the bug as a comment. If the comment in the p4 change matches the bugfix pattern the bug is also marked as * resolved/fixed. * * @author Warwick Hunter * @since 2016-11-26 */ public class BugzillaListener implements ChangeListener { /** Pattern to recognise and extract single bug number */ private static final String ONE_NUMBER_REGEX = "(#?(\\d+)[,\\s]*)?"; /** The introductory parts of the pattern that are recognised */ private static final String[] REGEX_STARTS = { "[Bb]ugs?:?\\s*", "[Bb]ugfix:?\\s*" }; private static final Matcher NUMBER_MATCHER = Pattern.compile("[0-9]+.*").matcher(""); private static final Matcher REVIEW_MATCHER = Pattern.compile("#review-([0-9]+).*").matcher(""); /** Max number of bugs we support in the one p4 checkin */ private static final int MAX_BUGS = 100; private static final String REVIEW_FMT = "Review: %s/%d"; private final List<Matcher> m_bugNumberMatchers = new ArrayList<Matcher>(); private final BugzillaSession m_session; private final String m_swarmUrl; private final Logger m_logger = Logger.getLogger("p4bugzilla"); public BugzillaListener(String url, String username, String password, String swarmUrl) throws HttpException, IOException { m_session = new BugzillaSession(url); for (String regexStart : REGEX_STARTS) { StringBuilder fullRegex = new StringBuilder(regexStart); for (int i = 0; i < MAX_BUGS; ++i) { fullRegex.append(ONE_NUMBER_REGEX); } m_bugNumberMatchers.add(Pattern.compile(fullRegex.toString()).matcher("")); } m_swarmUrl = swarmUrl; m_session.login(username, password); } @Override public void handleChange(IChangelistSummary change) { try { String username = change.getUsername(); if (username.contains("@")) { // Strip off the @domain part of the user username = username.substring(0, username.indexOf("@")); } Set<BugMention> bugMentions = findBugMentions(change.getDescription()); for (BugMention bugMention : bugMentions) { BugzillaBug bug = new BugzillaBug(m_session); bug.load(bugMention.getBugNumber()); switch (change.getStatus()) { case PENDING: handleMentionOnPendingChange(change, bug, bugMention); break; case SUBMITTED: handleMentionOnSubmittedChange(change, bug, bugMention); break; default: break; } } if (bugMentions.isEmpty()) { m_logger.info(String.format("Change %d by %s ", change.getId(), change.getUsername())); } } catch (NoSuchElementException e) { m_logger.info(e.getMessage()); } catch (IOException e) { m_logger.log(Level.WARNING, "Problem communicating with Bugzilla", e); } catch (Exception e) { m_logger.log(Level.WARNING, "Exception", e); } } private void handleMentionOnPendingChange(IChangelistSummary change, BugzillaBug bug, BugMention bugMention) throws HttpException, IOException { if (bugMention.getReviewNumber() != 0) { m_logger.info(String.format("Change %d bug %d review %d", change.getId(), bug.getBugNumber(), bugMention.getReviewNumber())); String comment = String.format(REVIEW_FMT, m_swarmUrl, bugMention.getReviewNumber()); bug.addComment(comment); } } private void handleMentionOnSubmittedChange(IChangelistSummary change, BugzillaBug bug, BugMention bugMention) throws HttpException, IOException { if (bugMention.isFixed()) { m_logger.info(String.format("Change %d bugfix %d ", change.getId(), bug.getBugNumber())); String comment = String.format("%s bugfix change %d%n%s", change.getUsername(), change.getId(), change.getDescription()); bug.markFixed(comment); } else { m_logger.info(String.format("Change %d bug %d", change.getId(), bug.getBugNumber())); String comment = String.format("%s change %d%n%s", change.getUsername(), change.getId(), change.getDescription()); bug.addComment(comment); } } /** * Parse the bug description and find any references to a bug number. * * @param description * the p4 checking description * @return the bug numbers discovered in the description and an indicator if they are preceded by the "bug" or "bugfix" keyword. */ public Set<BugMention> findBugMentions(String description) { Set<BugMention> result = new TreeSet<BugMention>(); // Look for a review number int reviewNumber = 0; if (REVIEW_MATCHER.reset(description).find()) { reviewNumber = Integer.parseInt(REVIEW_MATCHER.group(1)); } for (Matcher matcher : m_bugNumberMatchers) { matcher.reset(description); while (matcher.find()) { for (int i = 1; i <= matcher.groupCount(); ++i) { String bugNumber = matcher.group(i); if (bugNumber == null || !NUMBER_MATCHER.reset(bugNumber).matches()) { continue; } // Strip any whitespace or commas matched that we don't care about bugNumber = bugNumber.replaceAll("[\\s,]", ""); BugMention bug = new BugMention(Integer.parseInt(bugNumber)); bug.setFixed(matcher.pattern().pattern().contains("fix")); bug.setReviewNumber(reviewNumber); result.add(bug); } } } return result; } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 21161 | Warwick Hunter |
P4 Bugzilla v2.1 - Use the new Bugzilla REST API instead of the old XMLRPC API. - Use the latest P4 Java API. - Build with gradle and gradlew. - Integrate with the latest systemd Linux daemon startup environment found on Fedora 21+ systems. - Simplified the code to focus on just the job at hand. |