001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.scm.provider.git.gitexe.command; 020 021import java.io.File; 022import java.io.IOException; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.io.FilenameUtils; 028import org.apache.commons.lang3.StringUtils; 029import org.apache.maven.scm.ScmException; 030import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository; 031import org.apache.maven.scm.provider.git.util.GitUtil; 032import org.apache.maven.scm.providers.gitlib.settings.Settings; 033import org.codehaus.plexus.util.cli.CommandLineException; 034import org.codehaus.plexus.util.cli.CommandLineUtils; 035import org.codehaus.plexus.util.cli.Commandline; 036import org.codehaus.plexus.util.cli.StreamConsumer; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040/** 041 * Command line construction utility. 042 * 043 * @author Brett Porter 044 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a> 045 * 046 */ 047public final class GitCommandLineUtils { 048 private static final Logger LOGGER = LoggerFactory.getLogger(GitCommandLineUtils.class); 049 050 // https://git-scm.com/docs/git#Documentation/git.txt-codeGITSSHCOMMANDcode, requires git 2.3.0 or newer 051 public static final String VARIABLE_GIT_SSH_COMMAND = "GIT_SSH_COMMAND"; 052 053 private GitCommandLineUtils() {} 054 055 public static void addTarget(Commandline commandLine, List<File> files) { 056 if (files == null || files.isEmpty()) { 057 return; 058 } 059 final File workingDirectory = commandLine.getWorkingDirectory(); 060 try { 061 final String canonicalWorkingDirectory = workingDirectory.getCanonicalPath(); 062 for (File file : files) { 063 String relativeFile = file.getPath(); 064 065 final String canonicalFile = file.getCanonicalPath(); 066 if (canonicalFile.startsWith(canonicalWorkingDirectory)) { 067 // so we can omit the starting characters 068 relativeFile = canonicalFile.substring(canonicalWorkingDirectory.length()); 069 070 if (relativeFile.startsWith(File.separator)) { 071 relativeFile = relativeFile.substring(File.separator.length()); 072 } 073 } 074 075 // no setFile() since this screws up the working directory! 076 commandLine.createArg().setValue(FilenameUtils.separatorsToUnix(relativeFile)); 077 } 078 } catch (IOException ex) { 079 throw new IllegalArgumentException( 080 "Could not get canonical paths for workingDirectory = " + workingDirectory + " or files=" + files, 081 ex); 082 } 083 } 084 085 /** 086 * Use this only for commands not requiring environment variables (i.e. local commands). 087 */ 088 public static Commandline getBaseGitCommandLine(File workingDirectory, String command) { 089 return getBaseGitCommandLine(workingDirectory, command, null, null); 090 } 091 092 /** 093 * Use this for commands requiring environment variables (i.e. remote commands). 094 */ 095 public static Commandline getBaseGitCommandLine( 096 File workingDirectory, 097 String command, 098 GitScmProviderRepository repository, 099 Map<String, String> environment) { 100 Commandline commandLine = getAnonymousBaseGitCommandLine(workingDirectory, command); 101 if (repository != null) { 102 prepareEnvVariablesForRepository(repository, environment).forEach(commandLine::addEnvironment); 103 } else if (environment != null) { 104 environment.forEach(commandLine::addEnvironment); 105 } 106 return commandLine; 107 } 108 109 /** 110 * Creates a {@link Commandline} for which toString() does not display 111 * the password. 112 * 113 * @param workingDirectory 114 * @param command 115 * @return CommandLine with anonymous output 116 */ 117 private static Commandline getAnonymousBaseGitCommandLine(File workingDirectory, String command) { 118 if (command == null || command.isEmpty()) { 119 return null; 120 } 121 122 Commandline commandLine = new AnonymousCommandLine(); 123 124 composeCommand(workingDirectory, command, commandLine); 125 126 return commandLine; 127 } 128 129 private static void composeCommand(File workingDirectory, String command, Commandline commandLine) { 130 Settings settings = GitUtil.getSettings(); 131 132 commandLine.setExecutable(settings.getGitCommand()); 133 134 commandLine.createArg().setValue(command); 135 136 if (workingDirectory != null) { 137 commandLine.setWorkingDirectory(workingDirectory.getAbsolutePath()); 138 } 139 } 140 141 public static int execute( 142 Commandline commandline, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr) 143 throws ScmException { 144 if (LOGGER.isInfoEnabled()) { 145 LOGGER.info("Executing: " + commandline); 146 LOGGER.info( 147 "Working directory: " + commandline.getWorkingDirectory().getAbsolutePath()); 148 } 149 150 int exitCode; 151 try { 152 exitCode = CommandLineUtils.executeCommandLine(commandline, consumer, stderr); 153 } catch (CommandLineException ex) { 154 throw new ScmException("Error while executing command.", ex); 155 } 156 157 return exitCode; 158 } 159 160 public static int execute( 161 Commandline commandLine, 162 CommandLineUtils.StringStreamConsumer stdout, 163 CommandLineUtils.StringStreamConsumer stderr) 164 throws ScmException { 165 if (LOGGER.isInfoEnabled()) { 166 LOGGER.info("Executing: " + commandLine); 167 LOGGER.info( 168 "Working directory: " + commandLine.getWorkingDirectory().getAbsolutePath()); 169 } 170 171 int exitCode; 172 try { 173 exitCode = CommandLineUtils.executeCommandLine(commandLine, stdout, stderr); 174 } catch (CommandLineException ex) { 175 throw new ScmException("Error while executing command.", ex); 176 } 177 178 return exitCode; 179 } 180 181 static Map<String, String> prepareEnvVariablesForRepository( 182 GitScmProviderRepository repository, Map<String, String> environmentVariables) { 183 Map<String, String> effectiveEnvironmentVariables = new HashMap<>(); 184 if (environmentVariables != null) { 185 effectiveEnvironmentVariables.putAll(environmentVariables); 186 } 187 if (StringUtils.isNotBlank(repository.getPrivateKey())) { 188 if (effectiveEnvironmentVariables.putIfAbsent( 189 VARIABLE_GIT_SSH_COMMAND, 190 "ssh -o IdentitiesOnly=yes -i " 191 + FilenameUtils.separatorsToUnix(repository.getPrivateKey())) 192 != null) { 193 LOGGER.warn( 194 "Ignore GitScmProviderRepository.privateKey as environment variable {} is already set", 195 VARIABLE_GIT_SSH_COMMAND); 196 } 197 } 198 if (StringUtils.isNotBlank(repository.getPassphrase())) { 199 LOGGER.warn("GitScmProviderRepository.passphrase currently not supported by provider 'git'"); 200 } 201 return effectiveEnvironmentVariables; 202 } 203}