Skip to content

Commit

Permalink
Add option to reject update and revert operations if there are any fi…
Browse files Browse the repository at this point in the history
…le conflicts
  • Loading branch information
spyrkob committed Jul 4, 2024
1 parent 8cac0aa commit 6d0760c
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -695,4 +695,10 @@ default String getProfile() {
default OperationException sizeOfChannel(Path channelFile) {
return new OperationException(format(bundle.getString("prospero.channels.error.morethanonechannel.found"), channelFile));
}

default OperationException cancelledByConfilcts() {
return new OperationException(format(
bundle.getString("prospero.updates.apply.candidate.cancel_conflicts"),
CliConstants.NO_CONFLICTS_ONLY));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ private Commands() {
public static final String VV = "-vv";
public static final String Y = "-y";
public static final String YES = "--yes";
public static final String NO_CONFLICTS_ONLY = "--no-conflicts-only";
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,16 @@
)
public class RevertCommand extends AbstractParentCommand {

private static int applyCandidate(CliConsole console, ApplyCandidateAction applyCandidateAction, boolean yes) throws OperationException, ProvisioningException {
private static int applyCandidate(CliConsole console, ApplyCandidateAction applyCandidateAction, boolean yes, boolean noConflictsOnly) throws OperationException, ProvisioningException {
List<ArtifactChange> artifactUpdates = applyCandidateAction.findUpdates().getArtifactUpdates();
console.printArtifactChanges(artifactUpdates);
final List<FileConflict> conflicts = applyCandidateAction.getConflicts();
FileConflictPrinter.print(conflicts, console);

if (noConflictsOnly && !conflicts.isEmpty()) {
throw CliMessages.MESSAGES.cancelledByConfilcts();
}

if (!yes && !artifactUpdates.isEmpty() && !console.confirm(CliMessages.MESSAGES.continueWithRevert(),
CliMessages.MESSAGES.applyingChanges(), CliMessages.MESSAGES.revertCancelled())) {
return SUCCESS;
Expand Down Expand Up @@ -92,6 +96,9 @@ public static class PerformCommand extends AbstractMavenCommand {
@CommandLine.Option(names = {CliConstants.Y, CliConstants.YES})
boolean yes;

@CommandLine.Option(names = {"--no-conflicts-only"})
boolean noConflictsOnly;

public PerformCommand(CliConsole console, ActionFactory actionFactory) {
super(console, actionFactory);
}
Expand Down Expand Up @@ -121,7 +128,7 @@ public Integer call() throws Exception {

validateRevertCandidate(installationDirectory, tempDirectory, applyCandidateAction);

applyCandidate(console, applyCandidateAction, yes);
applyCandidate(console, applyCandidateAction, yes, noConflictsOnly);
} catch (IOException e) {
throw ProsperoLogger.ROOT_LOGGER.unableToCreateTemporaryDirectory(e);
}
Expand All @@ -147,6 +154,9 @@ public static class ApplyCommand extends AbstractCommand {
@CommandLine.Option(names = {CliConstants.Y, CliConstants.YES})
boolean yes;

@CommandLine.Option(names = {CliConstants.NO_CONFLICTS_ONLY})
boolean noConflictsOnly;

public ApplyCommand(CliConsole console, ActionFactory actionFactory) {
super(console, actionFactory);
}
Expand All @@ -162,7 +172,7 @@ public Integer call() throws Exception {
console.println(CliMessages.MESSAGES.revertStart(installationDirectory, applyCandidateAction.getCandidateRevision().getName()));
console.println("");

applyCandidate(console, applyCandidateAction, yes);
applyCandidate(console, applyCandidateAction, yes, noConflictsOnly);
if(remove) {
applyCandidateAction.removeCandidate(candidateDirectory.toFile());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public static class PerformCommand extends AbstractMavenCommand {
@CommandLine.Option(names = {CliConstants.Y, CliConstants.YES})
boolean yes;

@CommandLine.Option(names = {CliConstants.NO_CONFLICTS_ONLY})
boolean noConflictsOnly;

public PerformCommand(CliConsole console, ActionFactory actionFactory) {
super(console, actionFactory);
}
Expand Down Expand Up @@ -119,7 +122,7 @@ public Integer call() throws Exception {
console.println(CliMessages.MESSAGES.updateHeader(installationDir));

try (UpdateAction updateAction = actionFactory.update(installationDir, mavenOptions, console, repositories)) {
performUpdate(updateAction, yes, console, installationDir);
performUpdate(updateAction, yes, console, installationDir, noConflictsOnly);
}
}

Expand All @@ -129,7 +132,7 @@ public Integer call() throws Exception {
return ReturnCodes.SUCCESS;
}

private boolean performUpdate(UpdateAction updateAction, boolean yes, CliConsole console, Path installDir) throws OperationException, ProvisioningException {
private boolean performUpdate(UpdateAction updateAction, boolean yes, CliConsole console, Path installDir, boolean noConflictsOnly) throws OperationException, ProvisioningException {
Path targetDir = null;
try {
targetDir = Files.createTempDirectory("update-candidate");
Expand All @@ -141,6 +144,11 @@ private boolean performUpdate(UpdateAction updateAction, boolean yes, CliConsole
final List<FileConflict> conflicts = applyCandidateAction.getConflicts();
if (!conflicts.isEmpty()) {
FileConflictPrinter.print(conflicts, console);

if (noConflictsOnly) {
throw CliMessages.MESSAGES.cancelledByConfilcts();
}

if (!yes && !console.confirm(CliMessages.MESSAGES.continueWithUpdate(), "", CliMessages.MESSAGES.updateCancelled())) {
return false;
}
Expand Down Expand Up @@ -226,6 +234,9 @@ public static class ApplyCommand extends AbstractCommand {
@CommandLine.Option(names = {CliConstants.Y, CliConstants.YES})
boolean yes;

@CommandLine.Option(names = {"--no-conflicts-only"})
boolean noConflictsOnly;

public ApplyCommand(CliConsole console, ActionFactory actionFactory) {
super(console, actionFactory);
}
Expand Down Expand Up @@ -257,6 +268,10 @@ public Integer call() throws Exception {
final List<FileConflict> conflicts = applyCandidateAction.getConflicts();
FileConflictPrinter.print(conflicts, console);

if (noConflictsOnly && !conflicts.isEmpty()) {
throw CliMessages.MESSAGES.cancelledByConfilcts();
}

// there always should be updates, so confirm update
if (!yes && !console.confirm(CliMessages.MESSAGES.continueWithUpdate(), CliMessages.MESSAGES.applyingUpdates(), CliMessages.MESSAGES.updateCancelled())) {
return ReturnCodes.SUCCESS;
Expand Down
4 changes: 4 additions & 0 deletions prospero-cli/src/main/resources/UsageMessages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ package-stability-level.1 = Valid options are ${COMPLETION-CANDIDATES}.
${prospero.dist.name}.update.prepare.candidate-dir = Target directory where the candidate server will be provisioned. The existing server is not updated.
${prospero.dist.name}.update.subscribe.product = Specify the product name. This must be a known feature pack supported by ${prospero.dist.name}.
${prospero.dist.name}.update.subscribe.version = Specify the version of the product.
no-conflicts-only = Rejects the operation if any file conflicts are detected. If not used, the user will be asked to \
confirm automatic conflict resolution, unless @|bold --yes|@ option is used.

#
# Exit Codes
Expand Down Expand Up @@ -278,6 +280,8 @@ prospero.updates.apply.validation.candidate.wrong_type=Unable to apply candidate
prospero.updates.apply.validation.candidate.not_candidate=Unable to apply candidate.%n Installation at [%s] doesn't have a candidate marker file.
prospero.updates.apply.candidate.remove=Remove the candidate directory after applying update.

prospero.updates.apply.candidate.cancel_conflicts = Potential conflicts exist in the installation. Resolve the conflicts in the listed files, or \
use [%s=false] to preserve user changes where possible.

prospero.updates.build.candidate.header=Building update candidate for %s%n
prospero.updates.build.candidate.complete=Update candidate generated in %s
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.wildfly.prospero.actions.ApplyCandidateAction;
import org.wildfly.prospero.api.FileConflict;
Expand All @@ -43,6 +44,7 @@
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -188,6 +190,39 @@ public void testAskForConfirmationIfConflictsPresent() throws Exception {
assertEquals(1, askedConfirmation);
}

@Test
public void noConflictArgumentFailsCommand_WhenConflictsAreFound() throws Exception {
final Path updatePath = mockInstallation("update");
final Path targetPath = mockInstallation("target");
when(applyCandidateAction.getConflicts()).thenReturn(List.of(mock(FileConflict.class)));

int exitCode = commandLine.execute(CliConstants.Commands.UPDATE, CliConstants.Commands.APPLY,
CliConstants.CANDIDATE_DIR, updatePath.toString(),
CliConstants.DIR, targetPath.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.PROCESSING_ERROR, exitCode);
assertThat(getErrorOutput())
.contains(CliMessages.MESSAGES.cancelledByConfilcts().getMessage());

verify(applyCandidateAction, Mockito.never()).applyUpdate(any());
}

@Test
public void noConflictArgumentHasNoEffect_WhenNoConflictsAreFound() throws Exception {
final Path updatePath = mockInstallation("update");
final Path targetPath = mockInstallation("target");
when(applyCandidateAction.getConflicts()).thenReturn(Collections.emptyList());

int exitCode = commandLine.execute(CliConstants.Commands.UPDATE, CliConstants.Commands.APPLY,
CliConstants.CANDIDATE_DIR, updatePath.toString(),
CliConstants.DIR, targetPath.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.SUCCESS, exitCode);
verify(applyCandidateAction).applyUpdate(ApplyCandidateAction.Type.UPDATE);
}

private Path mockInstallation(String target) throws IOException, MetadataException, XMLStreamException {
final Path targetPath = temp.newFolder(target).toPath();
MetadataTestUtils.createInstallationMetadata(targetPath).close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.wildfly.prospero.actions.ApplyCandidateAction;
import org.wildfly.prospero.api.Console;
import org.wildfly.prospero.actions.InstallationHistoryAction;
import org.wildfly.prospero.api.FileConflict;
import org.wildfly.prospero.api.SavedState;
import org.wildfly.prospero.api.exceptions.OperationException;
import org.wildfly.prospero.cli.AbstractConsoleTest;
Expand All @@ -40,9 +42,13 @@

import java.nio.file.Path;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -119,4 +125,33 @@ public void callApplyOperation() throws Exception {
assertEquals(ReturnCodes.SUCCESS, exitCode);
verify(applyCandidateAction).applyUpdate(ApplyCandidateAction.Type.REVERT);
}

@Test
public void noConflictArgumentFailsCommand_WhenConflictsAreFound() throws Exception {
when(applyCandidateAction.getConflicts()).thenReturn(List.of(mock(FileConflict.class)));

int exitCode = commandLine.execute(CliConstants.Commands.REVERT, CliConstants.Commands.APPLY,
CliConstants.DIR, installationDir.toString(),
CliConstants.CANDIDATE_DIR, updateDir.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.PROCESSING_ERROR, exitCode);
assertThat(getErrorOutput())
.contains(CliMessages.MESSAGES.cancelledByConfilcts().getMessage());

verify(applyCandidateAction, Mockito.never()).applyUpdate(any());
}

@Test
public void noConflictArgumentHasNoEffect_WhenNoConflictsAreFound() throws Exception {
when(applyCandidateAction.getConflicts()).thenReturn(Collections.emptyList());

int exitCode = commandLine.execute(CliConstants.Commands.REVERT, CliConstants.Commands.APPLY,
CliConstants.DIR, installationDir.toString(),
CliConstants.CANDIDATE_DIR, updateDir.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.SUCCESS, exitCode);
verify(applyCandidateAction).applyUpdate(ApplyCandidateAction.Type.REVERT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.wildfly.channel.Repository;
import org.wildfly.prospero.actions.ApplyCandidateAction;
import org.wildfly.prospero.api.Console;
import org.wildfly.prospero.actions.InstallationHistoryAction;
import org.wildfly.prospero.api.FileConflict;
import org.wildfly.prospero.api.MavenOptions;
import org.wildfly.prospero.api.SavedState;
import org.wildfly.prospero.api.exceptions.OperationException;
Expand All @@ -50,6 +52,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -154,6 +157,35 @@ public void passRemoteRepositories() throws Exception {
.containsExactly("http://temp.repo.te");
}

@Test
public void noConflictArgumentFailsCommand_WhenConflictsAreFound() throws Exception {
when(applyCandidateAction.getConflicts()).thenReturn(List.of(mock(FileConflict.class)));

int exitCode = commandLine.execute(CliConstants.Commands.REVERT, CliConstants.Commands.PERFORM,
CliConstants.DIR, installationDir.toString(),
CliConstants.REVISION, "abcd",
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.PROCESSING_ERROR, exitCode);
assertThat(getErrorOutput())
.contains(CliMessages.MESSAGES.cancelledByConfilcts().getMessage());

verify(applyCandidateAction, Mockito.never()).applyUpdate(any());
}

@Test
public void noConflictArgumentHasNoEffect_WhenNoConflictsAreFound() throws Exception {
when(applyCandidateAction.getConflicts()).thenReturn(Collections.emptyList());

int exitCode = commandLine.execute(CliConstants.Commands.REVERT, CliConstants.Commands.PERFORM,
CliConstants.DIR, installationDir.toString(),
CliConstants.REVISION, "abcd",
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.SUCCESS, exitCode);
verify(applyCandidateAction).applyUpdate(ApplyCandidateAction.Type.REVERT);
}

@Override
protected MavenOptions getCapturedMavenOptions() throws Exception {
verify(historyAction).prepareRevert(eq(new SavedState("abcd")), mavenOptions.capture(), any(), any());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.wildfly.prospero.actions.ApplyCandidateAction;
import org.wildfly.prospero.actions.UpdateAction;
import org.wildfly.prospero.api.ArtifactChange;
import org.wildfly.prospero.api.FileConflict;
import org.wildfly.prospero.api.MavenOptions;
import org.wildfly.prospero.cli.ActionFactory;
import org.wildfly.prospero.cli.CliMessages;
Expand All @@ -51,7 +52,9 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
Expand Down Expand Up @@ -303,6 +306,37 @@ public void spliRepositoriesFromArgument() throws Exception {

}

@Test
public void noConflictArgumentFailsCommand_WhenConflictsAreFound() throws Exception {
when(updateAction.findUpdates()).thenReturn(new UpdateSet(List.of(change("1.0.0", "1.0.1"))));
when(updateAction.buildUpdate(any())).thenReturn(true);
when(applyCandidateAction.getConflicts()).thenReturn(List.of(mock(FileConflict.class)));

int exitCode = commandLine.execute(CliConstants.Commands.UPDATE, CliConstants.Commands.PERFORM,
CliConstants.DIR, installationDir.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.PROCESSING_ERROR, exitCode);
assertThat(getErrorOutput())
.contains(CliMessages.MESSAGES.cancelledByConfilcts().getMessage());

verify(applyCandidateAction, Mockito.never()).applyUpdate(any());
}

@Test
public void noConflictArgumentHasNoEffect_WhenNoConflictsAreFound() throws Exception {
when(updateAction.findUpdates()).thenReturn(new UpdateSet(List.of(change("1.0.0", "1.0.1"))));
when(updateAction.buildUpdate(any())).thenReturn(true);
when(applyCandidateAction.getConflicts()).thenReturn(Collections.emptyList());

int exitCode = commandLine.execute(CliConstants.Commands.UPDATE, CliConstants.Commands.PERFORM,
CliConstants.DIR, installationDir.toString(),
CliConstants.NO_CONFLICTS_ONLY);

assertEquals(ReturnCodes.SUCCESS, exitCode);
verify(applyCandidateAction).applyUpdate(ApplyCandidateAction.Type.UPDATE);
}

private ArtifactChange change(String oldVersion, String newVersion) {
return ArtifactChange.updated(new DefaultArtifact("org.foo", "bar", null, oldVersion),
new DefaultArtifact("org.foo", "bar", null, newVersion));
Expand Down

0 comments on commit 6d0760c

Please sign in to comment.