Skip to content

Commit

Permalink
Add manifest-subtract operation
Browse files Browse the repository at this point in the history
  • Loading branch information
spyrkob committed Feb 14, 2024
1 parent cf86e10 commit 4a9fd65
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 0 deletions.
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,86 @@
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.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;

@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));
}
}

0 comments on commit 4a9fd65

Please sign in to comment.