Skip to content

Commit 8e65772

Browse files
committed
feat: added support for defining additional repositories
1 parent 840e9da commit 8e65772

File tree

5 files changed

+152
-34
lines changed

5 files changed

+152
-34
lines changed

src/main/java/org/codejive/jpm/Jpm.java

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import java.io.IOException;
44
import java.nio.file.Path;
55
import java.util.*;
6-
import org.codejive.jpm.json.AppInfo;
6+
import org.codejive.jpm.config.AppInfo;
77
import org.codejive.jpm.util.*;
88
import org.eclipse.aether.artifact.Artifact;
99
import org.eclipse.aether.resolution.DependencyResolutionException;
@@ -91,7 +91,22 @@ public Jpm build() {
9191
*/
9292
public SyncStats copy(String[] artifactNames, boolean sync)
9393
throws IOException, DependencyResolutionException {
94-
List<Path> files = Resolver.create(artifactNames).resolvePaths();
94+
return copy(artifactNames, Collections.emptyMap(), sync);
95+
}
96+
97+
/**
98+
* Copies the given artifacts to the target directory.
99+
*
100+
* @param artifactNames The artifacts to copy.
101+
* @param repos A map of additional repository names to URLs where artifacts can be found.
102+
* @param sync Whether to sync the target directory or not.
103+
* @return An instance of {@link SyncStats} containing the statistics of the copy operation.
104+
* @throws IOException If an error occurred during the copy operation.
105+
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
106+
*/
107+
public SyncStats copy(String[] artifactNames, Map<String, String> repos, boolean sync)
108+
throws IOException, DependencyResolutionException {
109+
List<Path> files = Resolver.create(artifactNames, repos).resolvePaths();
95110
return FileUtils.syncArtifacts(files, directory, noLinks, !sync);
96111
}
97112

@@ -131,10 +146,28 @@ private static String artifactGav(Artifact artifact) {
131146
*/
132147
public SyncStats install(String[] artifactNames)
133148
throws IOException, DependencyResolutionException {
149+
return install(artifactNames, Collections.emptyMap());
150+
}
151+
152+
/**
153+
* Installs the given artifacts to the target directory while also registering them as
154+
* dependencies in the app.yml file in the current directory. If no artifacts are given, all
155+
* dependencies in the app.yml file will be installed. NB: "installation" in this context
156+
* basically means sync-copying the artifacts to the target directory.
157+
*
158+
* @param artifactNames The artifacts to install.
159+
* @param extraRepos A map of additional repository names to URLs where artifacts can be found.
160+
* @return An instance of {@link SyncStats} containing the statistics of the install operation.
161+
* @throws IOException If an error occurred during the install operation.
162+
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
163+
*/
164+
public SyncStats install(String[] artifactNames, Map<String, String> extraRepos)
165+
throws IOException, DependencyResolutionException {
134166
AppInfo appInfo = AppInfo.read();
135167
String[] artifacts = getArtifacts(artifactNames, appInfo);
168+
Map<String, String> repos = getRepositories(extraRepos, appInfo);
136169
if (artifacts.length > 0) {
137-
List<Path> files = Resolver.create(artifacts).resolvePaths();
170+
List<Path> files = Resolver.create(artifacts, repos).resolvePaths();
138171
SyncStats stats = FileUtils.syncArtifacts(files, directory, noLinks, true);
139172
if (artifactNames.length > 0) {
140173
for (String dep : artifactNames) {
@@ -162,10 +195,26 @@ public SyncStats install(String[] artifactNames)
162195
*/
163196
public List<Path> path(String[] artifactNames)
164197
throws DependencyResolutionException, IOException {
198+
return path(artifactNames, Collections.emptyMap());
199+
}
200+
201+
/**
202+
* Returns the paths of the given artifacts. If no artifacts are given, the paths for all
203+
* dependencies in the app.yml file will be returned instead.
204+
*
205+
* @param artifactNames The artifacts to get the paths for.
206+
* @param extraRepos A map of additional repository names to URLs where artifacts can be found.
207+
* @return A list of paths.
208+
* @throws DependencyResolutionException If an error occurred during the dependency resolution.
209+
* @throws IOException If an error occurred during the operation.
210+
*/
211+
public List<Path> path(String[] artifactNames, Map<String, String> extraRepos)
212+
throws DependencyResolutionException, IOException {
165213
AppInfo appInfo = AppInfo.read();
166214
String[] deps = getArtifacts(artifactNames, appInfo);
215+
Map<String, String> repos = getRepositories(extraRepos, appInfo);
167216
if (deps.length > 0) {
168-
return Resolver.create(deps).resolvePaths();
217+
return Resolver.create(deps, repos).resolvePaths();
169218
} else {
170219
return Collections.emptyList();
171220
}
@@ -181,6 +230,12 @@ private static String[] getArtifacts(String[] artifactNames, AppInfo appInfo) {
181230
return deps;
182231
}
183232

233+
private Map<String, String> getRepositories(Map<String, String> extraRepos, AppInfo appInfo) {
234+
Map<String, String> repos = new HashMap<>(appInfo.repositories);
235+
repos.putAll(extraRepos);
236+
return repos;
237+
}
238+
184239
/**
185240
* Executes an action defined in app.yml file.
186241
*

src/main/java/org/codejive/jpm/Main.java

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
//DEPS org.yaml:snakeyaml:2.4
55
//DEPS org.jline:jline-console-ui:3.30.5 org.jline:jline-terminal-jni:3.30.5
66
//DEPS org.slf4j:slf4j-api:2.0.17 org.slf4j:slf4j-simple:2.0.17
7-
//SOURCES Jpm.java json/AppInfo.java util/FileUtils.java util/Resolver.java util/ScriptUtils.java
7+
//SOURCES Jpm.java config/AppInfo.java util/FileUtils.java util/Resolver.java util/ScriptUtils.java
88
//SOURCES util/SearchResult.java util/SearchUtils.java util/SyncStats.java util/Version.java
99
// spotless:on
1010

1111
package org.codejive.jpm;
1212

1313
import java.io.*;
14+
import java.net.MalformedURLException;
15+
import java.net.URL;
1416
import java.nio.file.Path;
1517
import java.util.*;
1618
import java.util.concurrent.Callable;
@@ -80,7 +82,10 @@ public Integer call() throws Exception {
8082
.directory(artifactsMixin.depsMixin.directory)
8183
.noLinks(artifactsMixin.depsMixin.noLinks)
8284
.build()
83-
.copy(artifactsMixin.artifactNames, sync);
85+
.copy(
86+
artifactsMixin.artifactNames,
87+
artifactsMixin.getRepositoryMap(),
88+
sync);
8489
if (!quietMixin.quiet) {
8590
printStats(stats);
8691
}
@@ -285,7 +290,9 @@ public Integer call() throws Exception {
285290
.directory(optionalArtifactsMixin.depsMixin.directory)
286291
.noLinks(optionalArtifactsMixin.depsMixin.noLinks)
287292
.build()
288-
.install(optionalArtifactsMixin.artifactNames);
293+
.install(
294+
optionalArtifactsMixin.artifactNames,
295+
optionalArtifactsMixin.getRepositoryMap());
289296
if (!quietMixin.quiet) {
290297
printStats(stats);
291298
}
@@ -310,7 +317,9 @@ public Integer call() throws Exception {
310317
.directory(optionalArtifactsMixin.depsMixin.directory)
311318
.noLinks(optionalArtifactsMixin.depsMixin.noLinks)
312319
.build()
313-
.path(optionalArtifactsMixin.artifactNames);
320+
.path(
321+
optionalArtifactsMixin.artifactNames,
322+
optionalArtifactsMixin.getRepositoryMap());
314323
if (!files.isEmpty()) {
315324
String classpath =
316325
files.stream()
@@ -497,9 +506,37 @@ static class DepsMixin {
497506
boolean noLinks;
498507
}
499508

500-
static class ArtifactsMixin {
509+
static class BaseArtifactsMixin {
501510
@Mixin DepsMixin depsMixin;
502511

512+
@Option(
513+
names = {"-r", "--repo"},
514+
description =
515+
"URL to additional repository to use when resolving artifacts. Can be preceded by a name and an equals sign, e.g. -r myrepo=https://my.repo.com/maven2. When needing to pass user and password you can set JPM_REPO_<name>_USER and JPM_REPO_<name>_PASSWORD environment variables.")
516+
List<String> repositories = new ArrayList<>();
517+
518+
Map<String, String> getRepositoryMap() {
519+
Map<String, String> repoMap = new HashMap<>();
520+
for (String repo : repositories) {
521+
int eq = repo.indexOf('=');
522+
if (eq > 0) {
523+
repoMap.put(repo.substring(0, eq), repo.substring(eq + 1));
524+
} else {
525+
String name = repo;
526+
try {
527+
URL url = new URL(repo);
528+
name = url.getHost();
529+
} catch (MalformedURLException e) {
530+
// Ignore
531+
}
532+
repoMap.put(name, repo);
533+
}
534+
}
535+
return repoMap;
536+
}
537+
}
538+
539+
static class ArtifactsMixin extends BaseArtifactsMixin {
503540
@Parameters(
504541
paramLabel = "artifacts",
505542
description =
@@ -508,9 +545,7 @@ static class ArtifactsMixin {
508545
private String[] artifactNames = {};
509546
}
510547

511-
static class OptionalArtifactsMixin {
512-
@Mixin DepsMixin depsMixin;
513-
548+
static class OptionalArtifactsMixin extends BaseArtifactsMixin {
514549
@Parameters(
515550
paramLabel = "artifacts",
516551
description =

src/main/java/org/codejive/jpm/json/AppInfo.java renamed to src/main/java/org/codejive/jpm/config/AppInfo.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.codejive.jpm.json;
1+
package org.codejive.jpm.config;
22

33
import java.io.IOException;
44
import java.io.Reader;
@@ -17,7 +17,9 @@
1717
*/
1818
public class AppInfo {
1919
private Map<String, Object> yaml = new TreeMap<>();
20+
2021
public Map<String, String> dependencies = new TreeMap<>();
22+
public Map<String, String> repositories = new TreeMap<>();
2123
public Map<String, String> actions = new TreeMap<>();
2224

2325
/** The official name of the app.yml file. */
@@ -41,7 +43,7 @@ public String[] getDependencyGAVs() {
4143
* @return The action command or null if not found
4244
*/
4345
public String getAction(String actionName) {
44-
return actions.get(actionName);
46+
return actions != null ? actions.get(actionName) : null;
4547
}
4648

4749
/**
@@ -50,7 +52,7 @@ public String getAction(String actionName) {
5052
* @return A set of action names
5153
*/
5254
public java.util.Set<String> getActionNames() {
53-
return actions.keySet();
55+
return actions != null ? actions.keySet() : java.util.Collections.emptySet();
5456
}
5557

5658
/**
@@ -80,6 +82,14 @@ public static AppInfo read() throws IOException {
8082
appInfo.dependencies.put(entry.getKey(), entry.getValue().toString());
8183
}
8284
}
85+
// Parse repositories section
86+
if (appInfo.yaml.containsKey("repositories")
87+
&& appInfo.yaml.get("repositories") instanceof Map) {
88+
Map<String, Object> deps = (Map<String, Object>) appInfo.yaml.get("repositories");
89+
for (Map.Entry<String, Object> entry : deps.entrySet()) {
90+
appInfo.repositories.put(entry.getKey(), entry.getValue().toString());
91+
}
92+
}
8393
// Parse actions section
8494
if (appInfo.yaml.containsKey("actions") && appInfo.yaml.get("actions") instanceof Map) {
8595
Map<String, Object> actions = (Map<String, Object>) appInfo.yaml.get("actions");
@@ -105,7 +115,16 @@ public static void write(AppInfo appInfo) throws IOException {
105115
Yaml yaml = new Yaml(dopts);
106116
// WARNING awful code ahead
107117
appInfo.yaml.put("dependencies", (Map<String, Object>) (Map) appInfo.dependencies);
108-
appInfo.yaml.put("actions", (Map<String, Object>) (Map) appInfo.actions);
118+
if (!appInfo.repositories.isEmpty()) {
119+
appInfo.yaml.put("repositories", (Map<String, Object>) (Map) appInfo.repositories);
120+
} else {
121+
appInfo.yaml.remove("repositories");
122+
}
123+
if (!appInfo.actions.isEmpty()) {
124+
appInfo.yaml.put("actions", (Map<String, Object>) (Map) appInfo.actions);
125+
} else {
126+
appInfo.yaml.remove("actions");
127+
}
109128
yaml.dump(appInfo.yaml, out);
110129
}
111130
}

src/main/java/org/codejive/jpm/util/Resolver.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
import eu.maveniverse.maven.mima.context.Runtimes;
77
import java.nio.file.Path;
88
import java.util.Arrays;
9+
import java.util.Collections;
910
import java.util.List;
11+
import java.util.Map;
1012
import java.util.stream.Collectors;
1113
import org.eclipse.aether.artifact.Artifact;
1214
import org.eclipse.aether.artifact.DefaultArtifact;
1315
import org.eclipse.aether.collection.CollectRequest;
1416
import org.eclipse.aether.graph.Dependency;
17+
import org.eclipse.aether.repository.RemoteRepository;
1518
import org.eclipse.aether.resolution.ArtifactResult;
1619
import org.eclipse.aether.resolution.DependencyRequest;
1720
import org.eclipse.aether.resolution.DependencyResolutionException;
@@ -20,20 +23,22 @@
2023

2124
public class Resolver {
2225
private final List<Artifact> artifacts;
26+
private final List<RemoteRepository> repositories;
2327

2428
private List<ArtifactResult> resolvedArtifacts;
2529

26-
public static Resolver create(String[] artifactNames) {
27-
return new Resolver(artifactNames);
30+
public static Resolver create(String[] artifactNames, Map<String, String> repositories) {
31+
return new Resolver(artifactNames, repositories);
2832
}
2933

30-
private Resolver(String[] artifactNames) {
34+
private Resolver(String[] artifactNames, Map<String, String> repos) {
3135
artifacts = parseArtifacts(artifactNames);
36+
repositories = parseRepositories(repos);
3237
}
3338

3439
public List<ArtifactResult> resolve() throws DependencyResolutionException {
3540
if (resolvedArtifacts == null) {
36-
resolvedArtifacts = resolveArtifacts(artifacts);
41+
resolvedArtifacts = resolveArtifacts(artifacts, repositories);
3742
}
3843
return resolvedArtifacts;
3944
}
@@ -45,31 +50,22 @@ public List<Path> resolvePaths() throws DependencyResolutionException {
4550
.collect(Collectors.toList());
4651
}
4752

48-
/**
49-
* Parses the given artifact names into a list of {@link Artifact} instances.
50-
*
51-
* @param artifactNames the artifact names to parse as an array of strings in the format
52-
* "groupId:artifactId:version"
53-
* @return a list of {@link Artifact} instances
54-
*/
55-
public static List<Artifact> parseArtifacts(String[] artifactNames) {
56-
return Arrays.stream(artifactNames).map(DefaultArtifact::new).collect(Collectors.toList());
57-
}
58-
5953
/**
6054
* Resolves the given artifacts.
6155
*
6256
* @param artifacts the artifacts to resolve as a list of {@link Artifact} instances
6357
* @return the resolved artifacts as a list of {@link ArtifactResult} instances
6458
* @throws DependencyResolutionException if an error occurs while resolving the artifacts
6559
*/
66-
public static List<ArtifactResult> resolveArtifacts(List<Artifact> artifacts)
60+
public static List<ArtifactResult> resolveArtifacts(
61+
List<Artifact> artifacts, List<RemoteRepository> repositories)
6762
throws DependencyResolutionException {
6863
List<Dependency> dependencies =
6964
artifacts.stream()
7065
.map(a -> new Dependency(a, JavaScopes.RUNTIME))
7166
.collect(Collectors.toList());
72-
ContextOverrides overrides = ContextOverrides.create().build();
67+
ContextOverrides overrides =
68+
ContextOverrides.create().withUserSettings(true).repositories(repositories).build();
7369
Runtime runtime = Runtimes.INSTANCE.getRuntime();
7470
try (Context context = runtime.create(overrides)) {
7571
CollectRequest collectRequest =
@@ -86,4 +82,17 @@ public static List<ArtifactResult> resolveArtifacts(List<Artifact> artifacts)
8682
return dependencyResult.getArtifactResults();
8783
}
8884
}
85+
86+
private static List<Artifact> parseArtifacts(String[] artifactNames) {
87+
return Arrays.stream(artifactNames).map(DefaultArtifact::new).collect(Collectors.toList());
88+
}
89+
90+
private static List<RemoteRepository> parseRepositories(Map<String, String> repositories) {
91+
if (repositories == null) {
92+
return Collections.emptyList();
93+
}
94+
return repositories.entrySet().stream()
95+
.map(e -> new RemoteRepository.Builder(e.getKey(), "default", e.getValue()).build())
96+
.collect(Collectors.toList());
97+
}
8998
}

src/test/java/org/codejive/jpm/json/AppInfoTest.java renamed to src/test/java/org/codejive/jpm/config/AppInfoTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.codejive.jpm.json;
1+
package org.codejive.jpm.config;
22

33
import static org.assertj.core.api.Assertions.*;
44

0 commit comments

Comments
 (0)