Skip to content

Commit

Permalink
Merge pull request #149 from recena/JENKINS-16711
Browse files Browse the repository at this point in the history
[JENKINS-16711] Allow repository URL-encoded
  • Loading branch information
recena committed Dec 17, 2015
2 parents ae4ae64 + defa208 commit 7e586e7
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 101 deletions.
13 changes: 8 additions & 5 deletions src/main/java/hudson/scm/SubversionChangeLogBuilder.java
Expand Up @@ -154,18 +154,21 @@ private PathContext getUrlForPath(FilePath path, ISVNAuthenticationProvider auth
private boolean buildModule(PathContext context, SVNLogClient svnlc, DirAwareSVNXMLLogHandler logHandler) throws IOException2 {
String url = context.url;
PrintStream logger = listener.getLogger();

Long prevRev = previousRevisions.get(url);
if(prevRev==null) {
logger.println("no revision recorded for "+url+" in the previous build");
if (prevRev == null) {
logger.println("No revision recorded for " + url + " in the previous build");
return false;
}

Long thisRev = thisRevisions.get(url);
if (thisRev == null) {
listener.error("No revision found for URL: " + url + " in " + SubversionSCM.getRevisionFile(build) + ". Revision file contains: " + thisRevisions.keySet());
listener.error("No revision found for " + url + " in " + SubversionSCM.getRevisionFile(build) + ". Revision file contains: " + thisRevisions.keySet());
return false;
}
if(thisRev.equals(prevRev)) {
logger.println("no change for "+url+" since the previous build");

if (thisRev.equals(prevRev)) {
logger.println("No changes for " + url + " since the previous build");
return false;
}

Expand Down
117 changes: 36 additions & 81 deletions src/main/java/hudson/scm/SubversionSCM.java
Expand Up @@ -61,7 +61,6 @@
import hudson.Extension;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.Functions;
import hudson.Launcher;
import hudson.Util;
import hudson.init.InitMilestone;
Expand All @@ -78,11 +77,9 @@
import java.util.LinkedHashSet;
import java.util.WeakHashMap;

import hudson.remoting.LocalChannel;
import hudson.security.ACL;
import hudson.util.ListBoxModel;
import jenkins.model.Jenkins;
import jenkins.model.Jenkins.MasterComputer;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.VirtualChannel;
Expand All @@ -95,7 +92,6 @@
import hudson.scm.subversion.WorkspaceUpdater;
import hudson.scm.subversion.WorkspaceUpdater.UpdateTask;
import hudson.scm.subversion.WorkspaceUpdaterDescriptor;
import hudson.util.EditDistance;
import hudson.util.FormValidation;
import hudson.util.LogTaskListener;
import hudson.util.MultipartFormDataParser;
Expand Down Expand Up @@ -164,8 +160,8 @@
import org.tmatesoft.svn.core.internal.io.dav.http.DefaultHTTPConnectionFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.SVNPath;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory;
import org.tmatesoft.svn.core.io.SVNCapability;
import org.tmatesoft.svn.core.io.SVNRepository;
Expand Down Expand Up @@ -2291,8 +2287,7 @@ public FormValidation doCheckRemote(StaplerRequest req, @AncestorInPath Abstract
if (instance != null) {
ModuleLocation.DescriptorImpl d = instance.getDescriptorByType(ModuleLocation.DescriptorImpl.class);
if (d != null) {
return d.doCheckCredentialsId(
req, context, value, credentialsId);
return d.doCheckCredentialsId(req, context, value, credentialsId);
}
}

Expand All @@ -2316,7 +2311,8 @@ public SVNNodeKind checkRepositoryPath(Item context, SVNURL repoURL, StandardCre
SVNRepository repository = null;

try {
repository = getRepository(context,repoURL,credentials, Collections.<String, Credentials>emptyMap(), null);
repository = getRepository(context, repoURL, credentials, Collections.<String, Credentials>emptyMap(),
null);
repository.testConnection();

long rev = repository.getLatestRevision();
Expand All @@ -2336,8 +2332,9 @@ public SVNNodeKind checkRepositoryPath(Item context, SVNURL repoURL, StandardCre
}
throw e;
} finally {
if (repository != null)
if (repository != null) {
repository.closeSession();
}
}
}

Expand Down Expand Up @@ -3002,7 +2999,7 @@ public static class DescriptorImpl extends Descriptor<ModuleLocation> {

@Override
public String getDisplayName() {
return null; //To change body of implemented methods use File | Settings | File Templates.
return null;
}

public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, @QueryParameter String remote) {
Expand Down Expand Up @@ -3040,20 +3037,23 @@ public ListBoxModel fillCredentialsIdItems(@Nonnull Item context, String remote)
public FormValidation doCheckRemote(StaplerRequest req, @AncestorInPath Item context,
@QueryParameter String remote) {

// syntax check first
// repository URL is required
String url = Util.fixEmptyAndTrim(remote);
if (url == null) {
return FormValidation.error(Messages.SubversionSCM_doCheckRemote_required());
}

if (descriptor().isValidateRemoteUpToVar()) {
url = (url.indexOf('$') != -1) ? url.substring(0, url.indexOf('$')) : url;
} else {
url = new EnvVars(EnvVars.masterEnvVars).expand(url);
// Is the repository URL parameterized?
if (url.indexOf('$') != -1) {
return FormValidation.warning("This repository URL is parameterized, syntax validation skipped");
}

if (!URL_PATTERN.matcher(url).matches()) {
return FormValidation.errorWithMarkup(Messages.SubversionSCM_doCheckRemote_invalidUrl());
// repository URL syntax
try {
SVNURL.parseURIEncoded(url);
} catch (SVNException svne) {
LOGGER.log(Level.SEVERE, svne.getMessage());
return FormValidation.error(Messages.SubversionSCM_doCheckRemote_invalidUrl());
}
return FormValidation.ok();
}
Expand All @@ -3075,81 +3075,35 @@ public FormValidation doCheckCredentialsId(StaplerRequest req, @AncestorInPath I
* Validate the value for a remote (repository) location.
*/
public FormValidation checkCredentialsId(StaplerRequest req, @Nonnull Item context, String remote, String value) {

// Ignore validation if repository URL is empty
String url = Util.fixEmptyAndTrim(remote);
if (url == null) {
return FormValidation.ok();
}

// TODO: It would be interesting to include global variables.
url = new EnvVars(EnvVars.masterEnvVars).expand(url);
if (!URL_PATTERN.matcher(url).matches()) {
return FormValidation.ok();
}

// Is the repository URL parameterized?
if (url.indexOf('$') != -1) {
return FormValidation.warning("This repository URL is parameterized, validation skipped.");
if (remote.indexOf('$') != -1) {
return FormValidation.warning("The repository URL is parameterized, connection check skipped");
}

try {
String urlWithoutRevision = SvnHelper.getUrlWithoutRevision(url);

SVNURL repoURL = SVNURL.parseURIDecoded(urlWithoutRevision);

SVNURL repoURL = SVNURL.parseURIEncoded(remote);
StandardCredentials credentials = lookupCredentials(context, value, repoURL);
if (descriptor().checkRepositoryPath(context, repoURL, credentials) != SVNNodeKind.NONE) {
// something exists; now check revision if any

SVNRevision revision = getRevisionFromRemoteUrl(url);
if (revision != null && !revision.isValid()) {
return FormValidation.errorWithMarkup(Messages.SubversionSCM_doCheckRemote_invalidRevision());
}

return FormValidation.ok();
}

SVNRepository repository = null;
try {
repository = descriptor().getRepository(context, repoURL, credentials, Collections.<String,
Credentials>emptyMap(), null);
long rev = repository.getLatestRevision();
// now go back the tree and find if there's anything that exists
String repoPath = descriptor().getRelativePath(repoURL, repository);
String p = repoPath;
while (p.length() > 0) {
p = SVNPathUtil.removeTail(p);
if (repository.checkPath(p, rev) == SVNNodeKind.DIR) {
// found a matching path
List<SVNDirEntry> entries = new ArrayList<SVNDirEntry>();
repository.getDir(p, rev, false, entries);

// build up the name list
List<String> paths = new ArrayList<String>();
for (SVNDirEntry e : entries) {
if (e.getKind() == SVNNodeKind.DIR) {
paths.add(e.getName());
}
}

String head = SVNPathUtil.head(repoPath.substring(p.length() + 1));
String candidate = EditDistance.findNearest(head, paths);

return FormValidation.error(
Messages.SubversionSCM_doCheckRemote_badPathSuggest(p, head,
candidate != null ? "/" + candidate : ""));
}
}

return FormValidation.error(Messages.SubversionSCM_doCheckRemote_badPath(repoPath));
} finally {
if (repository != null) {
repository.closeSession();
}
SVNRepository repo = descriptor().getRepository(context, repoURL, credentials, Collections
.<String, Credentials>emptyMap(), null);
String repoRoot = repo.getRepositoryRoot(false).toString();
String repoPath = repo.getLocation().toString().substring(repoRoot.length());
SVNPath path = new SVNPath(repoPath, true, true);
SVNNodeKind svnNodeKind = repo.checkPath(path.getTarget(), path.getPegRevision().getNumber());
if (svnNodeKind != SVNNodeKind.DIR) {
return FormValidation.error("Credentials looks fine but the repository URL is invalid");
}
} catch (SVNException e) {
LOGGER.log(Level.SEVERE, "Unable to access to subversion repository. " + e.getMessage());
return FormValidation.error("Unable to access to subversion repository");
LOGGER.log(Level.SEVERE, e.getErrorMessage().getMessage());
return FormValidation.error("Unable to access to repository");
}
return FormValidation.ok();
}

/**
Expand Down Expand Up @@ -3249,9 +3203,10 @@ private static Map describeBean(Object o) throws InvocationTargetException, NoSu
* Gets the revision from a remote URL - i.e. the part after '@' if any
*
* @return the revision or null
*
* TODO: This method should be in {@link SVNURL}.
*/
private static SVNRevision getRevisionFromRemoteUrl(
String remoteUrlPossiblyWithRevision) {
private static SVNRevision getRevisionFromRemoteUrl(String remoteUrlPossiblyWithRevision) {
int idx = remoteUrlPossiblyWithRevision.lastIndexOf('@');
int slashIdx = remoteUrlPossiblyWithRevision.lastIndexOf('/');
if (idx > 0 && idx > slashIdx) {
Expand Down
10 changes: 4 additions & 6 deletions src/main/java/hudson/scm/subversion/CheckoutUpdater.java
Expand Up @@ -87,13 +87,11 @@ public List<External> perform() throws IOException, InterruptedException {
sct.start();

try {
SVNRevision r = getRevision(location);
SVNRevision r = getRevision(location);
String revisionName = r.getDate() != null ? fmt.format(r.getDate()) : r.toString();

String revisionName = r.getDate() != null ?
fmt.format(r.getDate()) : r.toString();

listener.getLogger().println("Checking out " + location.remote + " at revision " + revisionName);
listener.getLogger().println("Checking out " + location.getSVNURL().toString() + " at revision " +
revisionName);

File local = new File(ws, location.getLocalDir());
SubversionUpdateEventHandler eventHandler = new SubversionUpdateEventHandler(new PrintStream(pos), externals, local, location.getLocalDir());
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/hudson/scm/subversion/UpdateUpdater.java
Expand Up @@ -85,14 +85,14 @@ protected SvnCommandToUse getSvnCommandToUse() throws IOException {
}

try {
SVNInfo svnkitInfo = parseSvnInfo(module);
SvnInfo svnInfo = new SvnInfo(svnkitInfo);
SVNInfo svnInfo = parseSvnInfo(module);

String url = location.getSVNURL().toString();
String wcUrl = svnInfo.getURL().toString();

if (!svnInfo.url.equals(url)) {
if (isSameRepository(location, svnkitInfo)) {
listener.getLogger().println("Switching from " + svnInfo.url + " to " + url);
if (!wcUrl.equals(url)) {
if (isSameRepository(location, svnInfo)) {
listener.getLogger().println("Switching from " + wcUrl + " to " + url);
return SvnCommandToUse.SWITCH;
} else {
listener.getLogger().println("Checking out a fresh workspace because the workspace is not " + url);
Expand Down
5 changes: 1 addition & 4 deletions src/main/resources/hudson/scm/subversion/Messages.properties
Expand Up @@ -31,10 +31,7 @@ SubversionSCM.doCheckRemote.exceptionMsg1=\
Unable to access {0} : {1} <a href="#" id="svnerrorlink" onclick="{2}">(show details)</a>
SubversionSCM.doCheckRemote.exceptionMsg2=\
(Maybe you need to <a href="{0}" target="_blank">enter credential</a>?)
SubversionSCM.doCheckRemote.invalidUrl=\
Invalid URL syntax. See <a \
href="http://svnbook.red-bean.com/en/1.2/svn-book.html#svn.basic.in-action.wc.tbl-1" \
target="_blank">this</a> for information about valid URLs.
SubversionSCM.doCheckRemote.invalidUrl=Invalid URL syntax
SubversionSCM.doCheckRemote.invalidRevision=\
Invalid revision. Revision may either be a number or a <a \
href="http://svnbook.red-bean.com/en/1.5/svn.tour.revs.specifiers.html" \
Expand Down

0 comments on commit 7e586e7

Please sign in to comment.