/*
* The MIT License
*
* Copyright 2013 Kathi Stutz.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.plugins.jobConfigHistory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import hudson.Extension;
import hudson.model.BuildBadgeAction;
import hudson.model.Job;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.model.listeners.RunListener;
import jenkins.model.Jenkins;
import jenkins.model.RunAction2;
/**
* This class adds a badge to the build history marking builds that occurred
* after the configuration was changed.
*
* @author Kathi Stutz
*/
public class JobConfigBadgeAction implements BuildBadgeAction, RunAction2 {
/**
* The dates of the last two config changes as Strings.
*/
private String[] configDates;
/**
* We need the build in order to get the project name.
*/
private transient Run<?, ?> build;
/**
* Creates a new JobConfigBadgeAction.
*
* @param configDates
* The dates of the last two config changes
*/
JobConfigBadgeAction(String[] configDates) {
this.configDates = configDates.clone();
this.build = null;
}
@Override
public void onAttached(Run<?, ?> r) {
build = r;
}
@Override
public void onLoad(Run<?, ?> r) {
build = r;
}
/**
* Listener.
*/
@Extension
public static class Listener extends RunListener<Run<?, ?>> {
@Override
public void onStarted(Run<?, ?> build, TaskListener listener) {
final Job<?, ?> project = build.getParent();
if (project.getNextBuildNumber() <= 2) {
super.onStarted(build, listener);
return;
}
Date lastBuildDate = null;
final Run<?, ?> lastBuild = project.getLastBuild();
if (lastBuild != null && lastBuild.getPreviousBuild() != null) {
lastBuildDate = lastBuild.getPreviousBuild().getTime();
}
final List<HistoryDescr> historyDescriptions = getRevisions(
project);
if (historyDescriptions.size() > 1) {
Collections.sort(historyDescriptions,
ParsedDateComparator.DESCENDING);
final HistoryDescr lastChange = Collections.min(
historyDescriptions, ParsedDateComparator.DESCENDING);
final Date lastConfigChange = lastChange.parsedDate();
if (lastBuildDate != null
&& lastConfigChange.after(lastBuildDate)) {
final String[] dates = {lastChange.getTimestamp(),
findLastRelevantConfigChangeDate(
historyDescriptions, lastBuildDate),};
build.addAction(new JobConfigBadgeAction(dates));
}
}
super.onStarted(build, listener);
}
/**
* For tests.
*
* @param project
* to inspect.
* @return list of revisions
*/
List<HistoryDescr> getRevisions(final Job<?, ?> project) {
final HistoryDao historyDao = PluginUtils.getHistoryDao();
final ArrayList<HistoryDescr> historyDescriptions = new ArrayList<HistoryDescr>(
historyDao.getRevisions(project.getConfigFile()).values());
return historyDescriptions;
}
/**
* Finds the date of the last config change that happened before the
* last build. This is needed for the link in the build history that
* shows the difference between the current configuration and the
* version that was in place when the last build happened.
*
* @param historyDescriptions
* An ArrayList full of HistoryDescr.
* @param lastBuildDate
* The date of the lastBuild (as Date).
* @return The date of the last relevant config change (as String).
*/
private String findLastRelevantConfigChangeDate(
List<HistoryDescr> historyDescriptions, Date lastBuildDate) {
for (HistoryDescr oldConfigChange : historyDescriptions.subList(1,
historyDescriptions.size())) {
final Date changeDate = oldConfigChange.parsedDate();
if (changeDate != null && changeDate.before(lastBuildDate)) {
return oldConfigChange.getTimestamp();
}
}
return historyDescriptions.get(1).getTimestamp();
}
} // end Listener
/**
* Returns true if the config change build badges should appear (depending
* on plugin settings and user permissions). Called from badge.jelly.
*
* @return True if badges should appear.
*/
public boolean showBadge() {
return getPlugin().showBuildBadges(build.getParent());
}
/**
* Check if the config history files that are attached to the build still
* exist.
*
* @return True if both files exist.
*/
public boolean oldConfigsExist() {
final HistoryDao historyDao = getHistoryDao();
final Job<?, ?> project = build.getParent();
for (String timestamp : configDates) {
if (!historyDao.hasOldRevision(project.getConfigFile(),
timestamp)) {
return false;
}
}
return true;
}
/**
* Creates the target for the link to the showDiffFiles page.
*
* @return Link target as String.
*/
public String createLink() {
return getRootUrl() + build.getParent().getUrl()
+ JobConfigHistoryConsts.URLNAME + "/showDiffFiles?timestamp1="
+ configDates[1] + "×tamp2=" + configDates[0];
}
/**
* Returns the root URL.
*
* @return root-URL of Jenkins.
*/
String getRootUrl() {
return Jenkins.getInstance().getRootUrlFromRequest();
}
/**
* Returns tooltip so users know what our nice little icon stands for.
*
* @return Explanatory text as string
*/
public String getTooltip() {
return Messages.JobConfigBadgeAction_ToolTip();
}
/**
* Returns the path to our nice little icon.
*
* @return Icon path as string
*/
public String getIcon() {
return "/plugin/jobConfigHistory/img/buildbadge.png";
}
/**
* Non-use interface method. {@inheritDoc}
*/
public String getIconFileName() {
return null;
}
/**
* Non-use interface method. {@inheritDoc}
*/
public String getDisplayName() {
return null;
}
/**
* Non-use interface method. {@inheritDoc}
*/
public String getUrlName() {
return "";
}
/**
* Returns the plugin for tests.
*
* @return plugin
*/
JobConfigHistory getPlugin() {
return PluginUtils.getPlugin();
}
/**
* For tests.
*
* @return listener
*/
HistoryDao getHistoryDao() {
return PluginUtils.getHistoryDao();
}
}