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