Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
java-version-file: .java-version

- name: Set up ja
uses: Netflix/ja/.github/actions/setup-ja@v0.18.2
uses: Netflix/ja/.github/actions/setup-ja@v0.21.1

- name: Test
run: ja test --all
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: echo "BUILD_VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_ENV"

- name: Set up ja
uses: Netflix/ja/.github/actions/setup-ja@v0.18.2
uses: Netflix/ja/.github/actions/setup-ja@v0.21.1

- name: Test
run: ja test --all
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import javax.tools.ToolProvider;

Expand All @@ -45,6 +47,86 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

class MavenDeploymentTest {
@Test
void publishesNamedAutomaticModule(@TempDir Path directory) throws Exception {
Path artifacts = Files.createDirectory(directory.resolve("artifacts"));
createAutomaticModuleJar("com.example.application",
artifacts.resolve("com.example.application.jar"));
Files.writeString(artifacts.resolve("com.example.application.pom"),
"""
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>com.example.application</artifactId>
<version>1.2.3</version>
<packaging>jar</packaging>
<name>Example application</name>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>example-library</artifactId>
<version>4.5.6</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.acme</groupId>
<artifactId>com.acme.library</artifactId>
<version>7.8.9</version>
</dependency>
<dependency>
<groupId>java.sql</groupId>
<artifactId>java.sql</artifactId>
<version>25</version>
</dependency>
</dependencies>
</project>
""");
Path repository = directory.resolve("repository");
createMavenAutomaticModule(repository, "com.acme", "library", "7.8.9",
"com.acme.library");
var upstream = new Builder("test", "default", repository.toUri().toString()).build();

try (var session = ModuleRepositorySession.create(directory.resolve("session"), List.of(upstream));
var deployment = MavenDeployment.create(artifacts, session, "1.2.3")) {
Map<String, Artifact> deployed = deployment.artifacts().stream()
.collect(Collectors.toMap(artifact -> artifact.getExtension() + ":" + artifact.getClassifier(), artifact -> artifact));
assertEquals(Set.of("pom:", "jar:"), deployed.keySet());
Artifact jar = deployed.get("jar:");
assertEquals("com.example", jar.getGroupId());
assertEquals("com.example.application", jar.getArtifactId());
assertEquals("1.2.3", jar.getVersion());
Model consumer = readModel(deployed.get("pom:").getPath());
assertEquals("Example application", consumer.getName());
assertEquals(2, consumer.getDependencies().size());
var ordinary = consumer.getDependencies().stream()
.filter(dependency -> dependency.getArtifactId().equals("example-library"))
.findFirst()
.orElseThrow();
assertEquals("org.example", ordinary.getGroupId());
assertEquals("4.5.6", ordinary.getVersion());
var module = consumer.getDependencies().stream()
.filter(dependency -> dependency.getArtifactId().equals("library"))
.findFirst()
.orElseThrow();
assertEquals("com.acme", module.getGroupId());
assertEquals("7.8.9", module.getVersion());
}
}

@Test
void rejectsAutomaticModuleWithFilenameDerivedName(@TempDir Path directory) throws Exception {
Path artifacts = Files.createDirectory(directory.resolve("artifacts"));
try (var _ = new JarOutputStream(Files.newOutputStream(
artifacts.resolve("com.example.application.jar")))) {}

try (var session = ModuleRepositorySession.create(directory.resolve("session"), List.of())) {
var failure = assertThrows(IllegalArgumentException.class,
() -> MavenDeployment.create(artifacts, session, "1.2.3"));
assertEquals("Automatic module has no Automatic-Module-Name: com.example.application.jar",
failure.getMessage());
}
}

@Test
void createsConsumerPomFromFlatModuleArtifacts(@TempDir Path directory) throws Exception {
Path artifacts = Files.createDirectory(directory.resolve("artifacts"));
Expand Down Expand Up @@ -137,6 +219,25 @@ void appliesPublicationMetadataToTheMatchingModule(@TempDir Path directory) thro
}
}

@Test
void deploysNamedAutomaticModule(@TempDir Path directory) throws Exception {
Path artifacts = Files.createDirectory(directory.resolve("artifacts"));
createAutomaticModuleJar("com.example.application",
artifacts.resolve("com.example.application.jar"));
Path repository = directory.resolve("deployed");
var output = new StringWriter();
var errors = new StringWriter();

int result = new Jig().run(new PrintWriter(output, true), new PrintWriter(errors, true),
"maven", "deploy", "--module-version", "1.2.3", "--repository",
repository.toString(), artifacts.toString());

assertEquals(0, result, errors.toString());
Path deployed = repository.resolve("com/example/com.example.application/1.2.3");
assertTrue(Files.isRegularFile(deployed.resolve("com.example.application-1.2.3.jar")));
assertTrue(Files.isRegularFile(deployed.resolve("com.example.application-1.2.3.pom")));
}

