mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-17 02:31:14 +01:00
Enable plugin management via CLI (#2087)
Co-authored-by: Konstantin Schaper <konstantin.schaper@cloudogu.com>
This commit is contained in:
2
gradle/changelog/cli_plugin_management.yaml
Normal file
2
gradle/changelog/cli_plugin_management.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: added
|
||||||
|
description: Enable plugin management via CLI ([#2087](https://github.com/scm-manager/scm-manager/pull/2087))
|
||||||
76
scm-core/src/main/java/sonia/scm/plugin/PendingPlugins.java
Normal file
76
scm-core/src/main/java/sonia/scm/plugin/PendingPlugins.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.google.common.collect.Iterables.contains;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
|
|
||||||
|
public class PendingPlugins {
|
||||||
|
|
||||||
|
private final List<AvailablePlugin> install;
|
||||||
|
private final List<InstalledPlugin> update;
|
||||||
|
private final List<InstalledPlugin> uninstall;
|
||||||
|
|
||||||
|
public PendingPlugins(List<AvailablePlugin> availablePlugins, List<InstalledPlugin> installedPlugins) {
|
||||||
|
List<AvailablePlugin> pending = availablePlugins
|
||||||
|
.stream()
|
||||||
|
.filter(AvailablePlugin::isPending)
|
||||||
|
.collect(toList());
|
||||||
|
|
||||||
|
this.install = pending
|
||||||
|
.stream()
|
||||||
|
.filter(a -> !contains(installedPlugins, a)).collect(toList());
|
||||||
|
this.update = installedPlugins
|
||||||
|
.stream()
|
||||||
|
.filter(i -> contains(pending, i)).collect(toList());
|
||||||
|
this.uninstall = installedPlugins
|
||||||
|
.stream()
|
||||||
|
.filter(InstalledPlugin::isMarkedForUninstall).collect(toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<AvailablePlugin> getInstall() {
|
||||||
|
return install;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<InstalledPlugin> getUpdate() {
|
||||||
|
return update;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<InstalledPlugin> getUninstall() {
|
||||||
|
return uninstall;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPending(String name) {
|
||||||
|
return uninstall.stream().anyMatch(p -> p.getDescriptor().getInformation().getName().equals(name))
|
||||||
|
|| update.stream().anyMatch(p -> p.getDescriptor().getInformation().getName().equals(name))
|
||||||
|
|| install.stream().anyMatch(p -> p.getDescriptor().getInformation().getName().equals(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existPendingChanges() {
|
||||||
|
return !uninstall.isEmpty() || !update.isEmpty() || !install.isEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sonia.scm.plugin;
|
package sonia.scm.plugin;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -89,6 +89,14 @@ public interface PluginManager {
|
|||||||
*/
|
*/
|
||||||
List<InstalledPlugin> getUpdatable();
|
List<InstalledPlugin> getUpdatable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all pending plugins.
|
||||||
|
*
|
||||||
|
* @return a list of pending plugins.
|
||||||
|
* @since 2.38.0
|
||||||
|
*/
|
||||||
|
PendingPlugins getPending();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs the plugin with the given name from the list of available plugins.
|
* Installs the plugin with the given name from the list of available plugins.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ import io.swagger.v3.oas.annotations.media.Content;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||||
import sonia.scm.lifecycle.Restarter;
|
import sonia.scm.lifecycle.Restarter;
|
||||||
import sonia.scm.plugin.AvailablePlugin;
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
import sonia.scm.plugin.InstalledPlugin;
|
|
||||||
import sonia.scm.plugin.PluginManager;
|
import sonia.scm.plugin.PluginManager;
|
||||||
import sonia.scm.plugin.PluginPermissions;
|
import sonia.scm.plugin.PluginPermissions;
|
||||||
import sonia.scm.web.VndMediaType;
|
import sonia.scm.web.VndMediaType;
|
||||||
@@ -44,9 +43,7 @@ import javax.ws.rs.POST;
|
|||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static de.otto.edison.hal.Link.link;
|
import static de.otto.edison.hal.Link.link;
|
||||||
import static de.otto.edison.hal.Links.linkingTo;
|
import static de.otto.edison.hal.Links.linkingTo;
|
||||||
@@ -94,28 +91,13 @@ public class PendingPluginResource {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
public Response getPending() {
|
public Response getPending() {
|
||||||
List<AvailablePlugin> pending = pluginManager
|
PendingPlugins pending = pluginManager.getPending();
|
||||||
.getAvailable()
|
|
||||||
.stream()
|
|
||||||
.filter(AvailablePlugin::isPending)
|
|
||||||
.collect(toList());
|
|
||||||
List<InstalledPlugin> installed = pluginManager.getInstalled();
|
|
||||||
|
|
||||||
Stream<AvailablePlugin> newPlugins = pending
|
|
||||||
.stream()
|
|
||||||
.filter(a -> !contains(installed, a));
|
|
||||||
Stream<InstalledPlugin> updatePlugins = installed
|
|
||||||
.stream()
|
|
||||||
.filter(i -> contains(pending, i));
|
|
||||||
Stream<InstalledPlugin> uninstallPlugins = installed
|
|
||||||
.stream()
|
|
||||||
.filter(InstalledPlugin::isMarkedForUninstall);
|
|
||||||
|
|
||||||
Links.Builder linksBuilder = linkingTo().self(resourceLinks.pendingPluginCollection().self());
|
Links.Builder linksBuilder = linkingTo().self(resourceLinks.pendingPluginCollection().self());
|
||||||
|
|
||||||
List<PluginDto> installDtos = newPlugins.map(mapper::mapAvailable).collect(toList());
|
List<PluginDto> installDtos = pending.getInstall().stream().map(mapper::mapAvailable).collect(toList());
|
||||||
List<PluginDto> updateDtos = updatePlugins.map(i -> mapper.mapInstalled(i, pending)).collect(toList());
|
List<PluginDto> updateDtos = pending.getUpdate().stream().map(p -> mapper.mapInstalled(p, pending.getInstall())).collect(toList());
|
||||||
List<PluginDto> uninstallDtos = uninstallPlugins.map(i -> mapper.mapInstalled(i, pending)).collect(toList());
|
List<PluginDto> uninstallDtos = pending.getUninstall().stream().map(i -> mapper.mapInstalled(i, pending.getInstall())).collect(toList());
|
||||||
|
|
||||||
if (
|
if (
|
||||||
PluginPermissions.write().isPermitted() &&
|
PluginPermissions.write().isPermitted() &&
|
||||||
@@ -135,22 +117,6 @@ public class PendingPluginResource {
|
|||||||
return Response.ok(new HalRepresentation(linksBuilder.build(), embedded.build())).build();
|
return Response.ok(new HalRepresentation(linksBuilder.build(), embedded.build())).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean contains(Collection<InstalledPlugin> installedPlugins, AvailablePlugin availablePlugin) {
|
|
||||||
return installedPlugins
|
|
||||||
.stream()
|
|
||||||
.anyMatch(installedPlugin -> haveSameName(installedPlugin, availablePlugin));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean contains(Collection<AvailablePlugin> availablePlugins, InstalledPlugin installedPlugin) {
|
|
||||||
return availablePlugins
|
|
||||||
.stream()
|
|
||||||
.anyMatch(availablePlugin -> haveSameName(installedPlugin, availablePlugin));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean haveSameName(InstalledPlugin installedPlugin, AvailablePlugin availablePlugin) {
|
|
||||||
return installedPlugin.getDescriptor().getInformation().getName().equals(availablePlugin.getDescriptor().getInformation().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/execute")
|
@Path("/execute")
|
||||||
@Operation(
|
@Operation(
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -87,15 +88,11 @@ public class DefaultPluginManager implements PluginManager {
|
|||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.pluginSetConfigStore = pluginSetConfigStore;
|
this.pluginSetConfigStore = pluginSetConfigStore;
|
||||||
|
|
||||||
if (contextFactory != null) {
|
this.contextFactory = Objects.requireNonNullElseGet(contextFactory, () -> (plugins -> {
|
||||||
this.contextFactory = contextFactory;
|
List<AvailablePlugin> pendingPlugins = new ArrayList<>(plugins);
|
||||||
} else {
|
pendingInstallQueue.stream().map(PendingPluginInstallation::getPlugin).forEach(pendingPlugins::add);
|
||||||
this.contextFactory = (plugins -> {
|
return PluginInstallationContext.from(getInstalled(), pendingPlugins);
|
||||||
List<AvailablePlugin> pendingPlugins = new ArrayList<>(plugins);
|
}));
|
||||||
pendingInstallQueue.stream().map(PendingPluginInstallation::getPlugin).forEach(pendingPlugins::add);
|
|
||||||
return PluginInstallationContext.from(getInstalled(), pendingPlugins);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.computeInstallationDependencies();
|
this.computeInstallationDependencies();
|
||||||
}
|
}
|
||||||
@@ -192,6 +189,7 @@ public class DefaultPluginManager implements PluginManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<InstalledPlugin> getUpdatable() {
|
public List<InstalledPlugin> getUpdatable() {
|
||||||
|
PluginPermissions.read().check();
|
||||||
return getInstalled()
|
return getInstalled()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(p -> isUpdatable(p.getDescriptor().getInformation().getName()))
|
.filter(p -> isUpdatable(p.getDescriptor().getInformation().getName()))
|
||||||
@@ -199,6 +197,12 @@ public class DefaultPluginManager implements PluginManager {
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PendingPlugins getPending() {
|
||||||
|
PluginPermissions.read().check();
|
||||||
|
return new PendingPlugins(getAvailable(), getInstalled());
|
||||||
|
}
|
||||||
|
|
||||||
private <T extends Plugin> Predicate<T> filterByName(String name) {
|
private <T extends Plugin> Predicate<T> filterByName(String name) {
|
||||||
return plugin -> name.equals(plugin.getDescriptor().getInformation().getName());
|
return plugin -> name.equals(plugin.getDescriptor().getInformation().getName());
|
||||||
}
|
}
|
||||||
@@ -361,7 +365,7 @@ public class DefaultPluginManager implements PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isUpdatable(String name) {
|
private boolean isUpdatable(String name) {
|
||||||
return getAvailable(name).isPresent() && !getPending(name).isPresent();
|
return getAvailable(name).isPresent() && getPending(name).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "add")
|
||||||
|
class PluginAddCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Parameters(index = "0", paramLabel = "<name>", descriptionKey = "scm.plugin.name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--apply", "-a"}, descriptionKey = "scm.plugin.apply")
|
||||||
|
private boolean apply;
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginAddCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (manager.getInstalled(name).isPresent()) {
|
||||||
|
templateRenderer.renderPluginAlreadyInstalledError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (manager.getAvailable(name).isEmpty()) {
|
||||||
|
templateRenderer.renderPluginNotAvailableError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
manager.install(name, apply);
|
||||||
|
} catch (Exception e) {
|
||||||
|
templateRenderer.renderPluginCouldNotBeAdded(name);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
templateRenderer.renderPluginAdded(name);
|
||||||
|
if (!apply) {
|
||||||
|
templateRenderer.renderServerRestartRequired();
|
||||||
|
} else {
|
||||||
|
templateRenderer.renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setApply(boolean apply) {
|
||||||
|
this.apply = apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "apply")
|
||||||
|
class PluginApplyCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--yes", "-y"}, descriptionKey = "scm.plugin.restart")
|
||||||
|
private boolean restart;
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginApplyCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!restart) {
|
||||||
|
templateRenderer.renderConfirmServerRestart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (manager.getPending().existPendingChanges()) {
|
||||||
|
manager.executePendingAndRestart();
|
||||||
|
templateRenderer.renderServerRestartTriggered();
|
||||||
|
} else {
|
||||||
|
templateRenderer.renderSkipServerRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setRestart(boolean restart) {
|
||||||
|
this.restart = restart;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
@CommandLine.Command(name = "plugin")
|
||||||
|
public class PluginCommand {
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.cli.Table;
|
||||||
|
import sonia.scm.cli.TemplateRenderer;
|
||||||
|
import sonia.scm.plugin.AvailablePlugin;
|
||||||
|
import sonia.scm.plugin.InstalledPlugin;
|
||||||
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
|
import sonia.scm.plugin.PluginDescriptor;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "list", aliases = "ls")
|
||||||
|
class PluginListCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final TemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--short", "-s"})
|
||||||
|
private boolean useShortTemplate;
|
||||||
|
|
||||||
|
private static final String TABLE_TEMPLATE = String.join("\n",
|
||||||
|
"{{#rows}}",
|
||||||
|
"{{#cols}}{{#row.first}}{{#upper}}{{value}}{{/upper}}{{/row.first}}{{^row.first}}{{value}}{{/row.first}}{{^last}} {{/last}}{{/cols}}",
|
||||||
|
"{{/rows}}"
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final String SHORT_TEMPLATE = String.join("\n",
|
||||||
|
"{{#plugins}}",
|
||||||
|
"{{name}}",
|
||||||
|
"{{/plugins}}"
|
||||||
|
);
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public PluginListCommand(TemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Collection<ListablePlugin> plugins = getListablePlugins();
|
||||||
|
if (useShortTemplate) {
|
||||||
|
templateRenderer.renderToStdout(SHORT_TEMPLATE, Map.of("plugins", plugins));
|
||||||
|
} else {
|
||||||
|
Table table = templateRenderer.createTable();
|
||||||
|
String yes = spec.resourceBundle().getString("yes");
|
||||||
|
table.addHeader("scm.plugin.name", "scm.plugin.displayName", "scm.plugin.availableVersion", "scm.plugin.installedVersion", "scm.plugin.pending");
|
||||||
|
|
||||||
|
for (ListablePlugin plugin : plugins) {
|
||||||
|
table.addRow(
|
||||||
|
plugin.getName(),
|
||||||
|
plugin.getDisplayName(),
|
||||||
|
plugin.getAvailableVersion(),
|
||||||
|
plugin.getInstalledVersion(),
|
||||||
|
plugin.isPending() ? yes : ""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
templateRenderer.renderToStdout(TABLE_TEMPLATE, Map.of("rows", table, "plugins", plugins));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ListablePlugin> getListablePlugins() {
|
||||||
|
List<InstalledPlugin> installedPlugins = manager.getInstalled();
|
||||||
|
List<AvailablePlugin> availablePlugins = manager.getAvailable();
|
||||||
|
PendingPlugins pendingPlugins = manager.getPending();
|
||||||
|
|
||||||
|
Set<ListablePlugin> plugins = new HashSet<>();
|
||||||
|
for (PluginDescriptor pluginDesc : installedPlugins.stream().map(InstalledPlugin::getDescriptor).collect(Collectors.toList())) {
|
||||||
|
ListablePlugin listablePlugin = new ListablePlugin(pendingPlugins, pluginDesc, true);
|
||||||
|
setAvailableVersion(listablePlugin);
|
||||||
|
plugins.add(listablePlugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PluginDescriptor pluginDesc : availablePlugins.stream().map(AvailablePlugin::getDescriptor).collect(Collectors.toList())) {
|
||||||
|
if (plugins.stream().noneMatch(p -> p.name.equals(pluginDesc.getInformation().getName()))) {
|
||||||
|
plugins.add(new ListablePlugin(pendingPlugins, pluginDesc, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return plugins.stream().sorted((a, b) -> a.name.compareToIgnoreCase(b.name)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAvailableVersion(ListablePlugin listablePlugin) {
|
||||||
|
Optional<AvailablePlugin> availablePlugin = manager.getAvailable().stream().filter(p -> p.getDescriptor().getInformation().getName().equals(listablePlugin.name)).findFirst();
|
||||||
|
if (availablePlugin.isPresent() && !availablePlugin.get().getDescriptor().getInformation().getVersion().equals(listablePlugin.installedVersion)) {
|
||||||
|
listablePlugin.setAvailableVersion(availablePlugin.get().getDescriptor().getInformation().getVersion());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setSpec(CommandLine.Model.CommandSpec spec) {
|
||||||
|
this.spec = spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setUseShortTemplate(boolean useShortTemplate) {
|
||||||
|
this.useShortTemplate = useShortTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
static class ListablePlugin {
|
||||||
|
private String name;
|
||||||
|
private String displayName;
|
||||||
|
private String installedVersion;
|
||||||
|
private String availableVersion;
|
||||||
|
private boolean pending;
|
||||||
|
private boolean installed;
|
||||||
|
|
||||||
|
ListablePlugin(PendingPlugins pendingPlugins, PluginDescriptor descriptor, boolean installed) {
|
||||||
|
this.name = descriptor.getInformation().getName();
|
||||||
|
this.displayName = descriptor.getInformation().getDisplayName();
|
||||||
|
if (installed) {
|
||||||
|
this.installedVersion = descriptor.getInformation().getVersion();
|
||||||
|
} else {
|
||||||
|
this.availableVersion = descriptor.getInformation().getVersion();
|
||||||
|
}
|
||||||
|
this.pending = pendingPlugins.isPending(descriptor.getInformation().getName());
|
||||||
|
this.installed = installed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "remove", aliases = "rm")
|
||||||
|
class PluginRemoveCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Parameters(index = "0", paramLabel = "<name>", descriptionKey = "scm.plugin.name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--apply", "-a"}, descriptionKey = "scm.plugin.apply")
|
||||||
|
private boolean apply;
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginRemoveCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (manager.getInstalled(name).isEmpty()) {
|
||||||
|
templateRenderer.renderPluginNotInstalledError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
manager.uninstall(name, apply);
|
||||||
|
} catch (Exception e) {
|
||||||
|
templateRenderer.renderPluginCouldNotBeRemoved(name);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
templateRenderer.renderPluginRemoved(name);
|
||||||
|
if (!apply) {
|
||||||
|
templateRenderer.renderServerRestartRequired();
|
||||||
|
} else {
|
||||||
|
templateRenderer.renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setApply(boolean apply) {
|
||||||
|
this.apply = apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "cancel-pending", aliases = "reset")
|
||||||
|
class PluginResetChangesCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginResetChangesCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (manager.getPending().existPendingChanges()) {
|
||||||
|
manager.cancelPending();
|
||||||
|
templateRenderer.renderPluginsReseted();
|
||||||
|
} else {
|
||||||
|
templateRenderer.renderNoPendingPlugins();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import sonia.scm.cli.CliContext;
|
||||||
|
import sonia.scm.cli.ExitCode;
|
||||||
|
import sonia.scm.cli.TemplateRenderer;
|
||||||
|
import sonia.scm.template.TemplateEngineFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class PluginTemplateRenderer extends TemplateRenderer {
|
||||||
|
|
||||||
|
private static final String PLUGIN_NOT_AVAILABLE_ERROR_TEMPLATE = "{{i18n.scmPluginNotAvailable}}";
|
||||||
|
private static final String PLUGIN_NOT_INSTALLED_ERROR_TEMPLATE = "{{i18n.scmPluginNotInstalled}}";
|
||||||
|
private static final String PLUGIN_ALREADY_INSTALLED_ERROR_TEMPLATE = "{{i18n.scmPluginAlreadyInstalled}}";
|
||||||
|
private static final String PLUGIN_NOT_REMOVED_ERROR_TEMPLATE = "{{i18n.scmPluginNotRemoved}}";
|
||||||
|
private static final String PLUGIN_NOT_ADDED_ERROR_TEMPLATE = "{{i18n.scmPluginNotAdded}}";
|
||||||
|
private static final String PLUGIN_NOT_UPDATABLE_ERROR_TEMPLATE = "{{i18n.scmPluginNotUpdatable}}";
|
||||||
|
private static final String PLUGIN_UPDATE_ERROR_TEMPLATE = "{{i18n.scmPluginsUpdateFailed}}";
|
||||||
|
private static final String PLUGIN_ADDED_TEMPLATE = "{{i18n.scmPluginAdded}}";
|
||||||
|
private static final String PLUGIN_REMOVED_TEMPLATE = "{{i18n.scmPluginRemoved}}";
|
||||||
|
private static final String PLUGIN_UPDATED_TEMPLATE = "{{i18n.scmPluginUpdated}}";
|
||||||
|
private static final String ALL_PLUGINS_UPDATED_TEMPLATE = "{{i18n.scmPluginsUpdated}}";
|
||||||
|
private static final String SERVER_RESTART_REQUIRED_TEMPLATE = "{{i18n.scmServerRestartRequired}}";
|
||||||
|
private static final String SERVER_RESTART_TRIGGERED_TEMPLATE = "{{i18n.scmServerRestartTriggered}}";
|
||||||
|
private static final String SERVER_RESTART_SKIPPED_TEMPLATE = "{{i18n.scmServerRestartSkipped}}";
|
||||||
|
private static final String SERVER_RESTART_CONFIRMATION_TEMPLATE = "{{i18n.scmServerRestartConfirmation}}";
|
||||||
|
private static final String PLUGINS_NOT_PENDING_ERROR_TEMPLATE = "{{i18n.scmPluginsNotPending}}";
|
||||||
|
private static final String ALL_PENDING_PLUGINS_CANCELLED = "{{i18n.scmPendingPluginsCancelled}}";
|
||||||
|
|
||||||
|
private static final String PLUGIN = "plugin";
|
||||||
|
private final CliContext context;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginTemplateRenderer(CliContext context, TemplateEngineFactory templateEngineFactory) {
|
||||||
|
super(context, templateEngineFactory);
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginAdded(String pluginName) {
|
||||||
|
renderToStdout(PLUGIN_ADDED_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginRemoved(String pluginName) {
|
||||||
|
renderToStdout(PLUGIN_REMOVED_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginUpdated(String pluginName) {
|
||||||
|
renderToStdout(PLUGIN_UPDATED_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderAllPluginsUpdated() {
|
||||||
|
renderToStdout(ALL_PLUGINS_UPDATED_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginCouldNotBeRemoved(String pluginName) {
|
||||||
|
renderToStderr(PLUGIN_NOT_REMOVED_ERROR_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginCouldNotBeAdded(String pluginName) {
|
||||||
|
renderToStderr(PLUGIN_NOT_ADDED_ERROR_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginNotUpdatable(String pluginName) {
|
||||||
|
renderToStderr(PLUGIN_NOT_UPDATABLE_ERROR_TEMPLATE, Map.of(PLUGIN, pluginName));
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderServerRestartRequired() {
|
||||||
|
renderToStdout(SERVER_RESTART_REQUIRED_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderServerRestartTriggered() {
|
||||||
|
renderToStdout(SERVER_RESTART_TRIGGERED_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderSkipServerRestart() {
|
||||||
|
renderToStdout(SERVER_RESTART_SKIPPED_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginsReseted() {
|
||||||
|
renderToStdout(ALL_PENDING_PLUGINS_CANCELLED, Collections.emptyMap());
|
||||||
|
context.getStdout().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderNoPendingPlugins() {
|
||||||
|
renderToStderr(PLUGINS_NOT_PENDING_ERROR_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderConfirmServerRestart() {
|
||||||
|
renderToStderr(SERVER_RESTART_CONFIRMATION_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginNotAvailableError() {
|
||||||
|
renderToStderr(PLUGIN_NOT_AVAILABLE_ERROR_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginsUpdateError() {
|
||||||
|
renderToStderr(PLUGIN_UPDATE_ERROR_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginNotInstalledError() {
|
||||||
|
renderToStderr(PLUGIN_NOT_INSTALLED_ERROR_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderPluginAlreadyInstalledError() {
|
||||||
|
renderToStderr(PLUGIN_ALREADY_INSTALLED_ERROR_TEMPLATE, Collections.emptyMap());
|
||||||
|
context.getStderr().println();
|
||||||
|
context.exit(ExitCode.USAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "update-all")
|
||||||
|
class PluginUpdateAllCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--apply", "-a"}, descriptionKey = "scm.plugin.apply")
|
||||||
|
private boolean apply;
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginUpdateAllCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
manager.updateAll();
|
||||||
|
} catch (Exception e) {
|
||||||
|
templateRenderer.renderPluginsUpdateError();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
templateRenderer.renderAllPluginsUpdated();
|
||||||
|
if (!apply) {
|
||||||
|
templateRenderer.renderServerRestartRequired();
|
||||||
|
} else {
|
||||||
|
manager.executePendingAndRestart();
|
||||||
|
templateRenderer.renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
void setApply(boolean apply) {
|
||||||
|
this.apply = apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import com.cronutils.utils.VisibleForTesting;
|
||||||
|
import picocli.CommandLine;
|
||||||
|
import sonia.scm.cli.ParentCommand;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@ParentCommand(value = PluginCommand.class)
|
||||||
|
@CommandLine.Command(name = "update")
|
||||||
|
class PluginUpdateCommand implements Runnable {
|
||||||
|
|
||||||
|
@CommandLine.Parameters(index = "0", paramLabel = "<name>", descriptionKey = "scm.plugin.name")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@CommandLine.Option(names = {"--apply", "-a"}, descriptionKey = "scm.plugin.apply")
|
||||||
|
private boolean apply;
|
||||||
|
|
||||||
|
@CommandLine.Mixin
|
||||||
|
private final PluginTemplateRenderer templateRenderer;
|
||||||
|
private final PluginManager manager;
|
||||||
|
@CommandLine.Spec
|
||||||
|
private CommandLine.Model.CommandSpec spec;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
PluginUpdateCommand(PluginTemplateRenderer templateRenderer, PluginManager manager) {
|
||||||
|
this.templateRenderer = templateRenderer;
|
||||||
|
this.manager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (manager.getInstalled(name).isEmpty()) {
|
||||||
|
templateRenderer.renderPluginNotInstalledError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (manager.getUpdatable().stream().noneMatch(p -> Objects.equals(p.getDescriptor().getInformation().getName(), name))) {
|
||||||
|
templateRenderer.renderPluginNotUpdatable(name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
manager.install(name, apply);
|
||||||
|
templateRenderer.renderPluginUpdated(name);
|
||||||
|
if (!apply) {
|
||||||
|
templateRenderer.renderServerRestartRequired();
|
||||||
|
} else {
|
||||||
|
templateRenderer.renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
@VisibleForTesting
|
||||||
|
void setApply(boolean apply) {
|
||||||
|
this.apply = apply;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -189,3 +189,57 @@ scm.group.remove-member.name = Name of the group
|
|||||||
scm.group.remove-member.members = Members to remove from the group
|
scm.group.remove-member.members = Members to remove from the group
|
||||||
|
|
||||||
groupNotFound = Could not find group
|
groupNotFound = Could not find group
|
||||||
|
|
||||||
|
## Plugin
|
||||||
|
scm.plugin.usage.description.0 = Resource command for all plugin-management-related actions
|
||||||
|
scmPluginNotAvailable = This plugin is not available. Please check your input.
|
||||||
|
scmPluginNotInstalled = This plugin is not installed. Please check your input.
|
||||||
|
scmPluginAlreadyInstalled = This plugin is already installed.
|
||||||
|
scmPluginAdded = The plugin was added for installation.
|
||||||
|
scmPluginRemoved = The plugin was removed.
|
||||||
|
scmPluginUpdated = The plugin was updated.
|
||||||
|
scmPluginsUpdated = All available plugins were updated.
|
||||||
|
scmPluginNotRemoved = This plugin could not be removed. Other installed plugins could depend on this plugin.
|
||||||
|
scmPluginNotAdded = This plugin could not be added. Please check your server logs for further information.
|
||||||
|
scmPluginNotUpdatable = This plugin could not be updated.
|
||||||
|
scmPluginsUpdateFailed = Plugin updated failed. Please check your server logs for further information.
|
||||||
|
scmServerRestartRequired = Please restart your server to make the plugin changes effective.
|
||||||
|
scmServerRestartTriggered = Server restart: Applying the changes...
|
||||||
|
scmServerRestartSkipped = Server restart skipped: Nothing to apply.
|
||||||
|
scmServerRestartConfirmation = Please confirm the server restart using --yes.
|
||||||
|
scmPluginsNotPending = No pending plugin changes found.
|
||||||
|
scmPendingPluginsCancelled = All plugin changes were cancelled.
|
||||||
|
|
||||||
|
### List plugins
|
||||||
|
scm.plugin.name = Name
|
||||||
|
scm.plugin.displayName = Display Name
|
||||||
|
scm.plugin.installedVersion = Installed
|
||||||
|
scm.plugin.availableVersion = Available
|
||||||
|
scm.plugin.pending = Pending?
|
||||||
|
scm.plugin.list.usage.description.0 = List all plugins with versions
|
||||||
|
scm.plugin.list.short = Show only the plugin names
|
||||||
|
|
||||||
|
### Add plugin
|
||||||
|
scm.plugin.add.usage.description.0 = Installs the plugin with the required dependencies
|
||||||
|
scm.plugin.add.apply = Apply all plugin changes now by restarting the server
|
||||||
|
|
||||||
|
### Remove plugin
|
||||||
|
scm.plugin.remove.usage.description.0 = Deletes the plugin
|
||||||
|
scm.plugin.remove.apply = Apply all plugin changes now by restarting the server
|
||||||
|
|
||||||
|
### Update plugin
|
||||||
|
scm.plugin.update.usage.description.0 = Updates the installed plugin
|
||||||
|
scm.plugin.update.apply = Apply all plugin changes now by restarting the server
|
||||||
|
|
||||||
|
### Update all plugins
|
||||||
|
scm.plugin.update-all.usage.description.0 = Updates all installed plugins
|
||||||
|
scm.plugin.update-all.apply = Apply all plugins changes now by restarting the server
|
||||||
|
|
||||||
|
### Apply plugin changes
|
||||||
|
scm.plugin.apply.usage.description.0 = Applies the pending plugin changes by restarting the server
|
||||||
|
scm.plugin.restart = Confirms server restart
|
||||||
|
|
||||||
|
### Apply plugin changes
|
||||||
|
scm.plugin.cancel-pending.usage.description.0 = Cancel all pending plugin changes
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -199,3 +199,56 @@ scm.group.remove-member.name = Name der Gruppe
|
|||||||
scm.group.remove-member.members = Zu l<>schende Mitglieder
|
scm.group.remove-member.members = Zu l<>schende Mitglieder
|
||||||
|
|
||||||
groupNotFound = Gruppe konnte nicht gefunden werden
|
groupNotFound = Gruppe konnte nicht gefunden werden
|
||||||
|
|
||||||
|
## Plugin
|
||||||
|
scm.plugin.usage.description.0 = Ressourcenkommando f<>r alle Pluginverwaltungsaktionen
|
||||||
|
scmPluginNotAvailable = Dieses Plugin ist nicht verf<72>gbar. Bitte pr<70>fen Sie Ihre Eingabe.
|
||||||
|
scmPluginNotInstalled = Dieses Plugin ist nicht installiert. Bitte pr<70>fen Sie Ihre Eingabe.
|
||||||
|
scmPluginAlreadyInstalled = Dieses Plugin ist bereits installiert.
|
||||||
|
scmPluginAdded = Das Plugin wurde hinzugef<65>gt.
|
||||||
|
scmPluginRemoved = Das Plugin wurde entfernt.
|
||||||
|
scmPluginUpdated = Das Plugin wurde aktualisiert.
|
||||||
|
scmPluginsUpdated = Alle verf<72>gbaren Plugins wurden aktualisiert.
|
||||||
|
scmPluginNotRemoved = Dieses Plugin konnte nicht entfernt werden. Andere installierte Plugins k<>nnten von diesem Plugin abh<62>ngen.
|
||||||
|
scmPluginNotAdded = Dieses Plugin konnte nicht hinzugef<65>gt werden. Weitere Informationen finden Sie in den Server Logs.
|
||||||
|
scmPluginNotUpdatable = Dieses Plugin konnte nicht aktualisiert werden.
|
||||||
|
scmPluginsUpdateFailed = Plugin Aktualisierungen fehlgeschlagen. Weitere Informationen finden Sie in den Server Logs.
|
||||||
|
scmServerRestartRequired = Bitte starten Sie Ihren Server neu, um die Plugin <20>nderungen wirksam zu machen.
|
||||||
|
scmServerRestartTriggered = Server Neustart: <20>nderungen werden <20>bernommen...
|
||||||
|
scmServerRestartSkipped = Server Neustart <20>bersprungen: Keine <20>nderungen gefunden.
|
||||||
|
scmServerRestartConfirmation = Bitte best<73>tigen Sie den Server Neustart mit --yes.
|
||||||
|
scmPluginsNotPending = Keine ausstehenden Plugin <20>nderungen gefunden.
|
||||||
|
scmPendingPluginsCancelled = Alle ausstehenden Plugin <20>nderungen wurden zur<75>ckgesetzt.
|
||||||
|
|
||||||
|
### List plugins
|
||||||
|
scm.plugin.name = Name
|
||||||
|
scm.plugin.displayName = Anzeigename
|
||||||
|
scm.plugin.installedVersion = Installiert
|
||||||
|
scm.plugin.availableVersion = Verf<EFBFBD>gbar
|
||||||
|
scm.plugin.pending = <EFBFBD>nderung ausstehend
|
||||||
|
scm.plugin.list.usage.description.0 = Listet alle Plugins mit Versionen auf
|
||||||
|
scm.plugin.list.short = Zeigt nur die Plugin Namen an
|
||||||
|
|
||||||
|
### Add plugin
|
||||||
|
scm.plugin.add.usage.description.0 = Installiert das Plugin inklusive Abh<62>ngigkeiten
|
||||||
|
scm.plugin.add.apply = Macht alle Plugin <20>nderungen sofort wirksam (Server Neustart!)
|
||||||
|
|
||||||
|
### Remove plugin
|
||||||
|
scm.plugin.remove.usage.description.0 = Entfernt das Plugin
|
||||||
|
scm.plugin.remove.apply = Macht alle Plugin <20>nderungen sofort wirksam (Server Neustart!)
|
||||||
|
|
||||||
|
### Update plugin
|
||||||
|
scm.plugin.update.usage.description.0 = Aktualisiert das installierte Plugin
|
||||||
|
scm.plugin.update.apply = Macht alle Plugin <20>nderungen sofort wirksam (Server Neustart!)
|
||||||
|
|
||||||
|
### Update all plugins
|
||||||
|
scm.plugin.update-all.usage.description.0 = Aktualisiert alle installierten Plugins
|
||||||
|
scm.plugin.update-all.apply = Macht alle Plugin <20>nderungen sofort wirksam (Server Neustart!)
|
||||||
|
|
||||||
|
### Apply plugin changes
|
||||||
|
scm.plugin.apply.usage.description.0 = Macht alle ausstehenden Plugin <20>nderungen wirksam (Server Neustart!)
|
||||||
|
scm.plugin.restart = Server Neustart best<73>tigen
|
||||||
|
|
||||||
|
### Apply plugin changes
|
||||||
|
scm.plugin.cancel-pending.usage.description.0 = Setzt alle ausstehenden Plugin <20>nderungen zur<75>ck
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import sonia.scm.plugin.AvailablePlugin;
|
|||||||
import sonia.scm.plugin.AvailablePluginDescriptor;
|
import sonia.scm.plugin.AvailablePluginDescriptor;
|
||||||
import sonia.scm.plugin.InstalledPlugin;
|
import sonia.scm.plugin.InstalledPlugin;
|
||||||
import sonia.scm.plugin.InstalledPluginDescriptor;
|
import sonia.scm.plugin.InstalledPluginDescriptor;
|
||||||
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
import sonia.scm.plugin.PluginInformation;
|
import sonia.scm.plugin.PluginInformation;
|
||||||
import sonia.scm.plugin.PluginManager;
|
import sonia.scm.plugin.PluginManager;
|
||||||
import sonia.scm.web.RestDispatcher;
|
import sonia.scm.web.RestDispatcher;
|
||||||
@@ -65,7 +66,7 @@ import static org.mockito.Mockito.when;
|
|||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class PendingPluginResourceTest {
|
class PendingPluginResourceTest {
|
||||||
|
|
||||||
private RestDispatcher dispatcher = new RestDispatcher();
|
private final RestDispatcher dispatcher = new RestDispatcher();
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
ResourceLinks resourceLinks = ResourceLinksMock.createMock(create("/"));
|
ResourceLinks resourceLinks = ResourceLinksMock.createMock(create("/"));
|
||||||
@@ -125,23 +126,22 @@ class PendingPluginResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldGetEmptyPluginListsWithoutInstallLinkWhenNoPendingPluginsPresent() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldGetEmptyPluginListsWithoutInstallLinkWhenNoPendingPluginsPresent() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
AvailablePlugin availablePlugin = createAvailablePlugin("not-pending-plugin");
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
when(availablePlugin.isPending()).thenReturn(false);
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
when(pluginManager.getAvailable()).thenReturn(singletonList(availablePlugin));
|
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
|
|
||||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||||
assertThat(response.getContentAsString()).contains("\"_links\":{\"self\":{\"href\":\"/v2/plugins/pending\"}}");
|
assertThat(response.getContentAsString()).contains("\"_links\":{\"self\":{\"href\":\"/v2/plugins/pending\"}}");
|
||||||
assertThat(response.getContentAsString()).doesNotContain("not-pending-plugin");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldGetPendingAvailablePluginListWithInstallAndCancelLink() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldGetPendingAvailablePluginListWithInstallAndCancelLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
AvailablePlugin availablePlugin = createAvailablePlugin("pending-available-plugin");
|
AvailablePlugin availablePlugin = createAvailablePlugin("pending-available-plugin");
|
||||||
when(availablePlugin.isPending()).thenReturn(true);
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
when(pluginManager.getAvailable()).thenReturn(singletonList(availablePlugin));
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.getInstall()).thenReturn(singletonList(availablePlugin));
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
@@ -155,10 +155,11 @@ class PendingPluginResourceTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldGetPendingUpdatePluginListWithInstallLink() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldGetPendingUpdatePluginListWithInstallLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
AvailablePlugin availablePlugin = createAvailablePlugin("available-plugin");
|
AvailablePlugin availablePlugin = createAvailablePlugin("available-plugin");
|
||||||
when(availablePlugin.isPending()).thenReturn(true);
|
|
||||||
when(pluginManager.getAvailable()).thenReturn(singletonList(availablePlugin));
|
|
||||||
InstalledPlugin installedPlugin = createInstalledPlugin("available-plugin");
|
InstalledPlugin installedPlugin = createInstalledPlugin("available-plugin");
|
||||||
when(pluginManager.getInstalled()).thenReturn(singletonList(installedPlugin));
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.getUpdate()).thenReturn(singletonList(installedPlugin));
|
||||||
|
when(pendingPlugins.getInstall()).thenReturn(singletonList(availablePlugin));
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
@@ -170,10 +171,10 @@ class PendingPluginResourceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldGetPendingUninstallPluginListWithInstallLink() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldGetPendingUninstallPluginListWithInstallLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
when(pluginManager.getAvailable()).thenReturn(emptyList());
|
|
||||||
InstalledPlugin installedPlugin = createInstalledPlugin("uninstalled-plugin");
|
InstalledPlugin installedPlugin = createInstalledPlugin("uninstalled-plugin");
|
||||||
when(installedPlugin.isMarkedForUninstall()).thenReturn(true);
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
when(pluginManager.getInstalled()).thenReturn(singletonList(installedPlugin));
|
when(pendingPlugins.getUninstall()).thenReturn(singletonList(installedPlugin));
|
||||||
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
@@ -187,10 +188,10 @@ class PendingPluginResourceTest {
|
|||||||
void shouldNotReturnExecuteLinkIfRestartIsNotSupported() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldNotReturnExecuteLinkIfRestartIsNotSupported() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
when(restarter.isSupported()).thenReturn(false);
|
when(restarter.isSupported()).thenReturn(false);
|
||||||
|
|
||||||
when(pluginManager.getAvailable()).thenReturn(emptyList());
|
|
||||||
InstalledPlugin installedPlugin = createInstalledPlugin("uninstalled-plugin");
|
InstalledPlugin installedPlugin = createInstalledPlugin("uninstalled-plugin");
|
||||||
when(installedPlugin.isMarkedForUninstall()).thenReturn(true);
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
when(pluginManager.getInstalled()).thenReturn(singletonList(installedPlugin));
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.getUninstall()).thenReturn(singletonList(installedPlugin));
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
@@ -239,8 +240,9 @@ class PendingPluginResourceTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldGetPendingAvailablePluginListWithoutInstallAndCancelLink() throws URISyntaxException, UnsupportedEncodingException {
|
void shouldGetPendingAvailablePluginListWithoutInstallAndCancelLink() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
AvailablePlugin availablePlugin = createAvailablePlugin("pending-available-plugin");
|
AvailablePlugin availablePlugin = createAvailablePlugin("pending-available-plugin");
|
||||||
when(availablePlugin.isPending()).thenReturn(true);
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
when(pluginManager.getAvailable()).thenReturn(singletonList(availablePlugin));
|
when(pluginManager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.getInstall()).thenReturn(singletonList(availablePlugin));
|
||||||
|
|
||||||
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
MockHttpRequest request = MockHttpRequest.get("/v2/plugins/pending");
|
||||||
dispatcher.invoke(request, response);
|
dispatcher.invoke(request, response);
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ public class TemplateTestRenderer {
|
|||||||
return templateEngineFactory;
|
return templateEngineFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandLine.Model.CommandSpec getMockedSpeck() {
|
public CommandLine.Model.CommandSpec getMockedSpec() {
|
||||||
ResourceBundle resourceBundle = getResourceBundle();
|
ResourceBundle resourceBundle = getResourceBundle();
|
||||||
CommandLine.Model.CommandSpec mock = mock(CommandLine.Model.CommandSpec.class);
|
CommandLine.Model.CommandSpec mock = mock(CommandLine.Model.CommandSpec.class);
|
||||||
lenient().when(mock.resourceBundle()).thenReturn(resourceBundle);
|
lenient().when(mock.resourceBundle()).thenReturn(resourceBundle);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class GroupListCommandTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void initCommand() {
|
void initCommand() {
|
||||||
command = new GroupListCommand(testRenderer.createTemplateRenderer(), manager, beanMapper);
|
command = new GroupListCommand(testRenderer.createTemplateRenderer(), manager, beanMapper);
|
||||||
command.setSpec(testRenderer.getMockedSpeck());
|
command.setSpec(testRenderer.getMockedSpec());
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
import sonia.scm.plugin.PluginTestHelper;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginAddCommandTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginAddCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddPlugin() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createAvailable(pluginName))).when(manager).getAvailable(pluginName);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).install(pluginName, false);
|
||||||
|
verify(templateRenderer).renderPluginAdded(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldAddPluginWithRestart() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createAvailable(pluginName))).when(manager).getAvailable(pluginName);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.setApply(true);
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).install(pluginName, true);
|
||||||
|
verify(templateRenderer).renderPluginAdded(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginAlreadyInstalled() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(templateRenderer).renderPluginAlreadyInstalledError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginNotAvailable() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(Optional.empty()).when(manager).getAvailable(pluginName);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(templateRenderer).renderPluginNotAvailableError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginInstallationFailed() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createAvailable(pluginName))).when(manager).getAvailable(pluginName);
|
||||||
|
doThrow(RuntimeException.class).when(manager).install(pluginName, false);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
assertThrows(RuntimeException.class, () -> command.run());
|
||||||
|
|
||||||
|
verify(templateRenderer).renderPluginCouldNotBeAdded(pluginName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginApplyCommandTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginApplyCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRestartServer() {
|
||||||
|
command.setRestart(true);
|
||||||
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
when(manager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.existPendingChanges()).thenReturn(true);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).executePendingAndRestart();
|
||||||
|
verify(templateRenderer).renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRestartServerIfFlagIsMissing() {
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).executePendingAndRestart();
|
||||||
|
verify(templateRenderer).renderConfirmServerRestart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotRestartServerIfNoPendingChanges() {
|
||||||
|
command.setRestart(true);
|
||||||
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
when(manager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.existPendingChanges()).thenReturn(false);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).executePendingAndRestart();
|
||||||
|
verify(templateRenderer).renderSkipServerRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.cli.TemplateTestRenderer;
|
||||||
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
import sonia.scm.plugin.PluginTestHelper;
|
||||||
|
|
||||||
|
import static java.util.List.of;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginListCommandTest {
|
||||||
|
|
||||||
|
private final TemplateTestRenderer templateTestRenderer = new TemplateTestRenderer();
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
private PluginListCommand command;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void initCommand() {
|
||||||
|
command = new PluginListCommand(templateTestRenderer.createTemplateRenderer(), manager);
|
||||||
|
command.setSpec(templateTestRenderer.getMockedSpec());
|
||||||
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
doReturn(pendingPlugins).when(manager).getPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldListPlugins() {
|
||||||
|
doReturn(of(PluginTestHelper.createInstalled("scm-test-plugin"))).when(manager).getInstalled();
|
||||||
|
doReturn(of(
|
||||||
|
PluginTestHelper.createAvailable("scm-review-plugin"),
|
||||||
|
PluginTestHelper.createAvailable("scm-test-plugin", "1.1.0"))
|
||||||
|
).when(manager).getAvailable();
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
assertThat(templateTestRenderer.getStdOut())
|
||||||
|
.contains("NAME DISPLAY NAME AVAILABLE INSTALLED PENDING?")
|
||||||
|
.contains("scm-review-plugin 1.0")
|
||||||
|
.contains("scm-test-plugin 1.1.0 1.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldListPluginsAsShortList() {
|
||||||
|
doReturn(of(PluginTestHelper.createInstalled("scm-test-plugin"), PluginTestHelper.createInstalled("scm-archive-plugin"))).when(manager).getInstalled();
|
||||||
|
doReturn(of(
|
||||||
|
PluginTestHelper.createAvailable("scm-review-plugin"),
|
||||||
|
PluginTestHelper.createAvailable("scm-test-plugin", "1.1.0"))
|
||||||
|
).when(manager).getAvailable();
|
||||||
|
command.setUseShortTemplate(true);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
assertThat(templateTestRenderer.getStdOut())
|
||||||
|
.isEqualTo("scm-archive-plugin\nscm-review-plugin\nscm-test-plugin\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
import sonia.scm.plugin.PluginTestHelper;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginRemoveCommandTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginRemoveCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRemovePlugin() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
command.setName(pluginName);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).uninstall(pluginName, false);
|
||||||
|
verify(templateRenderer).renderPluginRemoved(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRemovePluginWithRestart() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.setApply(true);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).uninstall(pluginName, true);
|
||||||
|
verify(templateRenderer).renderPluginRemoved(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginNotInstalled() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
command.setName(pluginName);
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).uninstall(eq(pluginName), anyBoolean());
|
||||||
|
verify(templateRenderer).renderPluginNotInstalledError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginCouldNotBeRemoved() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
command.setName(pluginName);
|
||||||
|
doThrow(RuntimeException.class).when(manager).uninstall(pluginName, false);
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
assertThrows(RuntimeException.class, () -> command.run());
|
||||||
|
|
||||||
|
verify(templateRenderer).renderPluginCouldNotBeRemoved(pluginName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PendingPlugins;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginResetChangesCommandTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginResetChangesCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCancelPendingPlugins() {
|
||||||
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
when(manager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.existPendingChanges()).thenReturn(true);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).cancelPending();
|
||||||
|
verify(templateRenderer).renderPluginsReseted();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfNoPendingPlugins() {
|
||||||
|
PendingPlugins pendingPlugins = mock(PendingPlugins.class);
|
||||||
|
when(manager.getPending()).thenReturn(pendingPlugins);
|
||||||
|
when(pendingPlugins.existPendingChanges()).thenReturn(false);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).cancelPending();
|
||||||
|
verify(templateRenderer).renderNoPendingPlugins();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginUpdateAllCommandTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager pluginManager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginUpdateAllCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateAll() {
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(pluginManager).updateAll();
|
||||||
|
verify(templateRenderer).renderAllPluginsUpdated();
|
||||||
|
verify(templateRenderer).renderServerRestartRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateAllWithRestart() {
|
||||||
|
command.setApply(true);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(pluginManager).updateAll();
|
||||||
|
verify(pluginManager).executePendingAndRestart();
|
||||||
|
verify(templateRenderer).renderAllPluginsUpdated();
|
||||||
|
verify(templateRenderer).renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfUpdateFailed() {
|
||||||
|
doThrow(RuntimeException.class).when(pluginManager).updateAll();
|
||||||
|
|
||||||
|
assertThrows(RuntimeException.class, () -> command.run());
|
||||||
|
|
||||||
|
verify(templateRenderer).renderPluginsUpdateError();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonia.scm.plugin.cli;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import sonia.scm.plugin.PluginManager;
|
||||||
|
import sonia.scm.plugin.PluginTestHelper;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PluginUpdateCommandTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private PluginTemplateRenderer templateRenderer;
|
||||||
|
@Mock
|
||||||
|
private PluginManager manager;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private PluginUpdateCommand command;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateSinglePlugin() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(singletonList(PluginTestHelper.createInstalled(pluginName))).when(manager).getUpdatable();
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).install(pluginName, false);
|
||||||
|
verify(templateRenderer).renderPluginUpdated(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartRequired();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateSinglePluginWithRestart() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(singletonList(PluginTestHelper.createInstalled(pluginName))).when(manager).getUpdatable();
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
command.setApply(true);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager).install(pluginName, true);
|
||||||
|
verify(templateRenderer).renderPluginUpdated(pluginName);
|
||||||
|
verify(templateRenderer).renderServerRestartTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginNotInstalled() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.empty()).when(manager).getInstalled(pluginName);
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).install(eq(pluginName), anyBoolean());
|
||||||
|
verify(templateRenderer).renderPluginNotInstalledError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldRenderErrorIfPluginNotUpdatable() {
|
||||||
|
String pluginName = "scm-test-plugin";
|
||||||
|
doReturn(Optional.of(PluginTestHelper.createInstalled(pluginName))).when(manager).getInstalled(pluginName);
|
||||||
|
doReturn(emptyList()).when(manager).getUpdatable();
|
||||||
|
|
||||||
|
command.setName(pluginName);
|
||||||
|
|
||||||
|
command.run();
|
||||||
|
|
||||||
|
verify(manager, never()).install(eq(pluginName), anyBoolean());
|
||||||
|
verify(templateRenderer).renderPluginNotUpdatable(pluginName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -69,7 +69,7 @@ class UserListCommandTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldRenderShortTableInEnglish() {
|
void shouldRenderShortTableInEnglish() {
|
||||||
testRenderer.setLocale("en");
|
testRenderer.setLocale("en");
|
||||||
command.setSpec(testRenderer.getMockedSpeck());
|
command.setSpec(testRenderer.getMockedSpec());
|
||||||
command.setUseShortTemplate(true);
|
command.setUseShortTemplate(true);
|
||||||
|
|
||||||
command.run();
|
command.run();
|
||||||
@@ -81,7 +81,7 @@ class UserListCommandTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldRenderShortTableInGerman() {
|
void shouldRenderShortTableInGerman() {
|
||||||
testRenderer.setLocale("de");
|
testRenderer.setLocale("de");
|
||||||
command.setSpec(testRenderer.getMockedSpeck());
|
command.setSpec(testRenderer.getMockedSpec());
|
||||||
command.setUseShortTemplate(true);
|
command.setUseShortTemplate(true);
|
||||||
|
|
||||||
command.run();
|
command.run();
|
||||||
@@ -93,7 +93,7 @@ class UserListCommandTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldRenderLongTableInEnglish() {
|
void shouldRenderLongTableInEnglish() {
|
||||||
testRenderer.setLocale("en");
|
testRenderer.setLocale("en");
|
||||||
command.setSpec(testRenderer.getMockedSpeck());
|
command.setSpec(testRenderer.getMockedSpec());
|
||||||
|
|
||||||
command.run();
|
command.run();
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ class UserListCommandTest {
|
|||||||
@Test
|
@Test
|
||||||
void shouldRenderLongTableInGerman() {
|
void shouldRenderLongTableInGerman() {
|
||||||
testRenderer.setLocale("de");
|
testRenderer.setLocale("de");
|
||||||
command.setSpec(testRenderer.getMockedSpeck());
|
command.setSpec(testRenderer.getMockedSpec());
|
||||||
|
|
||||||
command.run();
|
command.run();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user