{
/**
* Set if we want the blame information to flow from upstream to downstream build.
@@ -321,80 +319,38 @@ public FilePath[] getModuleRoots() {
return getParent().getScm().getModuleRoots(ws, this);
}
- /**
- * List of users who committed a change since the last non-broken build till now.
- *
- *
- * This list at least always include people who made changes in this build, but
- * if the previous build was a failure it also includes the culprit list from there.
- *
- * @return
- * can be empty but never null.
- */
- @Exported
- public Set getCulprits() {
- if (culprits==null) {
- Set r = new HashSet();
- R p = getPreviousCompletedBuild();
- if (p !=null && isBuilding()) {
- Result pr = p.getResult();
- if (pr!=null && pr.isWorseThan(Result.SUCCESS)) {
- // we are still building, so this is just the current latest information,
- // but we seems to be failing so far, so inherit culprits from the previous build.
- // isBuilding() check is to avoid recursion when loading data from old Hudson, which doesn't record
- // this information
- r.addAll(p.getCulprits());
- }
- }
- for (Entry e : getChangeSet())
- r.add(e.getAuthor());
+ @Override
+ @CheckForNull public Set getCulpritIds() {
+ return culprits;
+ }
- if (upstreamCulprits) {
- // If we have dependencies since the last successful build, add their authors to our list
- if (getPreviousNotFailedBuild() != null) {
- Map depmap = getDependencyChanges(getPreviousSuccessfulBuild());
- for (DependencyChange dep : depmap.values()) {
- for (AbstractBuild,?> b : dep.getBuilds()) {
- for (Entry entry : b.getChangeSet()) {
- r.add(entry.getAuthor());
- }
+ @Override
+ public boolean shouldCalculateCulprits() {
+ return getCulpritIds() == null;
+ }
+
+ @Override
+ @Nonnull
+ public Set calculateCulprits() {
+ Set c = RunWithSCM.super.calculateCulprits();
+
+ AbstractBuild
p = getPreviousCompletedBuild();
+ if (upstreamCulprits) {
+ // If we have dependencies since the last successful build, add their authors to our list
+ if (p.getPreviousNotFailedBuild() != null) {
+ Map depmap =
+ p.getDependencyChanges(p.getPreviousSuccessfulBuild());
+ for (AbstractBuild.DependencyChange dep : depmap.values()) {
+ for (AbstractBuild, ?> b : dep.getBuilds()) {
+ for (ChangeLogSet.Entry entry : b.getChangeSet()) {
+ c.add(entry.getAuthor());
}
}
}
}
-
- return r;
}
- return new AbstractSet() {
- public Iterator iterator() {
- return new AdaptedIterator(culprits.iterator()) {
- protected User adapt(String id) {
- return User.get(id);
- }
- };
- }
-
- public int size() {
- return culprits.size();
- }
- };
- }
-
- /**
- * Returns true if this user has made a commit to this build.
- *
- * @since 1.191
- */
- public boolean hasParticipant(User user) {
- for (ChangeLogSet.Entry e : getChangeSet())
- try{
- if (e.getAuthor()==user)
- return true;
- } catch (RuntimeException re) {
- LOGGER.log(Level.INFO, "Failed to determine author of changelog " + e.getCommitId() + "for " + getParent().getDisplayName() + ", " + getDisplayName(), re);
- }
- return false;
+ return c;
}
/**
@@ -863,7 +819,7 @@ public Collection getBuildFingerprints() {
* @return never null.
*/
@Exported
- public ChangeLogSet extends Entry> getChangeSet() {
+ @Nonnull public ChangeLogSet extends Entry> getChangeSet() {
synchronized (changeSetLock) {
if (scm==null) {
scm = NullChangeLogParser.INSTANCE;
@@ -887,10 +843,10 @@ public ChangeLogSet extends Entry> getChangeSet() {
return cs;
}
- @Restricted(DoNotUse.class) // for project-changes.jelly
- public List> getChangeSets() {
+ @Override
+ @Nonnull public List> getChangeSets() {
ChangeLogSet extends Entry> cs = getChangeSet();
- return cs.isEmptySet() ? Collections.>emptyList() : Collections.>singletonList(cs);
+ return cs.isEmptySet() ? Collections.emptyList() : Collections.singletonList(cs);
}
/**
diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
index 995b584cb8b6..7e003bfca930 100644
--- a/core/src/main/java/hudson/model/AbstractProject.java
+++ b/core/src/main/java/hudson/model/AbstractProject.java
@@ -34,7 +34,6 @@
import hudson.EnvVars;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
-import hudson.FeedAdapter;
import hudson.FilePath;
import hudson.Functions;
import hudson.Launcher;
@@ -55,8 +54,6 @@
import hudson.model.queue.QueueTaskFuture;
import hudson.model.queue.SubTask;
import hudson.model.queue.SubTaskContributor;
-import hudson.scm.ChangeLogSet;
-import hudson.scm.ChangeLogSet.Entry;
import hudson.scm.NullSCM;
import hudson.scm.PollingResult;
@@ -87,7 +84,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -107,7 +103,6 @@
import javax.servlet.ServletException;
import jenkins.model.BlockedBecauseOfBuildInProgress;
import jenkins.model.Jenkins;
-import jenkins.model.JenkinsLocationConfiguration;
import jenkins.model.ParameterizedJobMixIn;
import jenkins.model.Uptime;
import jenkins.model.lazy.LazyBuildMixIn;
@@ -1974,66 +1969,6 @@ public HttpResponse doEnable() throws IOException, ServletException {
}
- /**
- * RSS feed for changes in this project.
- */
- public void doRssChangelog( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
- class FeedItem {
- ChangeLogSet.Entry e;
- int idx;
-
- public FeedItem(Entry e, int idx) {
- this.e = e;
- this.idx = idx;
- }
-
- AbstractBuild,?> getBuild() {
- return e.getParent().build;
- }
- }
-
- List entries = new ArrayList();
-
- for(R r=getLastBuild(); r!=null; r=r.getPreviousBuild()) {
- int idx=0;
- for( ChangeLogSet.Entry e : r.getChangeSet())
- entries.add(new FeedItem(e,idx++));
- }
-
- RSS.forwardToRss(
- getDisplayName()+' '+getScm().getDescriptor().getDisplayName()+" changes",
- getUrl()+"changes",
- entries, new FeedAdapter() {
- public String getEntryTitle(FeedItem item) {
- return "#"+item.getBuild().number+' '+item.e.getMsg()+" ("+item.e.getAuthor()+")";
- }
-
- public String getEntryUrl(FeedItem item) {
- return item.getBuild().getUrl()+"changes#detail"+item.idx;
- }
-
- public String getEntryID(FeedItem item) {
- return getEntryUrl(item);
- }
-
- public String getEntryDescription(FeedItem item) {
- StringBuilder buf = new StringBuilder();
- for(String path : item.e.getAffectedPaths())
- buf.append(path).append('\n');
- return buf.toString();
- }
-
- public Calendar getEntryTimestamp(FeedItem item) {
- return item.getBuild().getTimestamp();
- }
-
- public String getEntryAuthor(FeedItem entry) {
- return JenkinsLocationConfiguration.get().getAdminAddress();
- }
- },
- req, rsp );
- }
-
/**
* {@link AbstractProject} subtypes should implement this base class as a descriptor.
*
diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java
index c41ff5596698..0575724d9971 100644
--- a/core/src/main/java/hudson/model/Job.java
+++ b/core/src/main/java/hudson/model/Job.java
@@ -28,6 +28,7 @@
import hudson.EnvVars;
import hudson.Extension;
import hudson.ExtensionPoint;
+import hudson.FeedAdapter;
import hudson.PermalinkList;
import hudson.Util;
import hudson.cli.declarative.CLIResolver;
@@ -36,6 +37,8 @@
import hudson.model.Fingerprint.RangeSet;
import hudson.model.PermalinkProjectAction.Permalink;
import hudson.model.listeners.ItemListener;
+import hudson.scm.ChangeLogSet;
+import hudson.scm.SCM;
import hudson.search.QuickSilver;
import hudson.search.SearchIndex;
import hudson.search.SearchIndexBuilder;
@@ -83,12 +86,14 @@
import jenkins.model.BuildDiscarderProperty;
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
import jenkins.model.Jenkins;
+import jenkins.model.JenkinsLocationConfiguration;
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.ProjectNamingStrategy;
import jenkins.model.RunIdMigrator;
import jenkins.model.lazy.LazyBuildMixIn;
+import jenkins.scm.RunWithSCM;
import jenkins.security.HexStringConfidentialKey;
-import jenkins.util.io.OnMaster;
+import jenkins.triggers.SCMTriggerItem;
import net.sf.json.JSONException;
import net.sf.json.JSONObject;
import org.apache.commons.io.FileUtils;
@@ -1042,7 +1047,84 @@ public PermalinkList getPermalinks() {
}
return permalinks;
}
-
+
+ /**
+ * RSS feed for changes in this project.
+ *
+ * @since TODO
+ */
+ public void doRssChangelog(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
+ class FeedItem {
+ ChangeLogSet.Entry e;
+ int idx;
+
+ public FeedItem(ChangeLogSet.Entry e, int idx) {
+ this.e = e;
+ this.idx = idx;
+ }
+
+ Run, ?> getBuild() {
+ return e.getParent().build;
+ }
+ }
+
+ List entries = new ArrayList();
+ String scmDisplayName = "";
+ if (this instanceof SCMTriggerItem) {
+ SCMTriggerItem scmItem = (SCMTriggerItem) this;
+ List scmNames = new ArrayList<>();
+ for (SCM s : scmItem.getSCMs()) {
+ scmNames.add(s.getDescriptor().getDisplayName());
+ }
+ scmDisplayName = " " + Util.join(scmNames, ", ");
+ }
+
+ for (RunT r = getLastBuild(); r != null; r = r.getPreviousBuild()) {
+ int idx = 0;
+ if (r instanceof RunWithSCM) {
+ for (ChangeLogSet extends ChangeLogSet.Entry> c : ((RunWithSCM,?>) r).getChangeSets()) {
+ for (ChangeLogSet.Entry e : c) {
+ entries.add(new FeedItem(e, idx++));
+ }
+ }
+ }
+ }
+ RSS.forwardToRss(
+ getDisplayName() + scmDisplayName + " changes",
+ getUrl() + "changes",
+ entries, new FeedAdapter() {
+ public String getEntryTitle(FeedItem item) {
+ return "#" + item.getBuild().number + ' ' + item.e.getMsg() + " (" + item.e.getAuthor() + ")";
+ }
+
+ public String getEntryUrl(FeedItem item) {
+ return item.getBuild().getUrl() + "changes#detail" + item.idx;
+ }
+
+ public String getEntryID(FeedItem item) {
+ return getEntryUrl(item);
+ }
+
+ public String getEntryDescription(FeedItem item) {
+ StringBuilder buf = new StringBuilder();
+ for (String path : item.e.getAffectedPaths())
+ buf.append(path).append('\n');
+ return buf.toString();
+ }
+
+ public Calendar getEntryTimestamp(FeedItem item) {
+ return item.getBuild().getTimestamp();
+ }
+
+ public String getEntryAuthor(FeedItem entry) {
+ return JenkinsLocationConfiguration.get().getAdminAddress();
+ }
+ },
+ req, rsp);
+ }
+
+
+
@Override public ContextMenu doChildrenContextMenu(StaplerRequest request, StaplerResponse response) throws Exception {
// not sure what would be really useful here. This needs more thoughts.
// for the time being, I'm starting with permalinks
diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java
index 4be622f2a34d..db669cdb2a5d 100644
--- a/core/src/main/java/hudson/model/View.java
+++ b/core/src/main/java/hudson/model/View.java
@@ -59,9 +59,11 @@
import javax.annotation.Nonnull;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithChildren;
+import jenkins.model.ModelObjectWithContextMenu;
import jenkins.model.item_category.Categories;
import jenkins.model.item_category.Category;
import jenkins.model.item_category.ItemCategory;
+import jenkins.scm.RunWithSCM;
import jenkins.util.ProgressiveRendering;
import jenkins.util.xml.XMLUtils;
@@ -114,7 +116,8 @@
import java.util.logging.Logger;
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
-import static jenkins.model.Jenkins.*;
+import static jenkins.scm.RunWithSCM.*;
+
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.QueryParameter;
@@ -249,7 +252,7 @@ public String getViewName() {
*/
public void rename(String newName) throws Failure, FormException {
if(name.equals(newName)) return; // noop
- checkGoodName(newName);
+ Jenkins.checkGoodName(newName);
if(owner.getView(newName)!=null)
throw new FormException(Messages.Hudson_ViewAlreadyExists(newName),"name");
String oldName = name;
@@ -461,7 +464,7 @@ public List getComputers() {
private boolean isRelevant(Collection