0e4f9e30429949b2e833f874cd9f204739169669
[teamcity/git-plugin.git] / git-server / src / jetbrains / buildServer / buildTriggers / vcs / git / patch / GitPatchProcess.java
1 /*
2  * Copyright 2000-2018 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package jetbrains.buildServer.buildTriggers.vcs.git.patch;
18
19 import jetbrains.buildServer.buildTriggers.vcs.git.*;
20 import jetbrains.buildServer.buildTriggers.vcs.git.submodules.SubmoduleFetchException;
21 import jetbrains.buildServer.serverSide.CachePaths;
22 import jetbrains.buildServer.ssh.TeamCitySshKey;
23 import jetbrains.buildServer.ssh.VcsRootSshKeyManager;
24 import jetbrains.buildServer.vcs.CheckoutRules;
25 import jetbrains.buildServer.vcs.VcsRoot;
26 import jetbrains.buildServer.vcs.VcsUtil;
27 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
28 import jetbrains.buildServer.vcs.patches.PatchBuilderImpl;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 import java.io.*;
33 import java.util.Map;
34
35 public class GitPatchProcess {
36
37   public static void main(String... args) throws Exception {
38     Map<String, String> properties = VcsUtil.stringToProperties(GitServerUtil.readInput());
39     GitPatchProcessSettings settings = new GitPatchProcessSettings(properties);
40     GitServerUtil.configureInternalProperties(settings.getInternalProperties());
41     GitServerUtil.configureStreamFileThreshold(Integer.MAX_VALUE);
42     GitServerUtil.configureExternalProcessLogger(settings.isDebugEnabled());
43
44     PluginConfigImpl config = new PluginConfigImpl(new ConstantCachePaths(settings.getGitCachesDir()));
45     RepositoryManager repositoryManager = new RepositoryManagerImpl(config, new MirrorManagerImpl(config, new HashCalculatorImpl()));
46     GitMapFullPath mapFullPath = new GitMapFullPath(config, new RevisionsCache(config));
47     VcsRootSshKeyManager sshKeyManager = new ConstantSshKeyManager(settings.getKeyBytes());
48     TransportFactory transportFactory = new TransportFactoryImpl(config, sshKeyManager, settings.getGitTrustStoreProvider());
49     FetcherProperties fetcherProperties = new FetcherProperties(config);
50     FetchCommand fetchCommand = new FetchCommandImpl(config, transportFactory, fetcherProperties, sshKeyManager,
51                                                      settings.getGitTrustStoreProvider());
52     CommitLoader commitLoader = new CommitLoaderImpl(repositoryManager, fetchCommand, mapFullPath);
53
54     OperationContext context = new OperationContext(commitLoader, repositoryManager, settings.getRoot(), "build patch", GitProgress.NO_OP, config);
55     OutputStream fos = new BufferedOutputStream(new FileOutputStream(settings.getPatchFile()));
56     try {
57       PatchBuilderImpl patchBuilder = new PatchBuilderImpl(fos);
58       new GitPatchBuilder(context,
59                           patchBuilder,
60                           settings.getFromRevision(),
61                           settings.getToRevision(),
62                           settings.getCheckoutRules(),
63                           settings.isVerboseTreeWalkLog(),
64                           new PrintFile()).buildPatch();
65       patchBuilder.close();
66     } catch (Throwable t) {
67       if (settings.isDebugEnabled() || isImportant(t)) {
68         System.err.println(t.getMessage());
69         t.printStackTrace(System.err);
70       } else {
71         String msg = t.getMessage();
72         boolean printStackTrace = false;
73         if (t instanceof SubmoduleFetchException) {
74           Throwable cause = t.getCause();
75           printStackTrace = cause != null && isImportant(cause);
76         }
77         System.err.println(msg);
78         if (printStackTrace)
79           t.printStackTrace(System.err);
80       }
81       System.exit(1);
82     } finally {
83       fos.close();
84     }
85   }
86
87
88   private static class ConstantCachePaths implements CachePaths {
89     private final File myCachesDir;
90     public ConstantCachePaths(@NotNull File cachesDir) {
91       myCachesDir = cachesDir;
92     }
93
94     @NotNull
95     public File getCacheDirectory(@NotNull final String name) {
96       return myCachesDir;
97     }
98   }
99
100
101   private static class ConstantSshKeyManager implements VcsRootSshKeyManager {
102     private final byte[] myKeyBytes;
103
104     public ConstantSshKeyManager(@Nullable byte[] keyBytes) {
105       myKeyBytes = keyBytes;
106     }
107
108     @Nullable
109     public TeamCitySshKey getKey(@NotNull VcsRoot root) {
110       if (myKeyBytes == null)
111         return null;
112       return new TeamCitySshKey(""/*doesn't matter*/, myKeyBytes, false/*doesn't matter*/);
113     }
114   }
115
116
117   private static class GitPatchProcessSettings {
118     private final File myInternalProperties;
119     private final boolean myVerboseTreeWalkLog;
120     private final String myFromRevision;
121     private final String myToRevision;
122     private final CheckoutRules myCheckoutRules;
123     private final File myGitCachesDir;
124     private final File myPatchFile;
125     private final byte[] myKeyBytes;
126     private final boolean myDebugEnabled;
127     private final VcsRoot myRoot;
128     private final GitTrustStoreProvider myGitTrustStoreProvider;
129
130     public GitPatchProcessSettings(@NotNull Map<String, String> props) {
131       myInternalProperties = readInternalProperties(props);
132       myVerboseTreeWalkLog = readVerboseTreeLog(props);
133       myFromRevision = readFromRevision(props);
134       myToRevision = readToRevision(props);
135       myCheckoutRules = readCheckoutRules(props);
136       myGitCachesDir = readGitCachesDir(props);
137       myPatchFile = readPatchFile(props);
138       myKeyBytes = readKeyBytes(props);
139       myDebugEnabled = readDebugEnabled(props);
140       myRoot = readRoot(props);
141       myGitTrustStoreProvider = readGitTrustStoreProvider(props);
142     }
143
144     @NotNull
145     private File readInternalProperties(@NotNull Map<String, String> props) {
146       String path = props.remove(Constants.FETCHER_INTERNAL_PROPERTIES_FILE);
147       if (path == null)
148         throw new IllegalArgumentException("internal.properties file is not specified");
149       return new File(path);
150     }
151
152     private boolean readVerboseTreeLog(@NotNull Map<String, String> props) {
153       return Boolean.valueOf(props.remove("patcher.verboseTreeWalkLog"));
154     }
155
156     private String readFromRevision(@NotNull Map<String, String> props) {
157       return props.remove(Constants.PATCHER_FROM_REVISION);
158     }
159
160     private String readToRevision(@NotNull Map<String, String> props) {
161       String result = props.remove(Constants.PATCHER_TO_REVISION);
162       if (result == null)
163         throw new IllegalArgumentException("toRevision is not specified");
164       return result;
165     }
166
167     @NotNull
168     private CheckoutRules readCheckoutRules(@NotNull Map<String, String> props) {
169       String result = props.remove(Constants.PATCHER_CHECKOUT_RULES);
170       if (result == null)
171         throw new IllegalArgumentException("checkout rules are not specified");
172       return new CheckoutRules(result);
173     }
174
175     @NotNull
176     private File readGitCachesDir(@NotNull Map<String, String> props) {
177       String result = props.remove(Constants.PATCHER_CACHES_DIR);
178       if (result == null)
179         throw new IllegalArgumentException("git caches dir is not specified");
180       return new File(result);
181     }
182
183     @NotNull
184     private File readPatchFile(@NotNull Map<String, String> props) {
185       String result = props.remove(Constants.PATCHER_PATCH_FILE);
186       if (result == null)
187         throw new IllegalArgumentException("patch file is not specified");
188       return new File(result);
189     }
190
191     private byte[] readKeyBytes(@NotNull Map<String, String> props) {
192       String result = props.remove(Constants.PATCHER_UPLOADED_KEY);
193       if (result == null)
194         return null;
195       try {
196         return result.getBytes("UTF-8");
197       } catch (UnsupportedEncodingException e) {
198         return null;
199       }
200     }
201
202     private boolean readDebugEnabled(final Map<String, String> props) {
203       return "true".equals(props.remove(Constants.VCS_DEBUG_ENABLED));
204     }
205
206     @NotNull
207     private VcsRoot readRoot(@NotNull Map<String, String> props) {
208       return new VcsRootImpl(0, props);
209     }
210
211     @NotNull
212     private GitTrustStoreProvider readGitTrustStoreProvider(@NotNull Map<String, String> props) {
213       return new GitTrustStoreProviderStatic(props.get(Constants.GIT_TRUST_STORE_PROVIDER));
214     }
215
216
217     @NotNull
218     public File getInternalProperties() {
219       return myInternalProperties;
220     }
221
222     public boolean isVerboseTreeWalkLog() {
223       return myVerboseTreeWalkLog;
224     }
225
226     public String getFromRevision() {
227       return myFromRevision;
228     }
229
230     @NotNull
231     public String getToRevision() {
232       return myToRevision;
233     }
234
235     @NotNull
236     public CheckoutRules getCheckoutRules() {
237       return myCheckoutRules;
238     }
239
240     @NotNull
241     public File getGitCachesDir() {
242       return myGitCachesDir;
243     }
244
245     @NotNull
246     public File getPatchFile() {
247       return myPatchFile;
248     }
249
250     @Nullable
251     public byte[] getKeyBytes() {
252       return myKeyBytes;
253     }
254
255     public boolean isDebugEnabled() {
256       return myDebugEnabled;
257     }
258
259     @NotNull
260     public VcsRoot getRoot() {
261       return myRoot;
262     }
263
264     @NotNull
265     public GitTrustStoreProvider getGitTrustStoreProvider() {
266       return myGitTrustStoreProvider;
267     }
268   }
269
270   private static boolean isImportant(Throwable t) {
271     return t instanceof NullPointerException ||
272            t instanceof Error ||
273            t instanceof InterruptedException ||
274            t instanceof InterruptedIOException;
275   }
276
277
278   private final static class PrintFile extends PatchFileAction {
279     @Override
280     void call(@NotNull final String action, @NotNull final String file) {
281       System.out.println(action + " " + file);
282     }
283   }
284 }