mirror of
https://github.com/jenkinsci/jenkins.git
synced 2026-11-06 00:25:58 +00:00
implemented SCM repository browser support.
git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@2566 71c3de6d-444a-0410-be80-ed276b4c234a
This commit is contained in:
1 parent
a4a9be1129
commit
705ce39156
25 files changed
+494
-82
No files matched your search
@@ -23,6 +23,7 @@ import hudson.remoting.VirtualChannel;
|
||||
import hudson.scm.CVSSCM;
|
||||
import hudson.scm.SCM;
|
||||
import hudson.scm.SCMS;
|
||||
import hudson.scm.SCMDescriptor;
|
||||
import hudson.tasks.BuildStep;
|
||||
import hudson.tasks.BuildWrapper;
|
||||
import hudson.tasks.BuildWrappers;
|
||||
@@ -921,7 +922,7 @@ public final class Hudson extends View implements ItemGroup<TopLevelItem>, Node
|
||||
for( Descriptor<BuildWrapper> d : BuildWrappers.WRAPPERS )
|
||||
result &= d.configure(req);
|
||||
|
||||
for( Descriptor<SCM> scmd : SCMS.SCMS )
|
||||
for( SCMDescriptor scmd : SCMS.SCMS )
|
||||
result &= scmd.configure(req);
|
||||
|
||||
for( TriggerDescriptor d : Triggers.TRIGGERS )
|
||||
|
||||
@@ -443,34 +443,31 @@ public abstract class Job<JobT extends Job<JobT,RunT>, RunT extends Run<JobT,Run
|
||||
/**
|
||||
* Accepts submission from the configuration page.
|
||||
*/
|
||||
public synchronized void doConfigSubmit( StaplerRequest req, StaplerResponse rsp ) throws IOException, ServletException {
|
||||
if(!Hudson.adminCheck(req,rsp))
|
||||
public synchronized void doConfigSubmit(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
|
||||
if (!Hudson.adminCheck(req, rsp))
|
||||
return;
|
||||
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
|
||||
description = req.getParameter("description");
|
||||
|
||||
if(req.getParameter("logrotate")!=null)
|
||||
if (req.getParameter("logrotate") != null)
|
||||
logRotator = LogRotator.DESCRIPTOR.newInstance(req);
|
||||
else
|
||||
logRotator = null;
|
||||
|
||||
keepDependencies = req.getParameter("keepDependencies")!=null;
|
||||
keepDependencies = req.getParameter("keepDependencies") != null;
|
||||
|
||||
try {
|
||||
properties.clear();
|
||||
for (JobPropertyDescriptor d : JobPropertyDescriptor.getPropertyDescriptors(getClass())) {
|
||||
JobProperty prop = d.newInstance(req);
|
||||
if(prop!=null)
|
||||
properties.add(prop);
|
||||
try {
|
||||
properties.clear();
|
||||
for (JobPropertyDescriptor d : JobPropertyDescriptor.getPropertyDescriptors(getClass())) {
|
||||
JobProperty prop = d.newInstance(req);
|
||||
if (prop != null)
|
||||
properties.add(prop);
|
||||
}
|
||||
} catch (FormException e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
} catch (FormException e) {
|
||||
throw new ServletException(e);
|
||||
}
|
||||
|
||||
|
||||
save();
|
||||
|
||||
String newName = req.getParameter("name");
|
||||
if(newName!=null && !newName.equals(name)) {
|
||||
|
||||
@@ -118,6 +118,7 @@ public abstract class ViewJob<JobT extends ViewJob<JobT,RunT>, RunT extends Run<
|
||||
super.doConfigSubmit(req,rsp);
|
||||
// make sure to reload to reflect this config change.
|
||||
nextUpdate = 0;
|
||||
save();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -185,10 +185,23 @@ public final class CVSChangeLogSet extends ChangeLogSet<CVSChangeLog> {
|
||||
private String prevrevision;
|
||||
private boolean dead;
|
||||
|
||||
/**
|
||||
* Gets the full file name in the CVS repository, like
|
||||
* "/foo/bar/zot.c"
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets just the last component of the path, like "zot.c"
|
||||
*/
|
||||
public String getSimpleName() {
|
||||
int idx = name.lastIndexOf('/');
|
||||
if(idx>0) return name.substring(idx+1);
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
@@ -239,7 +252,7 @@ public final class CVSChangeLogSet extends ChangeLogSet<CVSChangeLog> {
|
||||
}
|
||||
|
||||
public Revision(String s) {
|
||||
String[] tokens = s.split(".");
|
||||
String[] tokens = s.split("\\.");
|
||||
numbers = new int[tokens.length];
|
||||
for( int i=0; i<tokens.length; i++ )
|
||||
numbers[i] = Integer.parseInt(tokens[i]);
|
||||
@@ -249,6 +262,8 @@ public final class CVSChangeLogSet extends ChangeLogSet<CVSChangeLog> {
|
||||
/**
|
||||
* Returns a new {@link Revision} that represents the previous revision.
|
||||
*
|
||||
* For example, "1.5"->"1.4", "1.5.2.13"->"1.5.2.12", "1.5.2.1"->"1.5"
|
||||
*
|
||||
* @return
|
||||
* null if there's no previous version, meaning this is "1.1"
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package hudson.scm;
|
||||
|
||||
import java.net.URL;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link RepositoryBrowser} for CVS.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class CVSRepositoryBrowser extends RepositoryBrowser {
|
||||
/**
|
||||
* Determines the link to the diff between the version
|
||||
* in the {@link CVSChangeLogSet.File} to its previous version.
|
||||
*
|
||||
* @return
|
||||
* null if the browser doesn't have any URL for diff.
|
||||
*/
|
||||
public abstract URL getDiffLink(CVSChangeLogSet.File file) throws IOException;
|
||||
|
||||
/**
|
||||
* Determines the link to a single file under CVS.
|
||||
* This page should display all the past revisions of this file, etc.
|
||||
*
|
||||
* @return
|
||||
* null if the browser doesn't have any suitable URL.
|
||||
*/
|
||||
public abstract URL getFileLink(CVSChangeLogSet.File file) throws IOException;
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import hudson.model.AbstractModelObject;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Action;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.LargeText;
|
||||
@@ -108,14 +107,23 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
||||
*/
|
||||
private boolean flatten;
|
||||
|
||||
private CVSRepositoryBrowser repositoryBrowser;
|
||||
|
||||
public CVSSCM(String cvsroot, String module,String branch,String cvsRsh,boolean canUseUpdate, boolean flatten) {
|
||||
/**
|
||||
* @stapler-constructor
|
||||
*/
|
||||
public CVSSCM(String cvsroot, String module,String branch,String cvsRsh,boolean canUseUpdate, boolean legacy) {
|
||||
this.cvsroot = cvsroot;
|
||||
this.module = module.trim();
|
||||
this.branch = nullify(branch);
|
||||
this.cvsRsh = nullify(cvsRsh);
|
||||
this.canUseUpdate = canUseUpdate;
|
||||
this.flatten = flatten && module.indexOf(' ')==-1;
|
||||
this.flatten = !legacy && module.indexOf(' ')==-1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CVSRepositoryBrowser getBrowser() {
|
||||
return repositoryBrowser;
|
||||
}
|
||||
|
||||
private String compression() {
|
||||
@@ -697,7 +705,7 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
||||
env.put("CVS_PASSFILE",cvspass);
|
||||
}
|
||||
|
||||
public static final class DescriptorImpl extends Descriptor<SCM> implements ModelObject {
|
||||
public static final class DescriptorImpl extends SCMDescriptor implements ModelObject {
|
||||
static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
|
||||
|
||||
/**
|
||||
@@ -710,18 +718,17 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
||||
*/
|
||||
private String cvsExe;
|
||||
|
||||
/**
|
||||
* Copy-on-write.
|
||||
*/
|
||||
private volatile Map<String,RepositoryBrowser> browsers = new HashMap<String,RepositoryBrowser>();
|
||||
// compatibility only
|
||||
private transient Map<String,RepositoryBrowser> browsers;
|
||||
|
||||
// compatibility only
|
||||
class RepositoryBrowser {
|
||||
String diffURL;
|
||||
String browseURL;
|
||||
}
|
||||
|
||||
DescriptorImpl() {
|
||||
super(CVSSCM.class);
|
||||
super(CVSSCM.class,CVSRepositoryBrowser.class);
|
||||
load();
|
||||
}
|
||||
|
||||
@@ -733,15 +740,10 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
||||
return "CVS";
|
||||
}
|
||||
|
||||
public SCM newInstance(StaplerRequest req) {
|
||||
return new CVSSCM(
|
||||
req.getParameter("cvs_root"),
|
||||
req.getParameter("cvs_module"),
|
||||
req.getParameter("cvs_branch"),
|
||||
req.getParameter("cvs_rsh"),
|
||||
req.getParameter("cvs_use_update")!=null,
|
||||
req.getParameter("cvs_legacy")==null
|
||||
);
|
||||
public SCM newInstance(StaplerRequest req) throws FormException {
|
||||
CVSSCM scm = req.bindParameters(CVSSCM.class, "cvs.");
|
||||
scm.repositoryBrowser = RepositoryBrowsers.createInstance(CVSRepositoryBrowser.class,req,"cvs.browser");
|
||||
return scm;
|
||||
}
|
||||
|
||||
public String getCvspassFile() {
|
||||
@@ -761,35 +763,10 @@ public class CVSSCM extends AbstractCVSFamilySCM implements Serializable {
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL that shows the diff.
|
||||
*/
|
||||
public String getDiffURL(String cvsRoot, String pathName, String oldRev, String newRev) {
|
||||
RepositoryBrowser b = browsers.get(cvsRoot);
|
||||
if(b==null) return null;
|
||||
return b.diffURL.replaceAll("%%P",pathName).replace("%%r",oldRev).replace("%%R",newRev);
|
||||
|
||||
}
|
||||
|
||||
public boolean configure( StaplerRequest req ) {
|
||||
cvsPassFile = fixEmpty(req.getParameter("cvs_cvspass").trim());
|
||||
cvsExe = fixEmpty(req.getParameter("cvs_exe").trim());
|
||||
|
||||
Map<String,RepositoryBrowser> browsers = new HashMap<String, RepositoryBrowser>();
|
||||
int i=0;
|
||||
while(true) {
|
||||
String root = req.getParameter("cvs_repobrowser_cvsroot" + i);
|
||||
if(root==null) break;
|
||||
|
||||
RepositoryBrowser rb = new RepositoryBrowser();
|
||||
rb.browseURL = req.getParameter("cvs_repobrowser"+i);
|
||||
rb.diffURL = req.getParameter("cvs_repobrowser_diff"+i);
|
||||
browsers.put(root,rb);
|
||||
|
||||
i++;
|
||||
}
|
||||
this.browsers = browsers;
|
||||
|
||||
save();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -5,7 +5,6 @@ import hudson.Launcher;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.TaskListener;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
@@ -32,7 +31,7 @@ public class NullSCM extends SCM {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Descriptor<SCM> getDescriptor() {
|
||||
public SCMDescriptor getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
@@ -48,7 +47,7 @@ public class NullSCM extends SCM {
|
||||
return new NullChangeLogParser();
|
||||
}
|
||||
|
||||
static final Descriptor<SCM> DESCRIPTOR = new Descriptor<SCM>(NullSCM.class) {
|
||||
static final SCMDescriptor DESCRIPTOR = new SCMDescriptor(NullSCM.class,null) {
|
||||
public String getDisplayName() {
|
||||
return "None";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package hudson.scm;
|
||||
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.model.Describable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Connects Hudson to repository browsers like ViewCVS or FishEye,
|
||||
* so that Hudson can generate links to them.
|
||||
*
|
||||
* <p>
|
||||
* {@link RepositoryBrowser} instance is normally created as
|
||||
* a result of job configuration, and stores immutable
|
||||
* configuration information (such as the URL of the FishEye site).
|
||||
*
|
||||
* <p>
|
||||
* {@link RepositoryBrowser} is persisted with {@link SCM}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.89
|
||||
* @see RepositoryBrowsers
|
||||
*/
|
||||
public abstract class RepositoryBrowser implements ExtensionPoint, Describable<RepositoryBrowser> {
|
||||
/**
|
||||
* Determines the link to the given change set.
|
||||
*
|
||||
* @return
|
||||
* null if this repository browser doesn't have any meaningful
|
||||
* URL for a change set (for example, ViewCVS doesn't have
|
||||
* any page for a change set, whereas FishEye does.)
|
||||
*/
|
||||
public abstract URL getChangeSetLink(ChangeLogSet.Entry changeSet) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package hudson.scm;
|
||||
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Descriptor.FormException;
|
||||
import hudson.scm.browsers.ViewCVS;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
/**
|
||||
* List of all installed {@link RepositoryBrowsers}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class RepositoryBrowsers {
|
||||
/**
|
||||
* List of all installed {@link RepositoryBrowsers}.
|
||||
*/
|
||||
public static final List<Descriptor<RepositoryBrowser>> LIST = Arrays.asList(
|
||||
ViewCVS.DESCRIPTOR
|
||||
);
|
||||
|
||||
/**
|
||||
* Only returns those {@link RepositoryBrowser} descriptors that extend from the given type.
|
||||
*/
|
||||
public static List<Descriptor<RepositoryBrowser>> filter(Class<? extends RepositoryBrowser> t) {
|
||||
List<Descriptor<RepositoryBrowser>> r = new ArrayList<Descriptor<RepositoryBrowser>>();
|
||||
for (Descriptor<RepositoryBrowser> d : LIST)
|
||||
if(t.isAssignableFrom(d.clazz))
|
||||
r.add(d);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link RepositoryBrowser} from a form submission.
|
||||
*/
|
||||
public static <T extends RepositoryBrowser>
|
||||
T createInstance(Class<T> type, StaplerRequest req, String fieldName) throws FormException {
|
||||
List<Descriptor<RepositoryBrowser>> list = filter(type);
|
||||
String value = req.getParameter(fieldName);
|
||||
if(value==null || value.equals("auto"))
|
||||
return null;
|
||||
|
||||
return type.cast(list.get(Integer.parseInt(value)).newInstance(req));
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ import hudson.model.Describable;
|
||||
import hudson.model.TaskListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -25,6 +25,18 @@ import java.util.Map;
|
||||
*/
|
||||
public abstract class SCM implements Describable<SCM>, ExtensionPoint {
|
||||
|
||||
/**
|
||||
* Returns the {@link RepositoryBrowser} for files
|
||||
* controlled by this {@link SCM}.
|
||||
*
|
||||
* @return
|
||||
* null to indicate that there's no configured browser
|
||||
* for this SCM instance.
|
||||
*/
|
||||
public RepositoryBrowser getBrowser() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there has been any changes to this module in the repository.
|
||||
*
|
||||
@@ -103,6 +115,8 @@ public abstract class SCM implements Describable<SCM>, ExtensionPoint {
|
||||
*/
|
||||
public abstract ChangeLogParser createChangeLogParser();
|
||||
|
||||
public abstract SCMDescriptor getDescriptor();
|
||||
|
||||
protected final boolean createEmptyChangeLog(File changelogFile, BuildListener listener, String rootTag) {
|
||||
try {
|
||||
FileWriter w = new FileWriter(changelogFile);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package hudson.scm;
|
||||
|
||||
import hudson.model.Descriptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* {@link Descriptor} for {@link SCM}.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class SCMDescriptor extends Descriptor<SCM> {
|
||||
/**
|
||||
* If this SCM has corresponding {@link RepositoryBrowser},
|
||||
* that type. Otherwise this SCM will not have any repository browser.
|
||||
*/
|
||||
public final Class<? extends RepositoryBrowser> repositoryBrowser;
|
||||
|
||||
protected SCMDescriptor(Class<? extends SCM> clazz, Class<? extends RepositoryBrowser> repositoryBrowser) {
|
||||
super(clazz);
|
||||
this.repositoryBrowser = repositoryBrowser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@link RepositoryBrowser} {@link Descriptor}
|
||||
* that can be used with this SCM.
|
||||
*
|
||||
* @return
|
||||
* can be empty but never null.
|
||||
*/
|
||||
public List<Descriptor<RepositoryBrowser>> getBrowserDescriptors() {
|
||||
if(repositoryBrowser==null) return Collections.emptyList();
|
||||
return RepositoryBrowsers.filter(repositoryBrowser);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ public class SCMS {
|
||||
* List of all installed SCMs.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // generic array creation
|
||||
public static final List<Descriptor<SCM>> SCMS =
|
||||
public static final List<SCMDescriptor> SCMS =
|
||||
Descriptor.toList(
|
||||
NullSCM.DESCRIPTOR,
|
||||
CVSSCM.DescriptorImpl.DESCRIPTOR,
|
||||
|
||||
@@ -110,6 +110,7 @@ public final class SubversionChangeLogSet extends ChangeLogSet<LogEntry> {
|
||||
}
|
||||
|
||||
public void addPath( Path p ) {
|
||||
p.entry = this;
|
||||
paths.add(p);
|
||||
}
|
||||
|
||||
@@ -118,10 +119,18 @@ public final class SubversionChangeLogSet extends ChangeLogSet<LogEntry> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A file in a commit.
|
||||
*/
|
||||
public static class Path {
|
||||
private LogEntry entry;
|
||||
private char action;
|
||||
private String value;
|
||||
|
||||
public LogEntry getLogEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action.charAt(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package hudson.scm;
|
||||
|
||||
import java.net.URL;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link RepositoryBrowser} for Subversion.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public abstract class SubversionRepositoryBrowser extends RepositoryBrowser {
|
||||
/**
|
||||
* Determines the link to the diff between the version
|
||||
* in the specified revision of {@link SubversionChangeLogSet.Path} to its previous version.
|
||||
*
|
||||
* @return
|
||||
* null if the browser doesn't have any URL for diff.
|
||||
*/
|
||||
public abstract URL getDiffLink(SubversionChangeLogSet.Path path) throws IOException;
|
||||
|
||||
/**
|
||||
* Determines the link to a single file under Subversion.
|
||||
* This page should display all the past revisions of this file, etc.
|
||||
*
|
||||
* @return
|
||||
* null if the browser doesn't have any suitable URL.
|
||||
*/
|
||||
public abstract URL getFileLink(SubversionChangeLogSet.Path path) throws IOException;
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import static hudson.Util.fixNull;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.remoting.Channel;
|
||||
@@ -62,8 +61,8 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Subversion.
|
||||
@@ -476,7 +475,7 @@ public class SubversionSCM extends SCM implements Serializable {
|
||||
return tokens[tokens.length-1]; // return the last token
|
||||
}
|
||||
|
||||
public static final class DescriptorImpl extends Descriptor<SCM> {
|
||||
public static final class DescriptorImpl extends SCMDescriptor {
|
||||
public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl();
|
||||
|
||||
/**
|
||||
@@ -559,7 +558,7 @@ public class SubversionSCM extends SCM implements Serializable {
|
||||
}
|
||||
|
||||
private DescriptorImpl() {
|
||||
super(SubversionSCM.class);
|
||||
super(SubversionSCM.class,null);
|
||||
load();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package hudson.scm.browsers;
|
||||
|
||||
import hudson.model.Descriptor;
|
||||
import hudson.scm.CVSChangeLogSet.File;
|
||||
import hudson.scm.CVSChangeLogSet.Revision;
|
||||
import hudson.scm.ChangeLogSet.Entry;
|
||||
import hudson.scm.RepositoryBrowser;
|
||||
import hudson.scm.CVSRepositoryBrowser;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* {@link RepositoryBrowser} for
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public final class ViewCVS extends CVSRepositoryBrowser {
|
||||
/**
|
||||
* The URL of the top of the site.
|
||||
*
|
||||
* Normalized to ends with '/', like <tt>http://isscvs.cern.ch/cgi-bin/viewcvs-all.cgi/</tt>
|
||||
* It may contain a query parameter like <tt>?cvsroot=foobar</tt>, so relative URL
|
||||
* construction needs to be done with care.
|
||||
*/
|
||||
public final URL url;
|
||||
|
||||
/**
|
||||
* @stapler-constructor
|
||||
*/
|
||||
public ViewCVS(URL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public URL getFileLink(File file) throws IOException {
|
||||
return new URL(url,trimHeadSlash(file.getName())+param());
|
||||
}
|
||||
|
||||
public URL getDiffLink(File file) throws IOException {
|
||||
Revision r = new Revision(file.getRevision());
|
||||
Revision p = r.getPrevious();
|
||||
if(p==null) return null;
|
||||
|
||||
return new URL(getFileLink(file), file.getSimpleName()+".diff"+param().add("r1="+p).add("r2="+r));
|
||||
}
|
||||
|
||||
/**
|
||||
* No changeset support in ViewCVS.
|
||||
*/
|
||||
public URL getChangeSetLink(Entry changeSet) throws IOException {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String trimHeadSlash(String s) {
|
||||
if(s.startsWith("/")) return s.substring(1);
|
||||
return s;
|
||||
}
|
||||
|
||||
private QueryBuilder param() {
|
||||
return new QueryBuilder(url.getQuery());
|
||||
}
|
||||
|
||||
private static final class QueryBuilder {
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
|
||||
public QueryBuilder(String s) {
|
||||
add(s);
|
||||
}
|
||||
|
||||
private QueryBuilder add(String s) {
|
||||
if(buf.length()==0) buf.append('?');
|
||||
else buf.append('&');
|
||||
buf.append(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public Descriptor<RepositoryBrowser> getDescriptor() {
|
||||
return DESCRIPTOR;
|
||||
}
|
||||
|
||||
public static final Descriptor<RepositoryBrowser> DESCRIPTOR = new Descriptor<RepositoryBrowser>(ViewCVS.class) {
|
||||
public String getDisplayName() {
|
||||
return "ViewCVS";
|
||||
}
|
||||
|
||||
public RepositoryBrowser newInstance(StaplerRequest req) throws FormException {
|
||||
return req.bindParameters(ViewCVS.class,"viewcvs.");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -8,6 +8,9 @@
|
||||
<li><st:out value="${cs.msg}" /></li>
|
||||
</j:forEach>
|
||||
</ol>
|
||||
|
||||
<j:set var="browser" value="${it.build.parent.scm.browser}"/>
|
||||
|
||||
<table class="pane" style="border:none">
|
||||
<j:forEach var="cs" items="${it.logs}" varStatus="loop">
|
||||
<tr class="pane">
|
||||
@@ -16,6 +19,11 @@
|
||||
<div class="changeset-message">
|
||||
<b><a href="${rootURL}/${cs.author.url}/">${cs.author}</a>:</b><br/>
|
||||
${cs.msgAnnotated}
|
||||
|
||||
<j:set var="csLink" value="${browser.getChangeSetLink(cs)}"/>
|
||||
<j:if test="${csLink!=null}">
|
||||
<a href="${csLink}">(${browser.descriptor.displayName})</a>
|
||||
</j:if>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -23,8 +31,11 @@
|
||||
<j:forEach var="f" items="${cs.files}">
|
||||
<tr>
|
||||
<td><t:editTypeIcon type="${f.editType}" /></td>
|
||||
<td>${f.revision}</td>
|
||||
<td>${f.name}</td>
|
||||
<td>
|
||||
<a href="${browser.getDiffLink(f)}">${f.revision}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="${browser.getFileLink(f)}">${f.name}</a></td>
|
||||
</tr>
|
||||
</j:forEach>
|
||||
</j:forEach>
|
||||
|
||||
@@ -1,24 +1,40 @@
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
<f:entry title="CVSROOT" help="/help/_cvs/cvsroot.html">
|
||||
<f:editableComboBox id="cvs_root" clazz="setting-input validated"
|
||||
name="cvs_root" value="${scm.cvsRoot}"
|
||||
name="cvs.cvsroot" value="${scm.cvsRoot}"
|
||||
items="${app.allCvsRoots}"
|
||||
checkUrl="'${rootURL}/scm/CVSSCM/cvsrootCheck?value='+escape(this.value)" />
|
||||
</f:entry>
|
||||
<f:entry title="Module(s)" help="/help/_cvs/modules.html">
|
||||
<f:textbox name="cvs_module" value="${scm.allModules}"/>
|
||||
<f:textbox name="cvs.module" value="${scm.allModules}"/>
|
||||
</f:entry>
|
||||
<f:entry title="Branch" help="/help/_cvs/branch.html">
|
||||
<f:textbox name="cvs_branch" value="${scm.branch}"/>
|
||||
<f:textbox name="cvs.branch" value="${scm.branch}"/>
|
||||
</f:entry>
|
||||
<f:entry title="CVS_RSH" help="/help/_cvs/cvs-rsh.html">
|
||||
<f:textbox name="cvs_rsh" value="${scm.cvsRsh}"/>
|
||||
<f:textbox name="cvs.cvsRsh" value="${scm.cvsRsh}"/>
|
||||
</f:entry>
|
||||
<f:entry title="Use update" help="/help/_cvs/update.html">
|
||||
<f:checkbox name="cvs_use_update" checked="${scm.canUseUpdate}"/>
|
||||
<f:checkbox name="cvs.canUseUpdate" checked="${scm.canUseUpdate}"/>
|
||||
</f:entry>
|
||||
<f:entry title="Legacy mode" help="/help/_cvs/legacy.html">
|
||||
<f:checkbox name="cvs_legacy" checked="${scm!=null and !scm.flatten}"/>
|
||||
<f:checkbox name="cvs.legacy" checked="${scm!=null and !scm.flatten}"/>
|
||||
(run CVS in a way compatible with older versions of Hudson <1.21)
|
||||
</f:entry>
|
||||
|
||||
<!-- TODO: share with all SCMs -->
|
||||
<f:dropdownList name="cvs.browser" title="Repository browser">
|
||||
<f:dropdownListBlock value="auto" title="(Auto)" />
|
||||
<j:set var="currentBrowser" value="${scm.browser}"/>
|
||||
<j:forEach var="d" items="${scmd.browserDescriptors}" varStatus="loop">
|
||||
<f:dropdownListBlock value="${loop.index}" title="${d.displayName}" selected="${currentBrowser.descriptor==d}">
|
||||
<f:nested>
|
||||
<table width="100%">
|
||||
<j:set var="browser" value="${h.ifThenElse(currentBrowser.descriptor==d, currentBrowser, null)}"/>
|
||||
<st:include from="${d}" page="${d.configPage}"/>
|
||||
</table>
|
||||
</f:nested>
|
||||
</f:dropdownListBlock>
|
||||
</j:forEach>
|
||||
</f:dropdownList>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,5 @@
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
<f:entry title="URL" help="/help/viewcvs/url.html">
|
||||
<f:textbox name="viewcvs.url" value="${browser.url}"/>
|
||||
</f:entry>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
Foldable block expanded when the corresponding item is selected in the drop-down list.
|
||||
|
||||
@name (mandatory)
|
||||
name of the drop-down list.
|
||||
-->
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
|
||||
<!-- first invoke the body to collect all <dropdownListBlock> -->
|
||||
<j:useList var="dropdownItems" />
|
||||
<d:invokeBody />
|
||||
|
||||
<j:set var="id" value="${h.generateId()}"/>
|
||||
<f:entry title="${title}">
|
||||
<!-- create drop-down list -->
|
||||
<select name="${name}" id="${id}" onchange="updateDropDownList(this)">
|
||||
<j:forEach var="item" items="${dropdownItems}">
|
||||
<f:option selected="${item.selected}" value="${item.value}">${item.title}</f:option>
|
||||
</j:forEach>
|
||||
</select>
|
||||
<script>
|
||||
$$('${id}').forms = [];
|
||||
</script>
|
||||
</f:entry>
|
||||
|
||||
<!-- generate the actual form entries -->
|
||||
<j:forEach var="item" items="${dropdownItems}">
|
||||
<!-- sandwitch them by a marker so that we now what to show/hide -->
|
||||
<j:set var="sid" value="${h.generateId()}"/>
|
||||
<j:set var="eid" value="${h.generateId()}"/>
|
||||
<tr id="${sid}" style="display:none" />
|
||||
<d:invoke script="${item.body}" />
|
||||
<tr id="${eid}" style="display:none" />
|
||||
<script>
|
||||
$$('${id}').forms.push({
|
||||
start: $$('${sid}'),
|
||||
end: $$('${eid}')
|
||||
});
|
||||
</script>
|
||||
</j:forEach>
|
||||
|
||||
<!-- set the initial visibility -->
|
||||
<script>
|
||||
updateDropDownList($$('${id}'));
|
||||
</script>
|
||||
</j:jelly>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
Foldable block expanded when the corresponding item is selected in the drop-down list.
|
||||
|
||||
@value (mandatory)
|
||||
value of the list item. set to <option value="...">
|
||||
@title (mandatory)
|
||||
human readable text displayed for this list item.
|
||||
@selected (mandatory)
|
||||
is this value initially selected?
|
||||
-->
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
<j:mute>
|
||||
${dropdownItems.add(attrs)}
|
||||
${attrs.put('body',context.getVariable('org.apache.commons.jelly.body'))}
|
||||
</j:mute>
|
||||
</j:jelly>
|
||||
@@ -11,11 +11,11 @@
|
||||
<td class="setting-name">
|
||||
${attrs.title}
|
||||
</td>
|
||||
<td>
|
||||
<td class="setting-main">
|
||||
<d:invokeBody />
|
||||
</td>
|
||||
<j:if test="${attrs.help!=null}">
|
||||
<td>
|
||||
<td class="setting-help">
|
||||
<a href="#" class="help-button" helpURL="${rootURL}${attrs.help}"><img src="${rootURL}/images/16x16/help.gif" alt="Help for feature: ${title}" /></a>
|
||||
</td>
|
||||
</j:if>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<!--
|
||||
Used to display indented nested portion of the form
|
||||
-->
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
|
||||
<tr>
|
||||
<td colspan="2"/>
|
||||
<td colspan="2">
|
||||
<d:invokeBody />
|
||||
</td>
|
||||
</tr>
|
||||
</j:jelly>
|
||||
@@ -160,12 +160,19 @@ pre.console {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.setting-leftspace {
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
.setting-name {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.setting-leftspace {
|
||||
width: 2em;
|
||||
.setting-main {
|
||||
width: 100%; /* try to make this column as big as possible. */
|
||||
}
|
||||
.setting-help {
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.setting-input {
|
||||
@@ -316,6 +323,7 @@ th.pane {
|
||||
border: 1px solid #ccb;
|
||||
background: #eed;
|
||||
padding: 4px;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.error {
|
||||
|
||||
@@ -386,3 +386,17 @@ Form.findMatchingInput = function(base, name) {
|
||||
return null; // not found
|
||||
}
|
||||
|
||||
// used witih <dropdownList> and <dropdownListBlock> to control visibility
|
||||
function updateDropDownList(sel) {
|
||||
// alert('Yay! '+sel.value+' '+sel.selectedIndex);
|
||||
for (var i = 0; i < sel.forms.length; i++) {
|
||||
var show = sel.selectedIndex == i;
|
||||
var f = sel.forms[i];
|
||||
var td = f.start;
|
||||
while (true) {
|
||||
td.style.display = (show ? "" : "none");
|
||||
if (td == f.end) break;
|
||||
td = td.nextSibling;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user