View Javadoc
1   package org.apache.maven.scm.provider.git.gitexe.command;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.io.FilenameUtils;
23  
24  import org.apache.maven.scm.ScmException;
25  import org.apache.maven.scm.provider.git.repository.GitScmProviderRepository;
26  import org.apache.maven.scm.provider.git.util.GitUtil;
27  import org.apache.maven.scm.providers.gitlib.settings.Settings;
28  import org.codehaus.plexus.util.StringUtils;
29  import org.codehaus.plexus.util.cli.CommandLineException;
30  import org.codehaus.plexus.util.cli.CommandLineUtils;
31  import org.codehaus.plexus.util.cli.Commandline;
32  import org.codehaus.plexus.util.cli.StreamConsumer;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  
36  import java.io.File;
37  import java.io.IOException;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  
42  /**
43   * Command line construction utility.
44   *
45   * @author Brett Porter
46   * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
47   *
48   */
49  public final class GitCommandLineUtils
50  {
51      private static final Logger LOGGER = LoggerFactory.getLogger( GitCommandLineUtils.class );
52  
53      // https://git-scm.com/docs/git#Documentation/git.txt-codeGITSSHCOMMANDcode, requires git 2.3.0 or newer
54      public static final String VARIABLE_GIT_SSH_COMMAND = "GIT_SSH_COMMAND";
55  
56      private GitCommandLineUtils()
57      {
58      }
59  
60      public static void addTarget( Commandline cl, List<File> files )
61      {
62          if ( files == null || files.isEmpty() )
63          {
64              return;
65          }
66          final File workingDirectory = cl.getWorkingDirectory();
67          try
68          {
69              final String canonicalWorkingDirectory = workingDirectory.getCanonicalPath();
70              for ( File file : files )
71              {
72                  String relativeFile = file.getPath();
73  
74                  final String canonicalFile = file.getCanonicalPath();
75                  if ( canonicalFile.startsWith( canonicalWorkingDirectory ) )
76                  {
77                      // so we can omit the starting characters
78                      relativeFile = canonicalFile.substring( canonicalWorkingDirectory.length() );
79  
80                      if ( relativeFile.startsWith( File.separator ) )
81                      {
82                          relativeFile = relativeFile.substring( File.separator.length() );
83                      }
84                  }
85  
86                  // no setFile() since this screws up the working directory!
87                  cl.createArg().setValue( FilenameUtils.separatorsToUnix( relativeFile ) );
88              }
89          }
90          catch ( IOException ex )
91          {
92              throw new IllegalArgumentException( "Could not get canonical paths for workingDirectory = "
93                  + workingDirectory + " or files=" + files, ex );
94          }
95      }
96  
97     /**
98      * Use this only for commands not requiring environment variables (i.e. local commands).
99      * @param workingDirectory
100     * @param command
101     * @return TODO
102     */
103    public static Commandline getBaseGitCommandLine( File workingDirectory, String command )
104    {
105        return getBaseGitCommandLine( workingDirectory, command, null, null );
106    }
107 
108     /**
109      * Use this for commands requiring environment variables (i.e. remote commands).
110      * @param workingDirectory
111      * @param command
112      * @param environment
113      * @return TODO
114      */
115     public static Commandline getBaseGitCommandLine( File workingDirectory, String command,
116                                                      GitScmProviderRepository repository,
117                                                      Map<String, String> environment )
118     {
119         Commandline cl = getAnonymousBaseGitCommandLine( workingDirectory, command );
120         if ( repository != null )
121         {
122             prepareEnvVariablesForRepository( repository, environment ).forEach( cl::addEnvironment );
123         }
124         else if ( environment != null )
125         {
126             environment.forEach( cl::addEnvironment );
127         }
128         return cl;
129     }
130 
131     /**
132      * Creates a {@link Commandline} for which the toString() do not display
133      * password.
134      *
135      * @param workingDirectory
136      * @param command
137      * @return CommandLine with anonymous output.
138      */
139     private static Commandline getAnonymousBaseGitCommandLine( File workingDirectory, String command )
140     {
141         if ( command == null || command.length() == 0 )
142         {
143             return null;
144         }
145 
146         Commandline cl = new AnonymousCommandLine();
147 
148         composeCommand( workingDirectory, command, cl );
149 
150         return cl;
151     }
152 
153     private static void composeCommand( File workingDirectory, String command, Commandline cl )
154     {
155         Settings settings = GitUtil.getSettings();
156 
157         cl.setExecutable( settings.getGitCommand() );
158 
159         cl.createArg().setValue( command );
160 
161         if ( workingDirectory != null )
162         {
163             cl.setWorkingDirectory( workingDirectory.getAbsolutePath() );
164         }
165     }
166 
167     public static int execute( Commandline cl, StreamConsumer consumer, CommandLineUtils.StringStreamConsumer stderr )
168         throws ScmException
169     {
170         if ( LOGGER.isInfoEnabled() )
171         {
172             LOGGER.info( "Executing: " + cl );
173             LOGGER.info( "Working directory: " + cl.getWorkingDirectory().getAbsolutePath() );
174         }
175 
176         int exitCode;
177         try
178         {
179             exitCode = CommandLineUtils.executeCommandLine( cl, consumer, stderr );
180         }
181         catch ( CommandLineException ex )
182         {
183             throw new ScmException( "Error while executing command.", ex );
184         }
185 
186         return exitCode;
187     }
188 
189     public static int execute( Commandline cl, CommandLineUtils.StringStreamConsumer stdout,
190                                CommandLineUtils.StringStreamConsumer stderr )
191         throws ScmException
192     {
193         if ( LOGGER.isInfoEnabled() )
194         {
195             LOGGER.info( "Executing: " + cl );
196             LOGGER.info( "Working directory: " + cl.getWorkingDirectory().getAbsolutePath() );
197         }
198 
199         int exitCode;
200         try
201         {
202             exitCode = CommandLineUtils.executeCommandLine( cl, stdout, stderr );
203         }
204         catch ( CommandLineException ex )
205         {
206             throw new ScmException( "Error while executing command.", ex );
207         }
208 
209         return exitCode;
210     }
211 
212     static Map<String, String> prepareEnvVariablesForRepository( GitScmProviderRepository repository,
213                                                                  Map<String, String> environmentVariables )
214     {
215         Map<String, String> effectiveEnvironmentVariables = new HashMap<>();
216         if ( environmentVariables != null )
217         {
218             effectiveEnvironmentVariables.putAll( environmentVariables );
219         }
220         if ( StringUtils.isNotBlank( repository.getPrivateKey() ) )
221         {
222             if ( effectiveEnvironmentVariables.putIfAbsent( VARIABLE_GIT_SSH_COMMAND, "ssh -o IdentitiesOnly=yes -i "
223                             + FilenameUtils.separatorsToUnix( repository.getPrivateKey() ) ) != null )
224             {
225                 LOGGER.warn( "Ignore GitScmProviderRepository.privateKey as environment variable {} is already set",
226                              VARIABLE_GIT_SSH_COMMAND );
227             }
228         }
229         if ( StringUtils.isNotBlank( repository.getPassphrase() ) )
230         {
231             LOGGER.warn( "GitScmProviderRepository.passphrase currently not supported by provider 'git'" );
232         }
233         return effectiveEnvironmentVariables;
234     }
235 }