-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
GitToolChooser.java
181 lines (160 loc) · 6.37 KB
/
GitToolChooser.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package jenkins.plugins.git;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.EnvVars;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.TaskListener;
import hudson.plugins.git.GitTool;
import hudson.plugins.git.util.GitUtils;
import jenkins.model.Jenkins;
import org.apache.commons.io.FileUtils;
import org.jenkinsci.plugins.gitclient.Git;
import org.jenkinsci.plugins.gitclient.GitClient;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* A class which allows Git Plugin to choose a git implementation by estimating the size of a repository from a distance
* without requiring a local checkout.
*/
public class GitToolChooser {
private long sizeOfRepo = 0L;
private String implementation;
private String gitTool;
/**
* Size to switch implementation in KiB
*/
public static final int SIZE_TO_SWITCH = 5000;
/**
* Instantiate class using {@link AbstractGitSCMSource}. It looks for a cached .git directory first, calculates the
* size if it is found else checks if the extension point has been implemented and asks for the size.
* @param source the {@link AbstractGitSCMSource}
* @throws IOException
* @throws InterruptedException
*/
public GitToolChooser(@NonNull AbstractGitSCMSource source) throws IOException, InterruptedException {
boolean useCache;
boolean useAPI = false;
implementation = determineSwitchOnSize(sizeOfRepo);
useCache = setSizeFromCache(source);
if (useCache) {
implementation = determineSwitchOnSize(sizeOfRepo);
} else {
useAPI = setSizeFromAPI(source.getRemote());
}
if (useAPI) {
implementation = determineSwitchOnSize(sizeOfRepo);
}
determineGitTool(implementation);
}
/**
* Estimate size of a repository using the extension point
* @param remoteName: The URL of the repository
*/
public GitToolChooser(String remoteName) {
implementation = determineSwitchOnSize(sizeOfRepo);
boolean useAPI = setSizeFromAPI(remoteName);
if (useAPI) {
implementation = determineSwitchOnSize(sizeOfRepo);
}
determineGitTool(implementation);
}
/**
* Determine and estimate the size of a .git cached directory
* @param source: Use a {@link AbstractGitSCMSource} to access a cached Jenkins directory, we do not lock it.
* @return useCache
* @throws IOException
* @throws InterruptedException
*/
private boolean setSizeFromCache(@NonNull AbstractGitSCMSource source) throws IOException, InterruptedException {
boolean useCache = false;
String cacheEntry = source.getCacheEntry();
File cacheDir = AbstractGitSCMSource.getCacheDir(cacheEntry);
if (cacheDir != null) {
Git git = Git.with(TaskListener.NULL, new EnvVars(EnvVars.masterEnvVars)).in(cacheDir).using("git");
GitClient client = git.getClient();
if (client.hasGitRepo()) {
sizeOfRepo = FileUtils.sizeOfDirectory(cacheDir);
sizeOfRepo = (sizeOfRepo/1000); // Conversion from Bytes to Kilo Bytes
useCache = true;
}
}
return useCache;
}
/**
* Check if the desired implementation of extension is present and ask for the size of repository if it does
* @param repoUrl: The remote name derived from {@link GitSCMSource} object
* @return boolean useAPI or not.
*/
private boolean setSizeFromAPI(String repoUrl) {
List<RepositorySizeAPI> acceptedRepository = Objects.requireNonNull(RepositorySizeAPI.all())
.stream()
.filter(r -> r.isApplicableTo(repoUrl))
.collect(Collectors.toList());
if (acceptedRepository.size() > 0) {
try {
sizeOfRepo = acceptedRepository.get(0).getSizeOfRepository(repoUrl);
} catch (Exception e) {
LOGGER.log(Level.INFO, "Not using size API estimation based performance improvement");
return false;
}
return sizeOfRepo != 0; // Check if the size of the repository is zero
} else {
return false;
}
}
/**
* Recommend a git implementation on the basis of the given size of a repository
* @param sizeOfRepo: Size of a repository (in KiBs)
* @return a git implementation, "git" or "jgit"
*/
private String determineSwitchOnSize(Long sizeOfRepo) {
if (sizeOfRepo != 0L) {
if (sizeOfRepo >= SIZE_TO_SWITCH) {
return "git";
} else {
return "jgit";
}
}
return "NONE";
}
/**
* For a given recommended git implementation, validate if the installation exists and provide no suggestion if
* implementation doesn't exist.
* @param gitImplementation: The recommended git implementation, "git" or "jgit" on the basis of the heuristics.
*/
private void determineGitTool(String gitImplementation) {
if (gitImplementation.equals("NONE")) {
gitTool = "NONE";
return; // Recommend nothing (GitToolRecommendation = NONE)
}
final Jenkins jenkins = Jenkins.get();
GitTool tool = GitUtils.resolveGitTool(gitImplementation, jenkins, null, TaskListener.NULL);
if (tool != null) {
gitTool = tool.getGitExe();
}
}
/**
* Recommend git tool to be used by the git client
* @return git implementation recommendation in the form of a string
*/
public String getGitTool() {
return gitTool;
}
/**
* Other plugins can estimate the size of repository using this extension point
* The size is assumed to be in KiBs
*/
public static abstract class RepositorySizeAPI implements ExtensionPoint {
public abstract boolean isApplicableTo(String remote);
public abstract Long getSizeOfRepository(String remote) throws Exception;
public static ExtensionList<RepositorySizeAPI> all() {
return Jenkins.get().getExtensionList(RepositorySizeAPI.class);
}
}
private static final Logger LOGGER = Logger.getLogger(GitToolChooser.class.getName());
}