@Test
void deploysToARepositorySelectedByPath(@TempDir Path directory) throws Exception {
Path artifacts = Files.createDirectory(directory.resolve("artifacts"));
Expand Down Expand Up @@ -256,6 +357,37 @@ private static void createModuleJar(Path directory, String moduleName, Path jar)
.name());
}

private static void createAutomaticModuleJar(String moduleName, Path jar) throws Exception {
var manifest = new Manifest();
manifest.getMainAttributes().put(Name.MANIFEST_VERSION, "1.0");
manifest.getMainAttributes().putValue("Automatic-Module-Name", moduleName);
try (var _ = new JarOutputStream(Files.newOutputStream(jar), manifest)) {}
assertTrue(ModuleFinder.of(jar)
.find(moduleName)
.orElseThrow()
.descriptor()
.isAutomatic());
}

private static void createMavenAutomaticModule(Path repository, String groupId,
String artifactId, String version, String moduleName) throws Exception {
Path directory = repository.resolve(groupId.replace('.', '/'))
.resolve(artifactId)
.resolve(version);
Files.createDirectories(directory);
createAutomaticModuleJar(moduleName,
directory.resolve(artifactId + "-" + version + ".jar"));
Files.writeString(directory.resolve(artifactId + "-" + version + ".pom"),
"""
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>%s</groupId>
<artifactId>%s</artifactId>
<version>%s</version>
</project>
""".formatted(groupId, artifactId, version));
}

