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 cl, List<File> files) { 056 if (files == null || files.isEmpty()) { 057 return; 058 } 059 final File workingDirectory = cl.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 cl.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 * @param workingDirectory 088 * @param command 089 * @return TODO 090 */ 091 public static Commandline getBaseGitCommandLine(File workingDirectory, String command) { 092 return getBaseGitCommandLine(workingDirectory, command, null, null); 093 } 094 095 /** 096 * Use this for commands requiring environment variables (i.e. remote commands). 097 * @param workingDirectory 098 * @param command 099 * @param environment 100 * @return TODO 101 */ 102 public static Commandline getBaseGitCommandLine( 103 File workingDirectory, 104 String command, 105 GitScmProviderRepository repository, 106 Map<String, String> environment) { 107 Commandline cl = getAnonymousBaseGitCommandLine(workingDirectory, command); 108 if (repository != null) { 109 prepareEnvVariablesForRepository(repository, environment).forEach(cl::addEnvironment); 110 } else if (environment != null) { 111 environment.forEach(cl::addEnvironment); 112 } 113 return cl; 114 } 115 116 /** 117 * Creates a {@link Commandline} for which the toString() do not display 118 * password. 119 * 120 * @param workingDirectory 121 * @param command 122 * @return CommandLine with anonymous output. 123 */ 124 private static Commandline getAnonymousBaseGitCommandLine(File workingDirectory, String command) { 125 if (command == null || command.length() == 0) { 126 return null; 127 } 128 129 Commandline cl = new AnonymousCommandLine(); 130 131 composeCommand(workingDirectory, command, cl); 132 133 return cl; 134 } 135 136 private static void composeCommand(File workingDirectory, String command, Commandline cl) { 137 Settings settings = GitUtil.getSettings(); 138 139 cl.setExecutable(settings.getGitCommand()); 140 141 cl.createArg().setValue(command); 142 143 if (workingDirectory != null) { 144 cl.setWorkingDirectory(workingDirectory.getAbsolutePath()); 145 } 146 } 147 148 public static int execute(Commandline cl, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr) 149 throws ScmException { 150 if (LOGGER.isInfoEnabled()) { 151 LOGGER.info("Executing: " + cl); 152 LOGGER.info("Working directory: " + cl.getWorkingDirectory().getAbsolutePath()); 153 } 154 155 int exitCode; 156 try { 157 exitCode = CommandLineUtils.executeCommandLine(cl, consumer, stderr); 158 } catch (CommandLineException ex) { 159 throw new ScmException("Error while executing command.", ex); 160 } 161 162 return exitCode; 163 } 164 165 public static int execute( 166 Commandline cl, CommandLineUtils.StringStreamConsumer stdout, CommandLineUtils.StringStreamConsumer stderr) 167 throws ScmException { 168 if (LOGGER.isInfoEnabled()) { 169 LOGGER.info("Executing: " + cl); 170 LOGGER.info("Working directory: " + cl.getWorkingDirectory().getAbsolutePath()); 171 } 172 173 int exitCode; 174 try { 175 exitCode = CommandLineUtils.executeCommandLine(cl, stdout, stderr); 176 } catch (CommandLineException ex) { 177 throw new ScmException("Error while executing command.", ex); 178 } 179 180 return exitCode; 181 } 182 183 static Map<String, String> prepareEnvVariablesForRepository( 184 GitScmProviderRepository repository, Map<String, String> environmentVariables) { 185 Map<String, String> effectiveEnvironmentVariables = new HashMap<>(); 186 if (environmentVariables != null) { 187 effectiveEnvironmentVariables.putAll(environmentVariables); 188 } 189 if (StringUtils.isNotBlank(repository.getPrivateKey())) { 190 if (effectiveEnvironmentVariables.putIfAbsent( 191 VARIABLE_GIT_SSH_COMMAND, 192 "ssh -o IdentitiesOnly=yes -i " 193 + FilenameUtils.separatorsToUnix(repository.getPrivateKey())) 194 != null) { 195 LOGGER.warn( 196 "Ignore GitScmProviderRepository.privateKey as environment variable {} is already set", 197 VARIABLE_GIT_SSH_COMMAND); 198 } 199 } 200 if (StringUtils.isNotBlank(repository.getPassphrase())) { 201 LOGGER.warn("GitScmProviderRepository.passphrase currently not supported by provider 'git'"); 202 } 203 return effectiveEnvironmentVariables; 204 } 205}