package hudson.plugins.cigame; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.TreeSet; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.Action; import hudson.model.BuildListener; import hudson.model.Result; import hudson.model.User; import hudson.model.Cause; import hudson.model.TopLevelItem; import hudson.model.Run; import hudson.model.Hudson; import hudson.plugins.cigame.model.RuleBook; import hudson.plugins.cigame.model.ScoreCard; import hudson.scm.ChangeLogSet; import hudson.scm.ChangeLogSet.Entry; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Notifier; import org.kohsuke.stapler.DataBoundConstructor; public class GamePublisher extends Notifier { @DataBoundConstructor public GamePublisher() { } @Override public GameDescriptor getDescriptor() { return (GameDescriptor) super.getDescriptor(); } @Override public boolean needsToRunAfterFinalized() { return true; } @Override public Action getProjectAction(AbstractProject<?, ?> project) { return null; } @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { perform(build, getDescriptor().getRuleBook(), getDescriptor().getNamesAreCaseSensitive(), listener); return true; } /** * Calculates score from the build and rule book and adds a Game action to the build. * @param build build to calculate points for * @param ruleBook rules used in calculation * @param usernameIsCasesensitive user names in Hudson are case insensitive. * @param listener the build listener * @return true, if any user scores were updated; false, otherwise * @throws IOException thrown if there was a problem setting a user property */ boolean perform(AbstractBuild<?, ?> build, RuleBook ruleBook, boolean usernameIsCasesensitive, BuildListener listener) throws IOException { ScoreCard sc = new ScoreCard(); sc.record(build, ruleBook, listener); ScoreCardAction action = new ScoreCardAction(sc, build); build.getActions().add(action); List<AbstractBuild<?, ?>> accountableBuilds = new ArrayList<AbstractBuild<?,?>>(); accountableBuilds.add(build); AbstractBuild upstreamBuild = getBuildByUpstreamCause(build.getCauses(), listener); if(upstreamBuild!= null) { accountableBuilds.add(upstreamBuild); ChangeLogSet<? extends Entry> changeSet = upstreamBuild.getChangeSet(); if(listener != null ) listener.getLogger().append("[ci-game] UpStream Build ID: " + upstreamBuild.getId()+ "\n"); if(listener != null ) listener.getLogger().append("[ci-game] UpStream Display Name: " + upstreamBuild.getFullDisplayName()+ "\n"); if(listener != null ) listener.getLogger().append("[ci-game] Is UpStream Change Set Empty: " + changeSet.isEmptySet() + "\n"); } // also add all previous aborted builds: AbstractBuild<?, ?> previousBuild = build.getPreviousBuild(); while (previousBuild != null && previousBuild.getResult() == Result.ABORTED) { accountableBuilds.add(previousBuild); previousBuild = previousBuild.getPreviousBuild(); } Set<User> players = new TreeSet<User>(usernameIsCasesensitive ? null : new UsernameCaseinsensitiveComparator()); for (AbstractBuild<?, ?> b : accountableBuilds) { ChangeLogSet<? extends Entry> changeSet = b.getChangeSet(); if (changeSet != null) { for (Entry e : changeSet) { players.add(e.getAuthor()); } } } return updateUserScores(players, sc.getTotalPoints(), accountableBuilds); } private AbstractBuild getBuildByUpstreamCause(List<Cause> causes,BuildListener listener ){ for(Cause cause: (List<Cause>) causes){ if(cause instanceof Cause.UpstreamCause) { TopLevelItem upstreamProject = Hudson.getInstance().getItemByFullName(((Cause.UpstreamCause)cause).getUpstreamProject(), TopLevelItem.class); if(upstreamProject instanceof AbstractProject){ int buildId = ((Cause.UpstreamCause)cause).getUpstreamBuild(); Run run = ((AbstractProject) upstreamProject).getBuildByNumber(buildId); System.out.println(); AbstractBuild upstreamRun = getBuildByUpstreamCause(run.getCauses(),listener); if(upstreamRun == null) { return (AbstractBuild) run; }else{ return upstreamRun; } } } } return null; } /** * Add the score to the users that have committed code in the change set * * * @param score the score that the build was worth * @param accountableBuilds the builds for which the {@code score} is awarded for. * @throws IOException thrown if the property could not be added to the user object. * @return true, if any user scores was updated; false, otherwise */ private boolean updateUserScores(Set<User> players, double score, List<AbstractBuild<?, ?>> accountableBuilds) throws IOException { if (score != 0) { for (User user : players) { UserScoreProperty property = user.getProperty(UserScoreProperty.class); if (property == null) { property = new UserScoreProperty(); user.addProperty(property); } if (property.isParticipatingInGame()) { property.setScore(property.getScore() + score); property.rememberAccountableBuilds(accountableBuilds, score); } user.save(); } } return (!players.isEmpty()); } public static class UsernameCaseinsensitiveComparator implements Comparator<User> { public int compare(User arg0, User arg1) { return arg0.getId().compareToIgnoreCase(arg1.getId()); } } public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.BUILD; } }