Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add manifest-subtract operation #14

Merged
merged 1 commit into from
Feb 16, 2024
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: 2 additions & 0 deletions src/main/java/org/wildfly/prospero/extras/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.wildfly.prospero.extras.manifest.diff.ManifestsDiffCommand;
import org.wildfly.prospero.extras.manifest.download.DownloadDiffCommand;
import org.wildfly.prospero.extras.manifest.merge.ManifestMergeCommand;
import org.wildfly.prospero.extras.manifest.subtract.ManifestSubtractCommand;
import org.wildfly.prospero.extras.repoository.RepositoryCommands;
import org.wildfly.prospero.extras.repository.create.DownloadArtifactListCommand;
import org.wildfly.prospero.extras.repository.create.DownloadRepositoryCommand;
Expand All @@ -44,6 +45,7 @@ private static CommandLine createCommandLine() {
commandLine.addSubcommand(new ManifestsDiffCommand());
commandLine.addSubcommand(new DownloadDiffCommand());
commandLine.addSubcommand(new ManifestMergeCommand());
commandLine.addSubcommand(new ManifestSubtractCommand());

commandLine.addSubcommand(new DownloadRepositoryCommand());
commandLine.addSubcommand(new DownloadArtifactListCommand());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,21 @@ ChannelManifest merge(ChannelManifest manifestOne, ChannelManifest manifestTwo,
VersionMergeStrategy.Strategies mergeStrategy,
String mergedManifestName, String mergedManifestId);

/**
* Subtracts streams of two manifests.
*
* Returns a manifest containing only streams from the first manifest that are The versions of artifacts in streams are ignored.
*
* The excluded streams are always included in the output manifest even if they are present in the second manifest.
*
* @param manifestOne - Initial manifest that the streams will be removed from.
* @param manifestTwo - Manifest containing streams to be removed.
* @param exclusions - list of excluded streams. To include all streams matching a group a wildcard syntax.
* @return
*/
ChannelManifest subtract(ChannelManifest manifestOne, ChannelManifest manifestTwo,
List<String> exclusions);

/**
* Performs a diff of {@code manifestOne} and {@code manifestTwo}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.wildfly.prospero.extras.manifest.diff.ManifestsDiffCommand;
import org.wildfly.prospero.extras.manifest.merge.ManifestMergeCommand;
import org.wildfly.prospero.extras.manifest.merge.VersionMergeStrategy;
import org.wildfly.prospero.extras.manifest.subtract.ManifestSubtractCommand;

import java.util.List;

Expand All @@ -17,6 +18,12 @@ public ChannelManifest merge(ChannelManifest manifestOne, ChannelManifest manife
return ManifestMergeCommand.merge(manifestOne, manifestTwo, mergeStrategy, mergedManifestName, mergedManifestId);
}

@Override
public ChannelManifest subtract(ChannelManifest manifestOne, ChannelManifest manifestTwo,
List<String> exclusions) {
return ManifestSubtractCommand.subtract(manifestOne, manifestTwo, exclusions);
}

@Override
public List<ArtifactChange> diff(ChannelManifest manifestOne, ChannelManifest manifestTwo) {
return ManifestsDiffCommand.manifestDiff(manifestOne, manifestTwo);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.wildfly.prospero.extras.manifest.subtract;

import org.wildfly.channel.ChannelManifest;
import org.wildfly.channel.ChannelManifestMapper;
import org.wildfly.channel.Stream;
import org.wildfly.prospero.extras.ReturnCodes;
import org.wildfly.prospero.extras.shared.CommandWithHelp;
import picocli.CommandLine;

import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@CommandLine.Command(name = "manifest-subtract")
public class ManifestSubtractCommand extends CommandWithHelp {

private static final Pattern EXCLUSION_PATTERN = Pattern.compile("[\\w-_.*]*:[\\w-_.*]*");

@CommandLine.Parameters(index = "0", descriptionKey = "parameterOne")
Path manifestOne;

@CommandLine.Parameters(index = "1", descriptionKey = "parameterTwo")
Path manifestTwo;

@CommandLine.Option(names = "--exclude", split = ",")
List<String> exclusions = Collections.emptyList();

@Override
public Integer call() throws Exception {
final ChannelManifest manifestOne = ChannelManifestMapper.from(this.manifestOne.toUri().toURL());
final ChannelManifest manifestTwo = ChannelManifestMapper.from(this.manifestTwo.toUri().toURL());

final ChannelManifest res = subtract(manifestOne, manifestTwo, exclusions);

System.out.println(ChannelManifestMapper.toYaml(res));

return ReturnCodes.SUCCESS;
}

public static ChannelManifest subtract(ChannelManifest manifestOne, ChannelManifest manifestTwo, List<String> exclusions) {
Objects.requireNonNull(manifestOne);
Objects.requireNonNull(manifestTwo);
Objects.requireNonNull(exclusions);

final Optional<String> illegalPattern = exclusions.stream()
.filter(e -> !EXCLUSION_PATTERN.matcher(e).matches())
.findFirst();
if (illegalPattern.isPresent()) {
throw new IllegalArgumentException("Exclusion [" + illegalPattern.get() + "] has invalid format ([\\w-_.*]*:[\\w-_.*]*).");
}

final Set<String> groupExclusions = exclusions.stream()
.map(String::trim)
.filter(e -> e.endsWith(":*"))
.map(e -> e.substring(0, e.length() - 2))
.collect(Collectors.toSet());

final Set<String> groupArtifactExclusions = exclusions.stream()
.map(String::trim)
.filter(e -> !e.endsWith(":*"))
.collect(Collectors.toSet());

final Set<String> streamKeys = manifestTwo.getStreams().stream()
.filter(s -> !groupExclusions.contains(s.getGroupId()))
.filter(s -> !groupArtifactExclusions.contains(s.getGroupId() + ":" + s.getArtifactId()))
.map(s -> getKey(s))
.collect(Collectors.toSet());


final List<Stream> filteredStreams = manifestOne.getStreams().stream()
.filter(s -> !streamKeys.contains(getKey(s)))
.collect(Collectors.toList());


return new ChannelManifest(manifestOne.getSchemaVersion(), manifestOne.getName(), manifestOne.getId(),
manifestOne.getDescription(), manifestOne.getManifestRequirements(), filteredStreams);
}

private static String getKey(Stream s) {
return s.getGroupId() + ":" + s.getArtifactId();
}
}
9 changes: 9 additions & 0 deletions src/main/resources/UsageMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ mode=merge strategy to use. The default strategy is ${DEFAULT-VALUE}.
tools.manifest-merge.name=name to set in the merged manifest. If not set, defaults to "merged-manifest".
tools.manifest-merge.id=id to set in the merged manifest
tools.manifest-subtract.usage.header=Subtracts streams of two manifests.
tools.manifest-subtract.usage.description=Prints a manifest containing only streams from the first manifest that are \
The versions of artifacts in streams are ignored.
tools.manifest-subtract.exclude=Comma-separated list of excluded streams. The excluded streams are always included in \
the output manifest even if they are present in the second manifest. To include all streams matching a group a wildcard \
syntax can be used: @|bold <groupId>:*|@
tools.manifest-subtract.parameterOne=Initial manifest that the streams will be removed from.
tools.manifest-subtract.parameterTwo=Manifest containing streams to be removed.
tools.channel.merge-repositories.usage.header=Merges repositories from multiple channels into one channel.
tools.channel.merge-repositories.usage.description.0=Prints a channel using all the repositories from the input channels.\
The new channel uses a manifest provided as @|bold manifestUrl|@.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.wildfly.prospero.extras.manifest.subtract;

import org.junit.jupiter.api.Test;
import org.wildfly.channel.ChannelManifest;
import org.wildfly.channel.Stream;

import java.util.Arrays;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

class SubtractCommandTest {

@Test
public void testSubtractTwoEmptySetsProducesEmptySet() throws Exception {
final ChannelManifest res = callSubtract(new ChannelManifest("", "", "", Collections.emptyList()), getChannelManifest());

assertThat(res.getStreams())
.isEmpty();
}

@Test
public void testSubtractEmptySetProducesEmptySet() throws Exception {
final ChannelManifest c1 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest res = callSubtract(c1, getChannelManifest());

assertThat(res.getStreams())
.containsAll(c1.getStreams());
}

@Test
public void testSubtractFromEmptySetProducesOriginalSet() throws Exception {
final ChannelManifest c1 = getChannelManifest();
final ChannelManifest c2 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest res = callSubtract(c1, c2);

assertThat(res.getStreams())
.isEmpty();
}

@Test
public void testRemoveCommonStream() {
final ChannelManifest c1 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest c2 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest res = callSubtract(c1, c2);

assertThat(res.getStreams())
.isEmpty();
}

@Test
public void testRemoveCommonStreamLivesUnique() {
final ChannelManifest c1 = getChannelManifest(
new Stream("foo", "bar", "1.1"),
new Stream("unique", "one", "1.1")
);
final ChannelManifest c2 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest res = callSubtract(c1, c2);

assertThat(res.getStreams())
.containsOnly(new Stream("unique", "one", "1.1"));
}

@Test
public void testRemoveCommonStreamIgnoresVersion() {
final ChannelManifest c1 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest c2 = getChannelManifest(new Stream("foo", "bar", "1.2"));
final ChannelManifest res = callSubtract(c1, c2);

assertThat(res.getStreams())
.isEmpty();
}

@Test
public void testExcludeStreamsByGroupArtifact() {
final ChannelManifest c1 = getChannelManifest(
new Stream("foo", "bar", "1.1"),
new Stream("other", "one", "1.1"));
final ChannelManifest c2 = getChannelManifest(new Stream("foo", "bar", "1.1"));
final ChannelManifest res = callSubtract(c1, c2, "foo:bar");

assertThat(res.getStreams())
.containsAll(c1.getStreams());
}

@Test
public void testExcludeStreamsByGroup() {
final ChannelManifest c1 = getChannelManifest(
new Stream("foo", "bar", "1.1"),
new Stream("foo", "other", "1.1"));
final ChannelManifest c2 = getChannelManifest(
new Stream("foo", "bar", "1.1"),
new Stream("foo", "other", "1.1")
);
final ChannelManifest res = callSubtract(c1, c2, "foo:*");

assertThat(res.getStreams())
.containsAll(c1.getStreams());
}

@Test
public void testInvalidExclusionPattern() {
final ChannelManifest c1 = getChannelManifest();
final ChannelManifest c2 = getChannelManifest();

assertThatThrownBy(()->callSubtract(c1, c2, "foo"))
.isInstanceOf(IllegalArgumentException.class);

assertThatThrownBy(()->callSubtract(c1, c2, "foo:bar:aaa"))
.isInstanceOf(IllegalArgumentException.class);

callSubtract(c1, c2, "org.test:bar-aaa122");
}

private static ChannelManifest callSubtract(ChannelManifest c1, ChannelManifest c2, String ... exclusions) {
return ManifestSubtractCommand.subtract(c1, c2, Arrays.asList(exclusions));
}

private static ChannelManifest getChannelManifest() {
return new ChannelManifest("", "", "", Collections.emptyList());
}

private static ChannelManifest getChannelManifest(Stream... stream) {
return new ChannelManifest("", "", "", Arrays.asList(stream));
}
}
Loading