package hudson.plugins.jira; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashSet; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import hudson.Extension; import hudson.MarkupText; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.scm.ChangeLogAnnotator; import hudson.scm.ChangeLogSet.Entry; /** * {@link ChangeLogAnnotator} that picks up JIRA issue IDs. * @author Kohsuke Kawaguchi */ @Extension public class JiraChangeLogAnnotator extends ChangeLogAnnotator { private static final Logger LOGGER = Logger.getLogger(JiraChangeLogAnnotator.class.getName()); @Override public void annotate(AbstractBuild<?,?> build, Entry change, MarkupText text) { JiraSite site = getSiteForProject(build.getProject()); if(site==null) return; // not configured with JIRA // if there's any recorded detail information, try to use that, too. JiraBuildAction a = build.getAction(JiraBuildAction.class); Set<JiraIssue> issuesToBeSaved = new HashSet<JiraIssue>(); Pattern pattern = site.getIssuePattern(); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Using issue pattern: " + pattern); } String plainText = text.getText(); Matcher m = pattern.matcher(plainText); while (m.find()) { if (m.groupCount() >= 1) { String id = m.group(1); LOGGER.info("Annotating JIRA id: '" + id + "'"); if(!site.existsIssue(id)) { continue; } URL url; try { url = site.getUrl(id); } catch (MalformedURLException e) { throw new AssertionError(e); // impossible } JiraIssue issue = null; if (a != null) { issue = a.getIssue(id); } if (issue == null) { try { issue = site.getIssue(id); if (issue != null) { issuesToBeSaved.add(issue); } } catch (Exception e) { LOGGER.log(Level.WARNING, "Error getting remote issue " + id, e); } } if(issue==null) { text.addMarkup(m.start(1), m.end(1), "<a href='"+url+"'>", "</a>"); } else { text.addMarkup(m.start(1), m.end(1), String.format("<a href='%s' tooltip='%s'>",url, Util.escape(issue.title)), "</a>"); } } else { LOGGER.log(Level.WARNING, "The JIRA pattern " + pattern + " doesn't define a capturing group!"); } } if (!issuesToBeSaved.isEmpty()) { saveIssues(build, a, issuesToBeSaved); } } private void saveIssues(AbstractBuild<?, ?> build, JiraBuildAction a, Set<JiraIssue> issuesToBeSaved) { if (a != null) { a.addIssues(issuesToBeSaved); } else { JiraBuildAction action = new JiraBuildAction(build, issuesToBeSaved); build.addAction(action); } try { build.save(); } catch (final IOException e) { LOGGER.log(Level.WARNING, "Error saving updated build", e); } } JiraSite getSiteForProject(AbstractProject<?, ?> project) { return JiraSite.get(project); } }