Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit 4ceb3ad

Browse files
committed
✨ Bump spring boot and shell (and others), add fix on completion
1 parent 1621b9a commit 4ceb3ad

File tree

5 files changed

+122
-31
lines changed

5 files changed

+122
-31
lines changed

samples/basic/src/main/java/com/github/fonimus/ssh/shell/basic/BasicCommands.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.github.fonimus.ssh.shell.PromptColor;
2020
import com.github.fonimus.ssh.shell.SimpleTable;
2121
import com.github.fonimus.ssh.shell.SshShellHelper;
22-
import com.github.fonimus.ssh.shell.auth.SshAuthentication;
2322
import com.github.fonimus.ssh.shell.commands.SshShellComponent;
2423
import lombok.AllArgsConstructor;
2524
import lombok.Data;
@@ -110,16 +109,6 @@ public Size size() {
110109
return helper.terminalSize();
111110
}
112111

113-
/**
114-
* Authentication example command
115-
*
116-
* @return principal
117-
*/
118-
@ShellMethod("Authentication command")
119-
public SshAuthentication authentication() {
120-
return helper.getAuthentication();
121-
}
122-
123112
/**
124113
* Simple table example command
125114
*

samples/complete/src/main/java/com/github/fonimus/ssh/shell/complete/CompleteCommands.java

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.github.fonimus.ssh.shell.interactive.KeyBinding;
2626
import com.github.fonimus.ssh.shell.providers.ExtendedFileValueProvider;
2727
import lombok.AllArgsConstructor;
28+
import lombok.Data;
29+
import lombok.NoArgsConstructor;
2830
import org.apache.sshd.server.Environment;
2931
import org.apache.sshd.server.session.ServerSession;
3032
import org.jline.terminal.Size;
@@ -40,6 +42,9 @@
4042
import org.springframework.shell.CompletionContext;
4143
import org.springframework.shell.CompletionProposal;
4244
import org.springframework.shell.standard.*;
45+
import org.springframework.shell.table.BorderStyle;
46+
import org.springframework.shell.table.SimpleHorizontalAligner;
47+
import org.springframework.shell.table.SimpleVerticalAligner;
4348
import org.springframework.stereotype.Component;
4449

4550
import java.io.File;
@@ -62,17 +67,58 @@ public class CompleteCommands extends AbstractHealthIndicator {
6267
* Echo command
6368
*
6469
* @param message message to print
70+
* @param color color for the message
6571
* @return message
6672
*/
6773
@ShellMethod("Echo command")
68-
public String echo(String message, @ShellOption(defaultValue = ShellOption.NULL, valueProvider = EnumValueProvider.class) PromptColor color) {
74+
public String echo(
75+
@ShellOption(valueProvider = CustomValuesProvider.class) String message,
76+
@ShellOption(defaultValue = ShellOption.NULL, valueProvider = EnumValueProvider.class) PromptColor color
77+
) {
6978
if (color != null) {
7079
return new AttributedStringBuilder().append(message,
7180
AttributedStyle.DEFAULT.foreground(color.toJlineAttributedStyle())).toAnsi();
7281
}
7382
return message;
7483
}
7584

85+
/**
86+
* Wait for some time
87+
*
88+
* @param waitInMillis wait time
89+
* @return message
90+
*/
91+
@ShellMethod(key = "wait", value = "Wait command")
92+
public String waitCmd(long waitInMillis) {
93+
try {
94+
Thread.sleep(waitInMillis);
95+
} catch (InterruptedException e) {
96+
LOGGER.warn("Got interrupted");
97+
}
98+
return "Waited " + waitInMillis + " milliseconds";
99+
}
100+
101+
/**
102+
* Pojo command
103+
* <p>Try the post processors like pretty, grep with it</p>
104+
*
105+
* @return pojo
106+
*/
107+
@ShellMethod("Pojo command")
108+
public Pojo pojo() {
109+
return new Pojo("value1", "value2");
110+
}
111+
112+
/**
113+
* Confirmation example command
114+
*
115+
* @return welcome message
116+
*/
117+
@ShellMethod("Confirmation command")
118+
public String conf() {
119+
return helper.confirm("Are you sure ?") ? "Great ! Let's do it !" : "Such a shame ...";
120+
}
121+
76122
/**
77123
* Terminal size command example
78124
*
@@ -83,6 +129,56 @@ public Size size() {
83129
return helper.terminalSize();
84130
}
85131

132+
/**
133+
* Simple table example command
134+
*
135+
* @return principal
136+
*/
137+
@ShellMethod("Simple table command")
138+
public String tableSimple() {
139+
return helper.renderTable(SimpleTable.builder()
140+
.column("col1")
141+
.column("col2")
142+
.column("col3")
143+
.column("col4")
144+
.line(Arrays.asList("line1 col1", "line1 col2", "line1 col3", "line1 col4"))
145+
.line(Arrays.asList("line2 col1", "line2 col2", "line2 col3", "line2 col4"))
146+
.line(Arrays.asList("line3 col1", "line3 col2", "line3 col3", "line3 col4"))
147+
.line(Arrays.asList("line4 col1", "line4 col2", "line4 col3", "line4 col4"))
148+
.line(Arrays.asList("line5 col1", "line5 col2", "line5 col3", "line5 col4"))
149+
.line(Arrays.asList("line6 col1", "line6 col2", "line6 col3", "line6 col4"))
150+
.build());
151+
}
152+
153+
/**
154+
* Complex table example command
155+
*
156+
* @return principal
157+
*/
158+
@ShellMethod("Complex table command")
159+
public String tableComplex() {
160+
return helper.renderTable(SimpleTable.builder()
161+
.column("col1")
162+
.column("col2")
163+
.column("col3")
164+
.column("col4")
165+
.line(Arrays.asList("line1 col1", "line1 col2", "line1 col3", "line1 col4"))
166+
.line(Arrays.asList("line2 col1", "line2 col2", "line2 col3", "line2 col4"))
167+
.line(Arrays.asList("line3 col1", "line3 col2", "line3 col3", "line3 col4"))
168+
.line(Arrays.asList("line4 col1", "line4 col2", "line4 col3", "line4 col4"))
169+
.line(Arrays.asList("line5 col1", "line5 col2", "line5 col3", "line5 col4"))
170+
.line(Arrays.asList("line6 col1", "line6 col2", "line6 col3", "line6 col4"))
171+
.headerAligner(SimpleHorizontalAligner.right)
172+
.lineAligner(SimpleHorizontalAligner.left)
173+
.lineAligner(SimpleVerticalAligner.bottom)
174+
.useFullBorder(false)
175+
.borderStyle(BorderStyle.fancy_heavy_double_dash)
176+
.tableBuilderListener(tableBuilder -> {
177+
tableBuilder.addInnerBorder(BorderStyle.fancy_light_double_dash);
178+
tableBuilder.addOutlineBorder(BorderStyle.fancy_double);
179+
}).build());
180+
}
181+
86182
/**
87183
* Progress displays command example
88184
*
@@ -128,7 +224,7 @@ private void info(File file) {
128224
* @param delay delay in ms
129225
*/
130226
@ShellMethod("Interactive command")
131-
public void interactive(boolean fullscreen, @ShellOption(defaultValue = "3000") long delay) {
227+
public void interactive(@ShellOption(defaultValue = "false") boolean fullscreen, @ShellOption(defaultValue = "3000") long delay) {
132228

133229
KeyBinding binding = KeyBinding.builder()
134230
.description("K binding example")
@@ -177,16 +273,6 @@ public String welcome() {
177273
return "Hello, '" + name + "' !";
178274
}
179275

180-
/**
181-
* Confirmation example command
182-
*
183-
* @return welcome message
184-
*/
185-
@ShellMethod("Confirmation command")
186-
public String conf() {
187-
return helper.confirm("Are you sure ?") ? "Great ! Let's do it !" : "Such a shame ...";
188-
}
189-
190276
/**
191277
* Admin only example command
192278
*
@@ -309,6 +395,16 @@ public void logWithLongDuration() throws InterruptedException {
309395
protected void doHealthCheck(Health.Builder builder) {
310396
builder.up().withDetail("a-key", "a-value");
311397
}
398+
399+
@Data
400+
@NoArgsConstructor
401+
@AllArgsConstructor
402+
private static class Pojo {
403+
404+
private String key1;
405+
406+
private String key2;
407+
}
312408
}
313409

314410
@Component

samples/complete/src/main/resources/application.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ spring:
66
second-datasource:
77
url: jdbc:h2:mem:testdb2
88
shell.interactive.enabled: false
9-
main.lazy-initialization: true
9+
main.lazy-initialization: false
1010

1111
ssh:
1212
shell:
1313
authentication: security
1414
auth-provider-bean-name: customAuthManager
1515
authorized-public-keys: classpath:.ssh/authorized.keys
16-
extended-file-provider: false
1716
commands:
1817
actuator:
1918
excludes:

starter/src/main/java/com/github/fonimus/ssh/shell/ExtendedShell.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,25 @@ public List<CompletionProposal> complete(CompletionContext context) {
170170
CompletionContext argsContext = context.commandRegistration(registration);
171171

172172
final List<String> words = context.getWords().stream().filter(StringUtils::hasText).collect(Collectors.toList());
173-
String lastWord = words.isEmpty() ? null : words.get(words.size() - 1);
173+
String lastNotEmptyWord = words.isEmpty() ? null : words.get(words.size() - 1);
174174

175175
List<CommandOption> matchedArgOptions = new ArrayList<>();
176-
if (lastWord != null) {
176+
if (lastNotEmptyWord != null) {
177177
// last word used instead of first to check if matching args
178-
matchedArgOptions.addAll(duplicatedMatchOptions(registration.getOptions(), lastWord));
178+
matchedArgOptions.addAll(duplicatedMatchOptions(registration.getOptions(), lastNotEmptyWord));
179179
}
180180
if (matchedArgOptions.isEmpty()) {
181181
// only add command options if last word did not match option
182182
for (CompletionResolver resolver : completionResolvers) {
183183
List<CompletionProposal> resolved = resolver.apply(argsContext);
184184
candidates.addAll(resolved.stream().filter(cp -> !words.contains(cp.value())).collect(Collectors.toList()));
185185
}
186+
// try to check if previous word before last word is an option and last word is not empty
187+
String lastOption = words.isEmpty() || words.size() < 2 ? null : words.get(words.size() - 2);
188+
String lastWord = context.getWords().isEmpty() ? null : context.getWords().get(context.getWords().size() - 1);
189+
if (lastOption != null && StringUtils.hasText(lastWord)) {
190+
matchedArgOptions.addAll(duplicatedMatchOptions(registration.getOptions(), lastOption));
191+
}
186192
}
187193

188194
List<CompletionProposal> argProposals = matchedArgOptions.stream()

starter/src/main/java/com/github/fonimus/ssh/shell/SshShellAutoConfiguration.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.shell.context.InteractionMode;
4848
import org.springframework.shell.context.ShellContext;
4949
import org.springframework.shell.jline.PromptProvider;
50+
import org.springframework.shell.standard.ValueProvider;
5051

5152
import javax.annotation.PostConstruct;
5253
import java.util.List;
@@ -114,10 +115,10 @@ public void init() {
114115
@ConditionalOnProperty(value = "spring.main.lazy-initialization", havingValue = "true")
115116
public ApplicationListener<ContextRefreshedEvent> lazyInitApplicationListener() {
116117
return event -> {
117-
LOGGER.info("Lazy initialization enabled, calling configuration bean explicitly to start ssh server");
118+
LOGGER.info("Lazy initialization enabled, calling configuration beans explicitly to start ssh server and initialize shell correctly");
118119
context.getBean(SshShellConfiguration.SshServerLifecycle.class);
119-
// also need to get terminal to initialize thread context of main thread
120-
context.getBean(Terminal.class);
120+
context.getBeansOfType(Terminal.class);
121+
context.getBeansOfType(ValueProvider.class);
121122
};
122123
}
123124

0 commit comments

Comments
 (0)