package hudson.plugins.release.dashboard; import hudson.Extension; import hudson.FeedAdapter; import hudson.model.Cause; import hudson.model.Descriptor; import hudson.model.Hudson; import hudson.model.Job; import hudson.model.RSS; import hudson.model.Run; import hudson.model.User; import hudson.model.Cause.UserCause; import hudson.plugins.release.ReleaseWrapper.ReleaseBuildBadgeAction; import hudson.plugins.view.dashboard.DashboardPortlet; import hudson.tasks.Mailer; import hudson.util.RunList; import java.io.IOException; import java.util.Calendar; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import javax.servlet.ServletException; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; public class RecentReleasesPortlet extends DashboardPortlet { @DataBoundConstructor public RecentReleasesPortlet(String name) { super(name); } public Collection<Run> getRecentReleases(int max) { LinkedList<Run> recentReleases = new LinkedList<Run>(); for (Job job : getDashboard().getJobs()) { // Keep going through jobs while there are more builds // and (the recent releases is less than max or this run // is newer than the last in the list) for (Run run = job.getLastCompletedBuild(); run != null && (recentReleases.size() < max || run.getTimestamp().compareTo(recentReleases.getLast().getTimestamp()) > 0); run = run.getPreviousBuild()) { ReleaseBuildBadgeAction rbb = run.getAction(ReleaseBuildBadgeAction.class); if (rbb != null) { // if you couldn't insert run, stop trying this job as next runs // are even older if (!insertRun(run, recentReleases, max)) { break; } } } } return recentReleases; } /** * Get the release version from this run * @param run Must be a release run - i.e. have a ReleaseBuildBadgeAction * @return */ public String getReleaseVersion(Run run) { ReleaseBuildBadgeAction rbb = run.getAction(ReleaseBuildBadgeAction.class); return rbb.getReleaseVersion(); } private boolean insertRun(Run run, LinkedList<Run> recentReleases, int max) { ListIterator<Run> iter = recentReleases.listIterator(); Run recentRun = null; do { if (iter.hasNext()) { recentRun = iter.next(); } else { recentRun = null; } // if we're at the end of the recent releases list and the list has room for another // or this run is more recent than current position, then insert if ((recentRun == null && recentReleases.size() < max) || (recentRun != null && run.getTimestamp().compareTo(recentRun.getTimestamp()) > 0)) { // back up one and add to list if (!recentReleases.isEmpty() && recentRun != null) { iter.previous(); } iter.add(run); // remove last on list if size == max - might be removing what we just added if (recentReleases.size() > max) { recentReleases.removeLast(); } return true; } } while (recentRun != null); return false; } public void doRssAll( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { rss(req, rsp, " all builds", RunList.fromRuns(getRecentReleases(20))); } public void doRssFailed( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException { rss(req, rsp, " failed builds", RunList.fromRuns(getRecentReleases(20)).failureOnly()); } private void rss(StaplerRequest req, StaplerResponse rsp, String suffix, RunList runs) throws IOException, ServletException { RSS.forwardToRss(getDisplayName()+ suffix, getDashboard().getUrl() + getUrl(), runs.newBuilds(), new RelativePathFeedAdapter(getDashboard().getUrl() + getUrl()), req, rsp ); } public static class DescriptorImpl extends Descriptor<DashboardPortlet> { @Extension public static DescriptorImpl newInstance() { if (Hudson.getInstance().getPlugin("dashboard-view") != null) { return new DescriptorImpl(); } else { return null; } } @Override public String getDisplayName() { return "Recent Releases"; } } private class RelativePathFeedAdapter implements FeedAdapter<Run> { private String url; RelativePathFeedAdapter(String url) { this.url = url; } public String getEntryTitle(Run entry) { return entry+" ("+entry.getResult()+")"; } public String getEntryUrl(Run entry) { return url + entry.getUrl(); } public String getEntryID(Run entry) { return "tag:" + "hudson.dev.java.net," + entry.getTimestamp().get(Calendar.YEAR) + ":" + entry.getParent().getName()+':'+entry.getId(); } public String getEntryDescription(Run entry) { return getReleaseVersion(entry); } public Calendar getEntryTimestamp(Run entry) { return entry.getTimestamp(); } public String getEntryAuthor(Run entry) { // release builds are manual so get the UserCause // and report rss entry as user who kicked off build List<Cause> causes = entry.getCauses(); for (Cause cause : causes) { if (cause instanceof UserCause) { return User.get(((UserCause) cause).getUserName()).getFullName(); } } // in the unexpected case where there is no user cause, return admin return Mailer.descriptor().getAdminAddress(); } } }