private static String metadata(String name) {
return """
<project xmlns="http://maven.apache.org/POM/4.0.0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* the License.
*/

package com.netflix.tools.jig.test.module.maven.transport;
package com.netflix.tools.jig.test.module;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
Expand Down Expand Up @@ -42,7 +42,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.netflix.tools.jig.module.maven.transport.AbstractModuleTransporter.ModuleIdentity;
import com.netflix.tools.jig.module.ModuleIdentity;
import com.netflix.tools.jig.module.maven.transport.ZipCentralDirectory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
Expand Down
2 changes: 1 addition & 1 deletion src/com.netflix.tools.jig.test/module-info.hash
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
org.apiguardian.api@1.1.2=module:sha256:8fe700256df67fb279c0734898bf7c25c199ef407ae18b918947b38dfdf0af2a
org.jspecify@1.0.0=module:sha256:db02318646febd4e03fc69910df454696d58b2be88e7232a0262c3fe0141bd03
org.jspecify=module:sha256:db02318646febd4e03fc69910df454696d58b2be88e7232a0262c3fe0141bd03
org.junit.jupiter.api@6.1.3=module:sha256:20c0d83d6f2882d9e5d49353c0f2c51acaa1b35d96ec7f3c242530ad0179b89a
org.junit.jupiter.engine@6.1.3=module:sha256:b6dff8c85dc1513bae171968a52a659aaa52dc31fda8b6e48e6e986d1601ed78
org.junit.jupiter.params@6.1.3=module:sha256:691c45b20325740fdb25dea947f22b267161a75a243a2f13ced3f06f88d8bb30
Expand Down
2 changes: 1 addition & 1 deletion src/com.netflix.tools.jig/com/netflix/tools/jig/Jig.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@
import com.netflix.tools.jig.module.ModuleRepositorySession;
import com.netflix.tools.jig.module.ModuleResolution;
import com.netflix.tools.jig.module.ModuleResolution.IntegrityMode;
import com.netflix.tools.jig.module.ModuleIdentity;
import com.netflix.tools.jig.module.SourceModule;
import com.netflix.tools.jig.module.SourceModuleFinder;
import com.netflix.tools.jig.module.SourceModuleReference;
import com.netflix.tools.jig.module.Trace;
import com.netflix.tools.jig.module.maven.transport.AbstractModuleTransporter.ModuleIdentity;
import com.netflix.tools.jig.module.maven.transport.TransporterHttpHandler;
import com.sun.net.httpserver.HttpServer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,17 @@ final class JigCommandLine {
private final ToolOption manualApproval = flag("--manual", "Wait for manual approval after validation");
private final CommandLine mavenInstallCommandLine = CommandLine.builder()
.description("Install flat module artifacts in the local Maven repository")
.option(moduleVersion)
.operand("ARTIFACT-DIRECTORY", "Directory of flat, module-named artifacts", Cardinality.EXACTLY_ONE)
.build();
private final CommandLine mavenDeployCommandLine = CommandLine.builder()
.description("Deploy flat module artifacts to a Maven repository")
.options(repository, sign)
.options(moduleVersion, repository, sign)
.operand("ARTIFACT-DIRECTORY", "Directory of flat, module-named artifacts", Cardinality.EXACTLY_ONE)
.build();
private final CommandLine mavenDeployCentralCommandLine = CommandLine.builder()
.description("Deploy flat module artifacts to Maven Central")
.options(deploymentName, manualApproval)
.options(moduleVersion, deploymentName, manualApproval)
.operand("ARTIFACT-DIRECTORY", "Directory of flat, module-named artifacts", Cardinality.EXACTLY_ONE)
.build();
private final CommandLine mavenCommandLine = CommandLine.builder()
Expand Down
29 changes: 22 additions & 7 deletions src/com.netflix.tools.jig/com/netflix/tools/jig/MavenCommands.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ static int run(PrintWriter out, PrintWriter err, Supplier<ModuleRepositorySessio
try {
Request request = parse(operation, Arrays.copyOfRange(arguments, 1, arguments.length));
try (var session = sessions.get();
var deployment = MavenDeployment.create(request.artifacts(), session)) {
var deployment = MavenDeployment.create(request.artifacts(), session,
request.moduleVersion())) {
if (operation.equals("install")) {
session.install(deployment.artifacts());
} else if (operation.equals("deploy")) {
Expand Down Expand Up @@ -97,11 +98,18 @@ private static Request parse(String operation, String[] arguments) {
Path artifacts = null;
RemoteRepository repository = null;
String name = null;
String moduleVersion = null;
boolean sign = false;
boolean manual = false;
for (int i = 0; i < arguments.length; i++) {
String argument = arguments[i];
if (argument.equals("--repository")) {
if (argument.equals("--module-version")) {
moduleVersion = moduleVersion(moduleVersion,
requireArgument(arguments, ++i, argument));
} else if (argument.startsWith("--module-version=")) {
moduleVersion = moduleVersion(moduleVersion,
argument.substring("--module-version=".length()));
} else if (argument.equals("--repository")) {
repository = repository(requireArgument(arguments, ++i, argument));
} else if (argument.startsWith("--repository=")) {
repository = repository(argument.substring("--repository=".length()));
Expand Down Expand Up @@ -136,7 +144,14 @@ private static Request parse(String operation, String[] arguments) {
if (!operation.equals("deploy-central") && manual) {
throw new IllegalArgumentException("--manual applies only to Maven deploy-central");
}
return new Request(artifacts, repository, name, sign, manual);
return new Request(artifacts, repository, name, moduleVersion, sign, manual);
}

private static String moduleVersion(String current, String value) {
if (current != null) {
throw new IllegalArgumentException("--module-version may only be specified once");
}
return Jig.Options.parseModuleVersion(value);
}

private static String requireArgument(String[] arguments, int index, String option) {
Expand Down Expand Up @@ -205,13 +220,13 @@ private static boolean isHelp(String argument) {
}

private static void printHelp(PrintWriter out) {
out.println("Usage: jig maven install <artifact-directory>");
out.println(" jig maven deploy --repository <id=uri|path> [--sign] <artifact-directory>");
out.println(" jig maven deploy-central [--name <name>] [--manual] <artifact-directory>");
out.println("Usage: jig maven install [--module-version <version>] <artifact-directory>");
out.println(" jig maven deploy [--module-version <version>] --repository <id=uri|path> [--sign] <artifact-directory>");
out.println(" jig maven deploy-central [--module-version <version>] [--name <name>] [--manual] <artifact-directory>");
out.println();
out.println("Installs or deploys flat, module-named artifacts.");
}

private record Request(Path artifacts, RemoteRepository repository, String name,
boolean sign, boolean manual) {}
String moduleVersion, boolean sign, boolean manual) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ public static Artifact locationCoordinate(String moduleName, String version) {
return new DefaultArtifact(moduleNamespace(moduleName), moduleName, "pom", version);
}

/** Returns whether an artifact uses the canonical coordinate for its module name. */
public static boolean isLocationCoordinate(Artifact artifact) {
return isLocationCoordinate(artifact.getGroupId(), artifact.getArtifactId());
}

/** Returns whether a group and artifact ID form the canonical coordinate for a module name. */
public static boolean isLocationCoordinate(String groupId, String artifactId) {
return artifactId != null
&& groupId != null
&& locationCoordinate(artifactId, null).getGroupId().equals(groupId);
}

/** Returns the coordinate used by the virtual module location repository. */
public static Artifact moduleLocationCoordinate(String moduleName, String version) {
return new DefaultArtifact(moduleNamespace(moduleName) + ".module", moduleName, "pom", version);
Expand Down
Loading
Loading