diff --git a/core/src/main/java/hudson/FileSystemProvisioner.java b/core/src/main/java/hudson/FileSystemProvisioner.java index 8991a20a41..014892e896 100644 --- a/core/src/main/java/hudson/FileSystemProvisioner.java +++ b/core/src/main/java/hudson/FileSystemProvisioner.java @@ -9,6 +9,7 @@ import hudson.model.Job; import hudson.model.TaskListener; import hudson.model.listeners.RunListener; import hudson.util.DescriptorList; +import hudson.scm.SCM; import java.io.BufferedOutputStream; import java.io.File; @@ -67,8 +68,22 @@ import java.io.OutputStream; * the workspace snapshot of the upstream? Yes, by using {@link RunListener}. * So this becomes like a special SCM type. * - * To support promoted builds, we need an abstraction for permalinks. - * This is also needed for other UI. + *

Design take 2

+ *

+ * The first piece of this is the custom {@link SCM}, which inherits the + * workspace of another job. When this executes, it picks up + * {@link WorkspaceSnapshot} from the other job and use it to obtain the workspace. + * + *

+ * Then there's {@link RunListener}, which creates a snapshot if + * someone else is interested in using a snapshot later. + * + *

TODOs

+ * * * @author Kohsuke Kawaguchi * @since 1.235 diff --git a/core/src/main/java/hudson/PermalinkList.java b/core/src/main/java/hudson/PermalinkList.java new file mode 100644 index 0000000000..88c62f8812 --- /dev/null +++ b/core/src/main/java/hudson/PermalinkList.java @@ -0,0 +1,47 @@ +package hudson; + +import hudson.model.PermalinkProjectAction.Permalink; +import hudson.util.EditDistance; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * {@link List} of {@link Permalink}s with some convenience methods. + * + * @author Kohsuke Kawaguchi + * @since 1.255 + */ +public final class PermalinkList extends ArrayList { + public PermalinkList(Collection c) { + super(c); + } + + public PermalinkList() { + } + + /** + * Gets the {@link Permalink} by its {@link Permalink#getId() id}. + * + * @return null if not found + */ + public Permalink get(String id) { + for (Permalink p : this) + if(p.getId().equals(id)) + return p; + return null; + } + + /** + * Finds the closest name match by its ID. + */ + public Permalink findNearest(String id) { + List ids = new ArrayList(); + for (Permalink p : this) + ids.add(p.getId()); + String nearest = EditDistance.findNearest(id, ids); + if(nearest==null) return null; + return get(nearest); + } +} diff --git a/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java new file mode 100644 index 0000000000..6fea95f77e --- /dev/null +++ b/core/src/main/java/hudson/fsp/WorkspaceSnapshotSCM.java @@ -0,0 +1,127 @@ +package hudson.fsp; + +import hudson.scm.SCM; +import hudson.scm.ChangeLogParser; +import hudson.scm.SCMDescriptor; +import hudson.model.AbstractProject; +import hudson.model.TaskListener; +import hudson.model.AbstractBuild; +import hudson.model.BuildListener; +import hudson.model.Hudson; +import hudson.model.Result; +import hudson.model.PermalinkProjectAction.Permalink; +import hudson.Launcher; +import hudson.FilePath; +import hudson.WorkspaceSnapshot; +import hudson.PermalinkList; + +import java.io.IOException; +import java.io.File; + +import org.kohsuke.stapler.DataBoundConstructor; + +/** + * {@link SCM} that inherits the workspace from another build through {@link WorkspaceSnapshot} + * + * @author Kohsuke Kawaguchi + */ +public class WorkspaceSnapshotSCM extends SCM { + /** + * The job name from which we inherit the workspace. + */ + public String jobName; + /** + * The permalink name indicating the build from which to inherit the workspace. + */ + public String permalink; + + @DataBoundConstructor + public WorkspaceSnapshotSCM(String jobName, String permalink) { + this.jobName = jobName; + this.permalink = permalink; + } + + /** + * {@link Exception} indicating that the resolution of the job/permalink failed. + */ + private final class ResolvedFailedException extends Exception { + private ResolvedFailedException(String message) { + super(message); + } + } + + private static class Snapshot { + final WorkspaceSnapshot snapshot; + final AbstractBuild owner; + private Snapshot(WorkspaceSnapshot snapshot, AbstractBuild owner) { + this.snapshot = snapshot; + this.owner = owner; + } + + void restoreTo(FilePath dst,TaskListener listener) throws IOException, InterruptedException { + snapshot.restoreTo(owner,dst,listener); + } + } + /** + * Obtains the {@link WorkspaceSnapshot} object that this {@link SCM} points to, + * or throws {@link ResolvedFailedException} upon failing. + * + * @return never null. + */ + public Snapshot resolve() throws ResolvedFailedException { + Hudson h = Hudson.getInstance(); + AbstractProject job = h.getItemByFullName(jobName, AbstractProject.class); + if(job==null) { + if(h.getItemByFullName(jobName)==null) { + AbstractProject nearest = AbstractProject.findNearest(jobName); + throw new ResolvedFailedException(Messages.WorkspaceSnapshotSCM_NoSuchJob(jobName,nearest.getFullName())); + } else + throw new ResolvedFailedException(Messages.WorkspaceSnapshotSCM_IncorrectJobType(jobName)); + } + + PermalinkList permalinks = job.getPermalinks(); + Permalink p = permalinks.get(permalink); + if(p==null) + throw new ResolvedFailedException(Messages.WorkspaceSnapshotSCM_NoSuchPermalink(permalink,jobName)); + + AbstractBuild b = (AbstractBuild)p.resolve(job); + if(b==null) + throw new ResolvedFailedException(Messages.WorkspaceSnapshotSCM_NoBuild(permalink,jobName)); + + WorkspaceSnapshot snapshot = b.getAction(WorkspaceSnapshot.class); + if(snapshot==null) + throw new ResolvedFailedException(Messages.WorkspaceSnapshotSCM_NoWorkspace(jobName,permalink)); + + return new Snapshot(snapshot,b); + } + + public boolean pollChanges(AbstractProject project, Launcher launcher, FilePath workspace, TaskListener listener) throws IOException, InterruptedException { + AbstractBuild lastBuild = (AbstractBuild) project.getLastBuild(); + if (lastBuild == null) { + listener.getLogger().println("No existing build. Starting a new one"); + return true; + } + + + return false; + } + + public boolean checkout(AbstractBuild build, Launcher launcher, FilePath workspace, BuildListener listener, File changelogFile) throws IOException, InterruptedException { + try { + resolve().restoreTo(workspace,listener); + return true; + } catch (ResolvedFailedException e) { + listener.error(e.getMessage()); // stack trace is meaningless + build.setResult(Result.FAILURE); + return false; + } + } + + public ChangeLogParser createChangeLogParser() { + return null; + } + + public SCMDescriptor getDescriptor() { + return null; + } +} diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index 63c5e46699..1040ce9c24 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -4,6 +4,7 @@ import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; import hudson.ExtensionPoint; import hudson.Util; import hudson.XmlFile; +import hudson.PermalinkList; import hudson.model.Descriptor.FormException; import hudson.model.listeners.ItemListener; import hudson.model.PermalinkProjectAction.Permalink; @@ -557,7 +558,7 @@ public abstract class Job, RunT extends Run, RunT extends Run getPermalinks() { + public PermalinkList getPermalinks() { // TODO: shall we cache this? - List permalinks = new ArrayList(Permalink.BUILTIN); + PermalinkList permalinks = new PermalinkList(Permalink.BUILTIN); for (Action a : getActions()) { if (a instanceof PermalinkProjectAction) { PermalinkProjectAction ppa = (PermalinkProjectAction) a; diff --git a/core/src/main/java/hudson/model/PermalinkProjectAction.java b/core/src/main/java/hudson/model/PermalinkProjectAction.java index fa8ddc8199..907dd86e20 100644 --- a/core/src/main/java/hudson/model/PermalinkProjectAction.java +++ b/core/src/main/java/hudson/model/PermalinkProjectAction.java @@ -34,10 +34,17 @@ public interface PermalinkProjectAction extends Action { public abstract String getDisplayName(); /** - * The URL token to represent the permalink, such as "lastSuccessfulBuild". + * ID that uniquely identifies this permalink. + * + *

+ * The is also used as an URL token to represent the permalink. * This becomes the part of the permanent URL. + * + *

+ * The expected format is the camel case, + * such as "lastSuccessfulBuild". */ - public abstract String getUrl(); + public abstract String getId(); /** * Resolves the permalink to a build. @@ -58,7 +65,7 @@ public interface PermalinkProjectAction extends Action { return Messages.Permalink_LastBuild(); } - public String getUrl() { + public String getId() { return "lastBuild"; } @@ -72,7 +79,7 @@ public interface PermalinkProjectAction extends Action { return Messages.Permalink_LastStableBuild(); } - public String getUrl() { + public String getId() { return "lastStableBuild"; } @@ -86,7 +93,7 @@ public interface PermalinkProjectAction extends Action { return Messages.Permalink_LastSuccessfulBuild(); } - public String getUrl() { + public String getId() { return "lastSuccessfulBuild"; } @@ -100,7 +107,7 @@ public interface PermalinkProjectAction extends Action { return Messages.Permalink_LastFailedBuild(); } - public String getUrl() { + public String getId() { return "lastFailedBuild"; } diff --git a/core/src/main/resources/hudson/fsp/Messages.properties b/core/src/main/resources/hudson/fsp/Messages.properties new file mode 100644 index 0000000000..6f3490388d --- /dev/null +++ b/core/src/main/resources/hudson/fsp/Messages.properties @@ -0,0 +1,8 @@ +WorkspaceSnapshotSCM.NoSuchJob=No such job ''{0}'' exists. Perhaps you meant ''{1}''? +WorkspaceSnapshotSCM.IncorrectJobType={0} isn''t a job that has a workspace. +WorkspaceSnapshotSCM.NoBuild=There''s no qualifying build for the {0} permalink in {1} +WorkspaceSnapshotSCM.NoSuchPermalink=No such permalink ''{0}'' exists for {1} +WorkspaceSnapshotSCM.NoWorkspace=\ + {0} {1} doesn''t have a workspace snapshot attached,n\ + probably because when the build was done, no other jobs needed its workspace snapshot.\n\ + Please run another build in {0} to get the workspace snapshot taken. \ No newline at end of file diff --git a/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link.jelly b/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link.jelly index 61df88e9ff..87b7b28168 100644 --- a/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link.jelly +++ b/core/src/main/resources/hudson/model/PermalinkProjectAction/Permalink/link.jelly @@ -7,7 +7,7 @@

  • - ${%format(it.displayName,b.number,b.timestampString)} + ${%format(it.displayName,b.number,b.timestampString)}
  • \ No newline at end of file