Make history file path configurable
- UserConfigPathProvider interface to provid "user-level" config directory which can be used within a shell. - New options which can be used to configure behaviour. spring.shell.history.enabled spring.shell.history.name spring.shell.config.location spring.shell.config.env - Fixes #417
This commit is contained in:
committed by
GitHub
parent
f2d97ae2de
commit
6a1158dc12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
* Copyright 2021-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@ package org.springframework.shell.boot;
|
||||
|
||||
import org.jline.reader.impl.history.DefaultHistory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -30,7 +29,7 @@ public class JLineAutoConfiguration {
|
||||
public static class JLineHistoryConfiguration {
|
||||
|
||||
@Bean
|
||||
public org.jline.reader.History history(@Value("${spring.application.name:spring-shell}.log") String historyPath) {
|
||||
public org.jline.reader.History history() {
|
||||
return new DefaultHistory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -28,17 +29,25 @@ import org.jline.terminal.Terminal;
|
||||
import org.jline.utils.AttributedString;
|
||||
import org.jline.utils.AttributedStringBuilder;
|
||||
import org.jline.utils.AttributedStyle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.ContextClosedEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.shell.command.CommandCatalog;
|
||||
import org.springframework.shell.config.UserConfigPathProvider;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SpringShellProperties.class)
|
||||
public class LineReaderAutoConfiguration {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(LineReaderAutoConfiguration.class);
|
||||
|
||||
private Terminal terminal;
|
||||
|
||||
private Completer completer;
|
||||
@@ -50,15 +59,21 @@ public class LineReaderAutoConfiguration {
|
||||
private org.jline.reader.History jLineHistory;
|
||||
|
||||
@Value("${spring.application.name:spring-shell}.log")
|
||||
private String historyPath;
|
||||
private String fallbackHistoryFileName;
|
||||
|
||||
private SpringShellProperties springShellProperties;
|
||||
private UserConfigPathProvider userConfigPathProvider;
|
||||
|
||||
public LineReaderAutoConfiguration(Terminal terminal, Completer completer, Parser parser,
|
||||
CommandCatalog commandRegistry, org.jline.reader.History jLineHistory) {
|
||||
CommandCatalog commandRegistry, org.jline.reader.History jLineHistory,
|
||||
SpringShellProperties springShellProperties, UserConfigPathProvider userConfigPathProvider) {
|
||||
this.terminal = terminal;
|
||||
this.completer = completer;
|
||||
this.parser = parser;
|
||||
this.commandRegistry = commandRegistry;
|
||||
this.jLineHistory = jLineHistory;
|
||||
this.springShellProperties = springShellProperties;
|
||||
this.userConfigPathProvider = userConfigPathProvider;
|
||||
}
|
||||
|
||||
@EventListener
|
||||
@@ -104,7 +119,20 @@ public class LineReaderAutoConfiguration {
|
||||
.parser(parser);
|
||||
|
||||
LineReader lineReader = lineReaderBuilder.build();
|
||||
lineReader.setVariable(LineReader.HISTORY_FILE, Paths.get(historyPath));
|
||||
if (this.springShellProperties.getHistory().isEnabled()) {
|
||||
// Discover history location
|
||||
Path userConfigPath = this.userConfigPathProvider.provide();
|
||||
log.debug("Resolved userConfigPath {}", userConfigPath);
|
||||
String historyFileName = this.springShellProperties.getHistory().getName();
|
||||
if (!StringUtils.hasText(historyFileName)) {
|
||||
historyFileName = fallbackHistoryFileName;
|
||||
}
|
||||
log.debug("Resolved historyFileName {}", historyFileName);
|
||||
String historyPath = userConfigPath.resolve(historyFileName).toAbsolutePath().toString();
|
||||
log.debug("Resolved historyPath {}", historyPath);
|
||||
// set history file
|
||||
lineReader.setVariable(LineReader.HISTORY_FILE, Paths.get(historyPath));
|
||||
}
|
||||
lineReader.unsetOpt(LineReader.Option.INSERT_TAB); // This allows completion on an empty buffer, rather than inserting a tab
|
||||
jLineHistory.attach(lineReader);
|
||||
return lineReader;
|
||||
|
||||
@@ -25,20 +25,38 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ConfigurationProperties(prefix = "spring.shell")
|
||||
public class SpringShellProperties {
|
||||
|
||||
private History history = new History();
|
||||
private Config config = new Config();
|
||||
private Script script = new Script();
|
||||
private Interactive interactive = new Interactive();
|
||||
private Noninteractive noninteractive = new Noninteractive();
|
||||
private Theme theme = new Theme();
|
||||
private Command command = new Command();
|
||||
|
||||
public void setScript(Script script) {
|
||||
this.script = script;
|
||||
public void setConfig(Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public History getHistory() {
|
||||
return history;
|
||||
}
|
||||
|
||||
public void setHistory(History history) {
|
||||
this.history = history;
|
||||
}
|
||||
|
||||
public Script getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public void setScript(Script script) {
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public void setInteractive(Interactive interactive) {
|
||||
this.interactive = interactive;
|
||||
}
|
||||
@@ -71,6 +89,50 @@ public class SpringShellProperties {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private String env;
|
||||
private String location;
|
||||
|
||||
public String getEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
public void setEnv(String env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public void setLocation(String location) {
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
public static class History {
|
||||
|
||||
private String name;
|
||||
private boolean enabled = true;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Script {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.config.UserConfigPathProvider;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(SpringShellProperties.class)
|
||||
public class UserConfigAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(UserConfigPathProvider.class)
|
||||
public UserConfigPathProvider userConfigPathProvider(SpringShellProperties springShellProperties) {
|
||||
return () -> {
|
||||
LocationResolver resolver = new LocationResolver(springShellProperties.getConfig().getEnv(),
|
||||
springShellProperties.getConfig().getLocation());
|
||||
return resolver.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
static class LocationResolver {
|
||||
|
||||
private final static String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
|
||||
private final static String APP_DATA = "APP_DATA";
|
||||
private static final String USERCONFIG_PLACEHOLDER = "{userconfig}";
|
||||
private Function<String, Path> pathProvider = (path) -> Paths.get(path);
|
||||
private final String configDirEnv;
|
||||
private final String configDirLocation;
|
||||
|
||||
LocationResolver(String configDirEnv, String configDirLocation) {
|
||||
this.configDirEnv = configDirEnv;
|
||||
this.configDirLocation = configDirLocation;
|
||||
}
|
||||
|
||||
Path resolve() {
|
||||
String location;
|
||||
if (StringUtils.hasText(configDirEnv) && StringUtils.hasText(System.getenv(configDirEnv))) {
|
||||
location = System.getenv(configDirEnv);
|
||||
}
|
||||
else if (StringUtils.hasText(configDirLocation)) {
|
||||
location = configDirLocation;
|
||||
}
|
||||
else {
|
||||
location = "";
|
||||
}
|
||||
if (usesUserConfigLocation(location)) {
|
||||
location = resolveUserConfigLocation(location);
|
||||
}
|
||||
return pathProvider.apply(location);
|
||||
}
|
||||
|
||||
private boolean usesUserConfigLocation(String location) {
|
||||
return location.contains(USERCONFIG_PLACEHOLDER);
|
||||
}
|
||||
|
||||
private String resolveUserConfigLocation(String location) {
|
||||
String userConfigHome = "";
|
||||
if (StringUtils.hasText(System.getenv(XDG_CONFIG_HOME))) {
|
||||
userConfigHome = System.getenv(XDG_CONFIG_HOME);
|
||||
}
|
||||
else if (isWindows() && StringUtils.hasText(System.getenv(APP_DATA))) {
|
||||
userConfigHome = System.getenv(APP_DATA);
|
||||
}
|
||||
else {
|
||||
userConfigHome = System.getProperty("user.home") + "/" + ".config";
|
||||
}
|
||||
return location.replace(USERCONFIG_PLACEHOLDER, userConfigHome);
|
||||
}
|
||||
|
||||
private boolean isWindows() {
|
||||
String os = System.getProperty("os.name");
|
||||
return os.startsWith("Windows");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ org.springframework.shell.boot.ApplicationRunnerAutoConfiguration,\
|
||||
org.springframework.shell.boot.CommandCatalogAutoConfiguration,\
|
||||
org.springframework.shell.boot.LineReaderAutoConfiguration,\
|
||||
org.springframework.shell.boot.CompleterAutoConfiguration,\
|
||||
org.springframework.shell.boot.UserConfigAutoConfiguration,\
|
||||
org.springframework.shell.boot.JLineAutoConfiguration,\
|
||||
org.springframework.shell.boot.JLineShellAutoConfiguration,\
|
||||
org.springframework.shell.boot.ParameterResolverAutoConfiguration,\
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.jline.reader.Completer;
|
||||
import org.jline.reader.History;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.Parser;
|
||||
import org.jline.terminal.Size;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.shell.command.CommandCatalog;
|
||||
import org.springframework.shell.config.UserConfigPathProvider;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class LineReaderAutoConfigurationTests {
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(LineReaderAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void testLineReaderCreated() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(MockConfiguration.class)
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(LineReader.class);
|
||||
LineReader lineReader = context.getBean(LineReader.class);
|
||||
assertThat(lineReader.getVariable(LineReader.HISTORY_FILE)).asString().contains("spring-shell.log");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLineReaderCreatedNoHistoryFile() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(MockConfiguration.class)
|
||||
.withPropertyValues("spring.shell.history.enabled=false")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(LineReader.class);
|
||||
LineReader lineReader = context.getBean(LineReader.class);
|
||||
assertThat(lineReader.getVariable(LineReader.HISTORY_FILE)).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLineReaderCreatedCustomHistoryFile() {
|
||||
this.contextRunner
|
||||
.withUserConfiguration(MockConfiguration.class)
|
||||
.withPropertyValues("spring.shell.history.name=fakehistory.txt")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(LineReader.class);
|
||||
LineReader lineReader = context.getBean(LineReader.class);
|
||||
assertThat(lineReader.getVariable(LineReader.HISTORY_FILE)).asString().contains("fakehistory.txt");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class MockConfiguration {
|
||||
|
||||
@Bean
|
||||
Terminal mockTerminal() {
|
||||
Terminal terminal = mock(Terminal.class);
|
||||
when(terminal.getBufferSize()).thenReturn(new Size());
|
||||
return terminal;
|
||||
}
|
||||
|
||||
@Bean
|
||||
Completer mockCompleter() {
|
||||
return mock(Completer.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
Parser mockParser() {
|
||||
return mock(Parser.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CommandCatalog mockCommandCatalog() {
|
||||
return mock(CommandCatalog.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
History mockHistory() {
|
||||
return mock(History.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
UserConfigPathProvider mockUserConfigPathProvider() {
|
||||
return () -> Paths.get("mockpath");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,10 @@ public class SpringShellPropertiesTests {
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
assertThat(properties.getHistory().isEnabled()).isTrue();
|
||||
assertThat(properties.getHistory().getName()).isNull();
|
||||
assertThat(properties.getConfig().getLocation()).isNull();
|
||||
assertThat(properties.getConfig().getEnv()).isNull();
|
||||
assertThat(properties.getScript().isEnabled()).isTrue();
|
||||
assertThat(properties.getInteractive().isEnabled()).isTrue();
|
||||
assertThat(properties.getNoninteractive().isEnabled()).isTrue();
|
||||
@@ -63,6 +67,10 @@ public class SpringShellPropertiesTests {
|
||||
@Test
|
||||
public void setProperties() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.shell.history.enabled=false")
|
||||
.withPropertyValues("spring.shell.history.name=fakename")
|
||||
.withPropertyValues("spring.shell.config.location=fakelocation")
|
||||
.withPropertyValues("spring.shell.config.env=FAKE_ENV")
|
||||
.withPropertyValues("spring.shell.script.enabled=false")
|
||||
.withPropertyValues("spring.shell.interactive.enabled=false")
|
||||
.withPropertyValues("spring.shell.noninteractive.enabled=false")
|
||||
@@ -90,6 +98,10 @@ public class SpringShellPropertiesTests {
|
||||
.withUserConfiguration(Config1.class)
|
||||
.run((context) -> {
|
||||
SpringShellProperties properties = context.getBean(SpringShellProperties.class);
|
||||
assertThat(properties.getHistory().isEnabled()).isFalse();
|
||||
assertThat(properties.getHistory().getName()).isEqualTo("fakename");
|
||||
assertThat(properties.getConfig().getLocation()).isEqualTo("fakelocation");
|
||||
assertThat(properties.getConfig().getEnv()).isEqualTo("FAKE_ENV");
|
||||
assertThat(properties.getScript().isEnabled()).isFalse();
|
||||
assertThat(properties.getInteractive().isEnabled()).isFalse();
|
||||
assertThat(properties.getNoninteractive().isEnabled()).isFalse();
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.boot;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.shell.config.UserConfigPathProvider;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class UserConfigAutoConfigurationTests {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(UserConfigAutoConfigurationTests.class);
|
||||
|
||||
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withConfiguration(AutoConfigurations.of(UserConfigAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void testDefaults() {
|
||||
this.contextRunner
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(UserConfigPathProvider.class);
|
||||
UserConfigPathProvider provider = context.getBean(UserConfigPathProvider.class);
|
||||
Path path = provider.provide();
|
||||
assertThat(path).isNotNull();
|
||||
log.info("Path testDefaults: {}", path.toAbsolutePath());
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserConfig() {
|
||||
this.contextRunner
|
||||
.withPropertyValues("spring.shell.config.location={userconfig}/test")
|
||||
.run(context -> {
|
||||
assertThat(context).hasSingleBean(UserConfigPathProvider.class);
|
||||
UserConfigPathProvider provider = context.getBean(UserConfigPathProvider.class);
|
||||
Path path = provider.provide();
|
||||
assertThat(path).isNotNull();
|
||||
log.info("Path testUserConfig: {}", path.toAbsolutePath());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.config;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Interface providing a {@link Path} to a location where
|
||||
* user level runtime configuration files are strored.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface UserConfigPathProvider {
|
||||
|
||||
/**
|
||||
* Provides a path to a user config location.
|
||||
*
|
||||
* @return a path to user config location
|
||||
*/
|
||||
Path provide();
|
||||
}
|
||||
@@ -2,6 +2,11 @@ spring:
|
||||
main:
|
||||
banner-mode: off
|
||||
shell:
|
||||
config:
|
||||
env: SPRING_SHELL_SAMPLES_USER_HOME
|
||||
location: "{userconfig}/spring-shell-samples"
|
||||
history:
|
||||
name: spring-shell-samples-history.log
|
||||
command:
|
||||
completion:
|
||||
root-command: spring-shell-samples
|
||||
|
||||
Reference in New Issue
Block a user