-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathJpm.java
More file actions
311 lines (284 loc) · 12.6 KB
/
Jpm.java
File metadata and controls
311 lines (284 loc) · 12.6 KB
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
package org.codejive.jpm;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import org.codejive.jpm.config.AppInfo;
import org.codejive.jpm.util.*;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.resolution.DependencyResolutionException;
/** The class implementing all the jpm command actions. */
public class Jpm {
private final Path directory;
private final boolean noLinks;
private final boolean verbose;
private Jpm(Path directory, boolean noLinks, boolean verbose) {
this.directory = directory;
this.noLinks = noLinks;
this.verbose = verbose;
}
/**
* Create a new {@link Builder} instance for the {@link Jpm} class.
*
* @return A new {@link Builder} instance.
*/
public static Builder builder() {
return new Builder();
}
/** Builder class for the {@link Jpm} class. */
public static class Builder {
private Path directory;
private boolean noLinks;
private boolean verbose;
private Builder() {}
/**
* Set the target directory to use for the jpm commands.
*
* @param directory The target directory.
* @return The builder instance for chaining.
*/
public Builder directory(Path directory) {
this.directory = directory;
return this;
}
/**
* Set whether to create symbolic links or not.
*
* @param noLinks Whether to create symbolic links or not.
* @return The builder instance for chaining.
*/
public Builder noLinks(boolean noLinks) {
this.noLinks = noLinks;
return this;
}
/**
* Set whether to enable verbose output or not.
*
* @param verbose Whether to enable verbose output or not.
* @return The builder instance for chaining.
*/
public Builder verbose(boolean verbose) {
this.verbose = verbose;
return this;
}
/**
* Builds the {@link Jpm} instance.
*
* @return A {@link Jpm} instance.
*/
public Jpm build() {
return new Jpm(directory, noLinks, verbose);
}
}
/**
* Copies the given artifacts to the target directory.
*
* @param artifactNames The artifacts to copy.
* @param sync Whether to sync the target directory or not.
* @return An instance of {@link SyncResult} containing the statistics of the copy operation.
* @throws IOException If an error occurred during the copy operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncResult copy(String[] artifactNames, boolean sync)
throws IOException, DependencyResolutionException {
return copy(artifactNames, Collections.emptyMap(), sync);
}
/**
* Copies the given artifacts to the target directory.
*
* @param artifactNames The artifacts to copy.
* @param repos A map of additional repository names to URLs where artifacts can be found.
* @param sync Whether to sync the target directory or not.
* @return An instance of {@link SyncResult} containing the statistics of the copy operation.
* @throws IOException If an error occurred during the copy operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncResult copy(String[] artifactNames, Map<String, String> repos, boolean sync)
throws IOException, DependencyResolutionException {
List<Path> files = Resolver.create(artifactNames, repos).resolvePaths();
return FileUtils.syncArtifacts(files, directory, noLinks, !sync);
}
/**
* Searches for artifacts matching the given pattern.
*
* @param artifactPattern The pattern to search for.
* @param count The maximum number of results to return.
* @return An array of artifact names matching the given pattern.
* @throws IOException If an error occurred during the search.
*/
public String[] search(String artifactPattern, int count) throws IOException {
List<Artifact> artifacts = new ArrayList<>();
int max = count <= 0 || count > 200 ? 200 : count;
SearchResult result = SearchUtils.findArtifacts(artifactPattern, max);
while (result != null) {
artifacts.addAll(result.artifacts);
result = count <= 0 ? SearchUtils.findNextArtifacts(result) : null;
}
return artifacts.stream().map(Jpm::artifactGav).toArray(String[]::new);
}
private static String artifactGav(Artifact artifact) {
return artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion();
}
/**
* Installs the given artifacts to the target directory while also registering them as
* dependencies in the app.yml file in the current directory. If no artifacts are given, all
* dependencies in the app.yml file will be installed. NB: "installation" in this context
* basically means sync-copying the artifacts to the target directory.
*
* @param artifactNames The artifacts to install.
* @return An instance of {@link SyncResult} containing the statistics of the install operation.
* @throws IOException If an error occurred during the install operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncResult install(String[] artifactNames)
throws IOException, DependencyResolutionException {
return install(artifactNames, Collections.emptyMap());
}
/**
* Installs the given artifacts to the target directory while also registering them as
* dependencies in the app.yml file in the current directory. If no artifacts are given, all
* dependencies in the app.yml file will be installed. NB: "installation" in this context
* basically means sync-copying the artifacts to the target directory.
*
* @param artifactNames The artifacts to install.
* @param extraRepos A map of additional repository names to URLs where artifacts can be found.
* @return An instance of {@link SyncResult} containing the statistics of the install operation.
* @throws IOException If an error occurred during the install operation.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
*/
public SyncResult install(String[] artifactNames, Map<String, String> extraRepos)
throws IOException, DependencyResolutionException {
AppInfo appInfo = AppInfo.read();
String[] artifacts = getArtifacts(artifactNames, appInfo);
Map<String, String> repos = getRepositories(extraRepos, appInfo);
if (artifacts.length > 0) {
List<Path> files = Resolver.create(artifacts, repos).resolvePaths();
SyncResult stats = FileUtils.syncArtifacts(files, directory, noLinks, true);
if (artifactNames.length > 0) {
for (String dep : artifactNames) {
int p = dep.lastIndexOf(':');
String name = dep.substring(0, p);
String version = dep.substring(p + 1);
appInfo.dependencies.put(name, version);
}
appInfo.repositories.putAll(repos);
AppInfo.write(appInfo);
}
return stats;
} else {
return new SyncResult();
}
}
/**
* Returns the paths of the given artifacts. If no artifacts are given, the paths for all
* dependencies in the app.yml file will be returned instead.
*
* @param artifactNames The artifacts to get the paths for.
* @return A list of paths.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
* @throws IOException If an error occurred during the operation.
*/
public List<Path> path(String[] artifactNames)
throws DependencyResolutionException, IOException {
return path(artifactNames, Collections.emptyMap());
}
/**
* Returns the paths of the given artifacts. If no artifacts are given, the paths for all
* dependencies in the app.yml file will be returned instead.
*
* @param artifactNames The artifacts to get the paths for.
* @param extraRepos A map of additional repository names to URLs where artifacts can be found.
* @return A list of paths.
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
* @throws IOException If an error occurred during the operation.
*/
public List<Path> path(String[] artifactNames, Map<String, String> extraRepos)
throws DependencyResolutionException, IOException {
AppInfo appInfo = AppInfo.read();
String[] deps = getArtifacts(artifactNames, appInfo);
Map<String, String> repos = getRepositories(extraRepos, appInfo);
if (deps.length > 0) {
List<Path> files = Resolver.create(deps, repos).resolvePaths();
if (artifactNames.length > 0) {
return files;
} else {
SyncResult result = FileUtils.syncArtifacts(files, directory, noLinks, true);
return result.files;
}
} else {
return Collections.emptyList();
}
}
private static String[] getArtifacts(String[] artifactNames, AppInfo appInfo) {
String[] deps;
if (artifactNames.length > 0) {
deps = artifactNames;
} else {
deps = appInfo.getDependencyGAVs();
}
return deps;
}
private Map<String, String> getRepositories(Map<String, String> extraRepos, AppInfo appInfo) {
Map<String, String> repos = new HashMap<>(appInfo.repositories);
repos.putAll(extraRepos);
return repos;
}
/**
* Executes an action defined in app.yml file.
*
* @param actionName The name of the action to execute
* @return An integer containing the exit result of the action
* @throws IllegalArgumentException If the action name is not provided or not found
* @throws IOException If an error occurred during the operation
* @throws DependencyResolutionException If an error occurred during dependency resolution
* @throws InterruptedException If the action execution was interrupted
*/
public int executeAction(String actionName, List<String> args)
throws IOException, DependencyResolutionException, InterruptedException {
AppInfo appInfo = AppInfo.read();
// Get the action command
String command = appInfo.getAction(actionName);
if (command == null) {
throw new IllegalArgumentException(
"Action '"
+ actionName
+ "' not found in app.yml. Use --list to see available actions.");
}
// Add the user arguments to the command
if (args != null && !args.isEmpty()) {
command +=
args.stream()
.map(ScriptUtils::quoteArgument)
.collect(Collectors.joining(" ", " ", ""));
}
return executeCommand(command);
}
/**
* Returns a list of available action names defined in the app.yml file.
*
* @return A list of available action names
* @throws IOException If an error occurred during the operation
*/
public List<String> listActions() throws IOException {
AppInfo appInfo = AppInfo.read();
return new ArrayList<>(appInfo.getActionNames());
}
/**
* Executes an action defined in app.yml file.
*
* @param command The command to execute
* @return An integer containing the exit result of the action
* @throws IOException If an error occurred during the operation
* @throws DependencyResolutionException If an error occurred during dependency resolution
* @throws InterruptedException If the action execution was interrupted
*/
public int executeCommand(String command)
throws IOException, DependencyResolutionException, InterruptedException {
// Get the classpath for variable substitution only if needed
List<Path> classpath = Collections.emptyList();
if (command.contains("{{deps}}")) {
classpath = this.path(new String[0]); // Empty array means use dependencies from app.yml
}
return ScriptUtils.executeScript(command, classpath, verbose);
}
}