forked from jenkinsci/scm-sync-configuration-plugin
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JENKINS-24686: fix behavior for job names with blanks
The basic problem here is SCM-772.[1] maven-scm cannot parse the output from git status if it contains quoted/escaped file names. There's at least two pull requests, one attached to SCM-772 and one at [2] aimed at fixing this; both are erroneous and don't look like they'd go in anytime soon. (The first one would at least replace \r by \f, and the second one only strips quotes but doesn't de-escape.) So we fix this by providing our own commands in our own gitexe provider and making sure that our implementation of these commands can deal with quoted/escaped filenames. Enabled the two test cases for job names with blanks. Also, since we're rewriting part of maven-scm here anyway, I've also included a minimal and proper fix for SCM-695.[3] [1] https://issues.apache.org/jira/browse/SCM-772 [2] apache/maven-scm#26 [3] https://issues.apache.org/jira/browse/SCM-695
- Loading branch information
Showing
8 changed files
with
616 additions
and
124 deletions.
There are no files selected for viewing
204 changes: 204 additions & 0 deletions
204
...lugins/scm_sync_configuration/scms/customproviders/git/gitexe/FixedGitStatusConsumer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
package hudson.plugins.scm_sync_configuration.scms.customproviders.git.gitexe; | ||
|
||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import java.io.File; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
import org.apache.commons.lang.StringUtils; | ||
import org.apache.maven.scm.ScmFile; | ||
import org.apache.maven.scm.ScmFileStatus; | ||
import org.apache.maven.scm.log.ScmLogger; | ||
import org.codehaus.plexus.util.cli.StreamConsumer; | ||
|
||
import com.google.common.base.Strings; | ||
|
||
/** | ||
* Copied from org.apache.maven.scm.provider.git.gitexe.command.status.GitStatusConsumer (maven-scm 1.9.1) | ||
* and fixed to account for https://issues.apache.org/jira/browse/SCM-772 . | ||
*/ | ||
public class FixedGitStatusConsumer | ||
implements StreamConsumer | ||
{ | ||
|
||
/** | ||
* The pattern used to match added file lines | ||
*/ | ||
private static final Pattern ADDED_PATTERN = Pattern.compile( "^A[ M]* (.*)$" ); | ||
|
||
/** | ||
* The pattern used to match modified file lines | ||
*/ | ||
private static final Pattern MODIFIED_PATTERN = Pattern.compile( "^ *M[ M]* (.*)$" ); | ||
|
||
/** | ||
* The pattern used to match deleted file lines | ||
*/ | ||
private static final Pattern DELETED_PATTERN = Pattern.compile( "^ *D * (.*)$" ); | ||
|
||
/** | ||
* The pattern used to match renamed file lines | ||
*/ | ||
private static final Pattern RENAMED_PATTERN = Pattern.compile( "^R (.*) -> (.*)$" ); | ||
|
||
private ScmLogger logger; | ||
|
||
private File workingDirectory; | ||
|
||
/** | ||
* Entries are relative to working directory, not to the repositoryroot | ||
*/ | ||
private List<ScmFile> changedFiles = new ArrayList<ScmFile>(); | ||
|
||
private String relativeRepositoryPath; | ||
|
||
private final File repositoryRoot; | ||
|
||
// ---------------------------------------------------------------------- | ||
// | ||
// ---------------------------------------------------------------------- | ||
|
||
public FixedGitStatusConsumer (ScmLogger logger, File workingDirectory, File repositoryRoot) { | ||
this.logger = logger; | ||
this.workingDirectory = workingDirectory; | ||
if (repositoryRoot != null) { | ||
String absoluteRepositoryRoot = repositoryRoot.getAbsolutePath(); // Make sure all separators are File.separator | ||
// The revparse runs with fileset.getBasedir(). That of course must be under the repo root. | ||
String basePath = workingDirectory.getAbsolutePath(); | ||
if (!absoluteRepositoryRoot.endsWith(File.separator)) { | ||
absoluteRepositoryRoot += File.separator; | ||
} | ||
if (basePath.startsWith(absoluteRepositoryRoot)) { | ||
String pathInsideRepo = basePath.substring(absoluteRepositoryRoot.length()); | ||
if (!Strings.isNullOrEmpty(pathInsideRepo)) { | ||
relativeRepositoryPath = pathInsideRepo; | ||
} | ||
} | ||
} | ||
this.repositoryRoot = repositoryRoot; | ||
// Either the workingDirectory == repositoryRoot: we have no relativeRepositoryPath set | ||
// Or the working directory was a subdirectory (in the workspace!) of repositoryRoot, then | ||
// relativeRepositoryPath contains now the relative path to the working directory. | ||
// | ||
// It would appear that git status --porcelain always returns paths relative to the repository | ||
// root. | ||
} | ||
|
||
// ---------------------------------------------------------------------- | ||
// StreamConsumer Implementation | ||
// ---------------------------------------------------------------------- | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public void consumeLine( String line ) | ||
{ | ||
if ( logger.isDebugEnabled() ) | ||
{ | ||
logger.debug( line ); | ||
} | ||
if ( StringUtils.isEmpty( line ) ) | ||
{ | ||
return; | ||
} | ||
|
||
ScmFileStatus status = null; | ||
|
||
List<String> files = new ArrayList<String>(); | ||
|
||
Matcher matcher; | ||
if ( ( matcher = ADDED_PATTERN.matcher( line ) ).find() ) | ||
{ | ||
status = ScmFileStatus.ADDED; | ||
files.add(ScmSyncGitUtils.dequote(matcher.group(1))); | ||
} | ||
else if ( ( matcher = MODIFIED_PATTERN.matcher( line ) ).find() ) | ||
{ | ||
status = ScmFileStatus.MODIFIED; | ||
files.add(ScmSyncGitUtils.dequote(matcher.group(1))); | ||
} | ||
else if ( ( matcher = DELETED_PATTERN.matcher( line ) ) .find() ) | ||
{ | ||
status = ScmFileStatus.DELETED; | ||
files.add(ScmSyncGitUtils.dequote(matcher.group(1))); | ||
} | ||
else if ( ( matcher = RENAMED_PATTERN.matcher( line ) ).find() ) | ||
{ | ||
status = ScmFileStatus.RENAMED; | ||
files.add(ScmSyncGitUtils.dequote(matcher.group(1))); | ||
files.add(ScmSyncGitUtils.dequote(matcher.group(2))); | ||
} | ||
else | ||
{ | ||
logger.warn( "Ignoring unrecognized line: " + line ); | ||
return; | ||
} | ||
|
||
// If the file isn't a file; don't add it. | ||
if (files.isEmpty() || status == null) { | ||
return; | ||
} | ||
File checkDir = repositoryRoot; | ||
if (workingDirectory != null && relativeRepositoryPath != null) { | ||
// Make all paths relative to this directory. | ||
List<String> relativeNames = new ArrayList<String>(); | ||
for (String repoRelativeName : files) { | ||
relativeNames.add(ScmSyncGitUtils.relativizePath(relativeRepositoryPath, new File(repoRelativeName).getPath())); | ||
} | ||
files = relativeNames; | ||
checkDir = workingDirectory; | ||
} | ||
// Now check them all against the checkDir. This check has been taken over from the base implementation | ||
// in maven-scm's GitStatusConsumer, but I'm not really sure this makes sense. Who said the workspace | ||
// had to be equal to the git index (staging area) here? | ||
if (status == ScmFileStatus.RENAMED) { | ||
String oldFilePath = files.get( 0 ); | ||
String newFilePath = files.get( 1 ); | ||
if (new File(checkDir, oldFilePath).isFile()) { | ||
logger.debug("file '" + oldFilePath + "' still exists after rename"); | ||
return; | ||
} | ||
if (!new File(checkDir, newFilePath).isFile()) { | ||
logger.debug("file '" + newFilePath + "' does not exist after rename"); | ||
return; | ||
} | ||
} else if (status == ScmFileStatus.DELETED) { | ||
if (new File(checkDir, files.get(0)).isFile()) { | ||
return; | ||
} | ||
} else { | ||
if (!new File(checkDir, files.get(0)).isFile()) { | ||
return; | ||
} | ||
} | ||
|
||
for (String file : files) { | ||
changedFiles.add(new ScmFile(file.replaceAll(File.separator, "/"), status)); | ||
} | ||
} | ||
|
||
public List<ScmFile> getChangedFiles() | ||
{ | ||
return changedFiles; | ||
} | ||
} |
116 changes: 116 additions & 0 deletions
116
.../plugins/scm_sync_configuration/scms/customproviders/git/gitexe/ScmSyncGitAddCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
package hudson.plugins.scm_sync_configuration.scms.customproviders.git.gitexe; | ||
|
||
import java.io.File; | ||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import org.apache.commons.io.FilenameUtils; | ||
import org.apache.maven.scm.ScmException; | ||
import org.apache.maven.scm.ScmFile; | ||
import org.apache.maven.scm.ScmFileSet; | ||
import org.apache.maven.scm.command.add.AddScmResult; | ||
import org.apache.maven.scm.command.status.StatusScmResult; | ||
import org.apache.maven.scm.provider.ScmProviderRepository; | ||
import org.apache.maven.scm.provider.git.gitexe.command.GitCommandLineUtils; | ||
import org.apache.maven.scm.provider.git.gitexe.command.add.GitAddCommand; | ||
import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; | ||
import org.codehaus.plexus.util.Os; | ||
import org.codehaus.plexus.util.cli.CommandLineUtils; | ||
import org.codehaus.plexus.util.cli.Commandline; | ||
|
||
public class ScmSyncGitAddCommand extends GitAddCommand { | ||
|
||
protected AddScmResult executeAddCommand(ScmProviderRepository repo, ScmFileSet fileSet, String message, boolean binary) throws ScmException { | ||
GitScmProviderRepository repository = (GitScmProviderRepository) repo; | ||
|
||
if (fileSet.getFileList().isEmpty()) { | ||
throw new ScmException("You must provide at least one file/directory to add"); | ||
} | ||
|
||
AddScmResult result = executeAddFileSet(fileSet); | ||
|
||
if (result != null) { | ||
return result; | ||
} | ||
|
||
ScmSyncGitStatusCommand statusCommand = new ScmSyncGitStatusCommand(); | ||
statusCommand.setLogger(getLogger()); | ||
StatusScmResult status = statusCommand.executeStatusCommand(repository, fileSet); | ||
getLogger().warn("add - status - " + status.isSuccess()); | ||
for (ScmFile s : status.getChangedFiles()) { | ||
getLogger().warn("added " + s.getPath()); | ||
} | ||
List<ScmFile> changedFiles = new ArrayList<ScmFile>(); | ||
|
||
if (fileSet.getFileList().isEmpty()) { | ||
changedFiles = status.getChangedFiles(); | ||
} else { | ||
for (ScmFile scmfile : status.getChangedFiles()) { | ||
// if a specific fileSet is given, we have to check if the file is really tracked | ||
for (File f : fileSet.getFileList()) { | ||
if (FilenameUtils.separatorsToUnix(f.getPath()).equals(scmfile.getPath())) { | ||
changedFiles.add(scmfile); | ||
} | ||
} | ||
} | ||
} | ||
Commandline cl = createCommandLine(fileSet.getBasedir(), fileSet.getFileList()); | ||
return new AddScmResult(cl.toString(), changedFiles); | ||
} | ||
|
||
public static Commandline createCommandLine(File workingDirectory, List<File> files) throws ScmException { | ||
Commandline cl = GitCommandLineUtils.getBaseGitCommandLine(workingDirectory, "add"); | ||
|
||
// use this separator to make clear that the following parameters are files and not revision info. | ||
cl.createArg().setValue("--"); | ||
|
||
ScmSyncGitUtils.addTarget(cl, files); | ||
|
||
return cl; | ||
} | ||
|
||
protected AddScmResult executeAddFileSet(ScmFileSet fileSet) throws ScmException { | ||
File workingDirectory = fileSet.getBasedir(); | ||
List<File> files = fileSet.getFileList(); | ||
|
||
// command line can be too long for windows so add files individually (see SCM-697) | ||
if (Os.isFamily(Os.FAMILY_WINDOWS)) { | ||
for (File file : files) { | ||
AddScmResult result = executeAddFiles(workingDirectory, Collections.singletonList(file)); | ||
if (result != null) { | ||
return result; | ||
} | ||
} | ||
} else { | ||
AddScmResult result = executeAddFiles(workingDirectory, files); | ||
if (result != null) { | ||
return result; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private AddScmResult executeAddFiles(File workingDirectory, List<File> files) throws ScmException { | ||
Commandline cl = createCommandLine(workingDirectory, files); | ||
|
||
CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer(); | ||
CommandLineUtils.StringStreamConsumer stdout = new CommandLineUtils.StringStreamConsumer(); | ||
|
||
int exitCode = -1; | ||
try { | ||
exitCode = GitCommandLineUtils.execute(cl, stdout, stderr, getLogger()); | ||
} catch (Throwable t) { | ||
getLogger().error("Failed:", t); | ||
} | ||
if (exitCode != 0) { | ||
String msg = stderr.getOutput(); | ||
getLogger().info("Add failed:" + msg); | ||
return new AddScmResult(cl.toString(), "The git-add command failed.", msg, false); | ||
} | ||
|
||
return null; | ||
} | ||
|
||
} |
Oops, something went wrong.