2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package jetbrains.buildServer.buildTriggers.vcs.git.agent;
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import com.intellij.openapi.util.SystemInfo;
21 import com.intellij.openapi.util.io.FileUtil;
22 import jetbrains.buildServer.ExecResult;
23 import jetbrains.buildServer.LineAwareByteArrayOutputStream;
24 import jetbrains.buildServer.agent.BuildInterruptReason;
25 import jetbrains.buildServer.buildTriggers.vcs.git.AuthSettings;
26 import jetbrains.buildServer.buildTriggers.vcs.git.AuthenticationMethod;
27 import jetbrains.buildServer.buildTriggers.vcs.git.GitUtils;
28 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.ScriptGen;
29 import jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl.*;
30 import jetbrains.buildServer.ssh.VcsRootSshKeyManager;
31 import jetbrains.buildServer.vcs.VcsException;
32 import org.jetbrains.annotations.NotNull;
33 import org.jetbrains.annotations.Nullable;
35 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 import java.nio.charset.Charset;
39 import java.util.ArrayList;
40 import java.util.HashMap;
41 import java.util.List;
44 public class GitCommandLine extends GeneralCommandLine {
46 private final GitAgentSSHService mySsh;
47 private final ScriptGen myScriptGen;
48 private final List<Runnable> myPostActions = new ArrayList<Runnable>();
49 private final File myTmpDir;
50 private final boolean myDeleteTempFiles;
51 private final GitProgressLogger myLogger;
52 private final GitVersion myGitVersion;
53 private final Context myCtx;
54 private File myWorkingDirectory;
55 private boolean myRepeatOnEmptyOutput = false;
56 private VcsRootSshKeyManager mySshKeyManager;
57 private boolean myHasProgress = false;
59 public GitCommandLine(@Nullable GitAgentSSHService ssh,
60 @NotNull ScriptGen scriptGen,
62 boolean deleteTempFiles,
63 @NotNull GitProgressLogger logger,
64 @NotNull GitVersion gitVersion,
65 @NotNull Map<String, String> env,
66 @NotNull Context ctx) {
68 myScriptGen = scriptGen;
70 myDeleteTempFiles = deleteTempFiles;
72 myGitVersion = gitVersion;
74 setPassParentEnvs(true);
78 public ExecResult run(@NotNull GitCommandSettings settings) throws VcsException {
79 AuthSettings authSettings = settings.getAuthSettings();
80 if (myCtx.isProvideCredHelper() && !getParametersList().getParametersString().contains("credential.helper") && !myGitVersion.isLessThan(UpdaterImpl.EMPTY_CRED_HELPER)) {
81 //Disable credential helper if it wasn't specified by us, as default
82 //helper can require a user input. Do that even if our repository doesn't
83 //require any auth, auth can be required by submodules or git lfs.
85 //It would be cleaner to add the following
90 //to the local repository config and not disable helpers in every command.
91 //But some commands ignore this setting, e.g. 'git submodules update':
92 //https://public-inbox.org/git/CAC+L6n0YeX_n_AysCLtBWkA+jPHwg7HmOWq2PLj75byxOZE=qQ@mail.gmail.com/
93 getParametersList().addAt(0, "-c");
94 getParametersList().addAt(1, "credential.helper=");
96 if (authSettings != null) {
98 throw new IllegalStateException("Ssh is not initialized");
99 if (authSettings.getAuthMethod() == AuthenticationMethod.PASSWORD) {
101 final File askPass = myScriptGen.generateAskPass(authSettings);
102 String askPassPath = askPass.getAbsolutePath();
103 if (askPassPath.contains(" ") && SystemInfo.isWindows) {
104 askPassPath = GitUtils.getShortFileName(askPass);
106 getParametersList().addAt(0, "-c");
107 getParametersList().addAt(1, "core.askpass=" + askPassPath);
108 addPostAction(new Runnable() {
110 if (myDeleteTempFiles)
111 FileUtil.delete(askPass);
114 addEnvParam("GIT_ASKPASS", askPassPath);
115 } catch (IOException e) {
116 throw new VcsException(e);
119 if (settings.isUseNativeSsh()) {
120 return CommandUtil.runCommand(this, settings.getTimeout());
122 SshHandler h = new SshHandler(mySsh, mySshKeyManager, authSettings, this, myTmpDir, myCtx.getSshMacType());
124 return CommandUtil.runCommand(this, settings.getTimeout());
130 return CommandUtil.runCommand(this, settings.getTimeout());
136 public GitVersion getGitVersion() {
140 public void addPostAction(@NotNull Runnable action) {
141 myPostActions.add(action);
144 public List<Runnable> getPostActions() {
145 return myPostActions;
149 public void setWorkingDirectory(File workingDirectory) {
150 myWorkingDirectory = workingDirectory;
151 super.setWorkingDirectory(workingDirectory);
155 public File getWorkingDirectory() {
156 return myWorkingDirectory;
159 public GitCommandLine repeatOnEmptyOutput(boolean doRepeat) {
160 myRepeatOnEmptyOutput = doRepeat;
164 public boolean isRepeatOnEmptyOutput() {
165 return myRepeatOnEmptyOutput;
168 public void setSshKeyManager(VcsRootSshKeyManager sshKeyManager) {
169 mySshKeyManager = sshKeyManager;
172 public void addEnvParam(@NotNull String name, @NotNull String value) {
173 Map<String, String> existing = getEnvParams();
174 if (existing == null)
175 existing = new HashMap<String, String>();
176 Map<String, String> newParams = new HashMap<String, String>(existing);
177 newParams.put(name, value);
178 setEnvParams(newParams);
182 public ByteArrayOutputStream createStderrBuffer() {
183 LineAwareByteArrayOutputStream buffer = new LineAwareByteArrayOutputStream(Charset.forName("UTF-8"), new GitProgressListener(myLogger));
184 buffer.setCREndsLine(true);
188 public void logStart(@NotNull String msg) {
190 myLogger.openBlock(msg);
192 myLogger.message(msg);
196 public void logFinish(@NotNull String msg) {
198 myLogger.closeBlock(msg);
201 public void setHasProgress(final boolean hasProgress) {
202 myHasProgress = hasProgress;
206 public void checkCanceled() throws VcsException {
207 BuildInterruptReason reason = myCtx.getInterruptionReason();
209 throw new CheckoutCanceledException(reason);