Compare commits

...
5 Commits
Author SHA1 Message Date
CyberShell 5f6459d5be v0.13.1
ci/woodpecker/push/publish-docs Pipeline is pending
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/tag/gitea Pipeline was successful
ci/woodpecker/tag/publish-docs Pipeline was successful
ci/woodpecker/release/publish-docs Pipeline was successful
2026-08-15 09:53:03 -05:00
CyberShell 1d2a3a8306 v0.13.0
ci/woodpecker/push/publish-docs Pipeline failed
ci/woodpecker/tag/gitea Pipeline failed
ci/woodpecker/tag/publish-docs Pipeline failed
2026-08-15 00:35:57 -05:00
CyberShell 045dedef67 v0.13.0
ci/woodpecker/push/go-lint Pipeline failed
2026-08-15 00:34:13 -05:00
CyberShell 60989a3905 fix error hooks and add listing everything
ci/woodpecker/push/go-lint Pipeline failed
2026-08-15 00:30:55 -05:00
CyberShell e789924547 fix error hooks and add listing everything
ci/woodpecker/push/go-lint Pipeline failed
ci/woodpecker/push/publish-docs Pipeline failed
2026-08-15 00:16:05 -05:00
21 changed files with 233 additions and 139 deletions
+5
View File
@@ -0,0 +1,5 @@
## v0.13.0 - 2026-08-15
### Changed
* List subcommands print out everything with no arguments
### Fixed
* Error hooks no longer return after one is executed
+3
View File
@@ -0,0 +1,3 @@
## v0.13.1 - 2026-08-15
### Changed
* Print help for exec command if no args given
+10
View File
@@ -6,6 +6,16 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
and is generated by [Changie](https://github.com/miniscruff/changie). and is generated by [Changie](https://github.com/miniscruff/changie).
## v0.13.1 - 2026-08-15
### Changed
* Print help for exec command if no args given
## v0.13.0 - 2026-08-15
### Changed
* List subcommands print out everything with no arguments
### Fixed
* Error hooks no longer return after one is executed
## v0.12.1 - 2026-03-23 ## v0.12.1 - 2026-03-23
### Added ### Added
* Command output respects `command.[name].output.toLog` when standard output is enabled * Command output respects `command.[name].output.toLog` when standard output is enabled
+3 -1
View File
@@ -5,6 +5,8 @@
package cmd package cmd
import ( import (
"fmt"
"git.andrewnw.xyz/CyberShell/backy/pkg/backy" "git.andrewnw.xyz/CyberShell/backy/pkg/backy"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging" "git.andrewnw.xyz/CyberShell/backy/pkg/logging"
@@ -29,7 +31,7 @@ func execute(cmd *cobra.Command, args []string) {
parseS3Config() parseS3Config()
if len(args) < 1 { if len(args) < 1 {
logging.ExitWithMSG("Please provide a command to run. Pass --help to see options.", 1, nil) logging.ExitWithMSG(fmt.Sprintf("Please provide a command to run.\n\n%s", cmd.Long), 1, nil)
} }
opts := backy.NewConfigOptions(configFile, opts := backy.NewConfigOptions(configFile,
+1 -1
View File
@@ -9,7 +9,7 @@ import (
var ( var (
hostExecCommand = &cobra.Command{ hostExecCommand = &cobra.Command{
Use: "host [--command=command1 --command=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] ", Use: "host [--command=command1 --command=command2 ... | -c command1 -c command2 ...] [--hosts=host1 --hosts=hosts2 ... | -m host1 -m host2 ...] ",
Short: "Runs command defined in config file on the hosts in order specified.", Short: "Specify command(s) defined in config file on the host in order specified.",
Long: "Host executes specified commands on the hosts defined in config file.\nUse the --commands or -c flag to choose the commands.", Long: "Host executes specified commands on the hosts defined in config file.\nUse the --commands or -c flag to choose the commands.",
Run: Host, Run: Host,
} }
+42 -28
View File
@@ -5,8 +5,10 @@
package cmd package cmd
import ( import (
"fmt"
"os"
"git.andrewnw.xyz/CyberShell/backy/pkg/backy" "git.andrewnw.xyz/CyberShell/backy/pkg/backy"
"git.andrewnw.xyz/CyberShell/backy/pkg/logging"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@@ -15,28 +17,26 @@ var (
listCmd = &cobra.Command{ listCmd = &cobra.Command{
Use: "list [command]", Use: "list [command]",
Short: "List commands, lists, or hosts defined in config file.", Short: "List commands, lists, or hosts defined in config file.",
Long: "List commands, lists, or hosts defined in config file", Long: "List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists",
} }
listCmds = &cobra.Command{ listCmds = &cobra.Command{
Use: "cmds [cmd1 cmd2 cmd3...]", Use: "cmds [cmd1 cmd2 cmd3...]",
Short: "List commands defined in config file.", Short: "Prints commands defined in config file.",
Long: "List commands defined in config file", Long: "Prints commands defined in config file. Pass no arguments to print all commands",
Run: ListCommands, Run: ListCommands,
} }
listCmdLists = &cobra.Command{ listCmdLists = &cobra.Command{
Use: "lists [list1 list2 ...]", Use: "lists [list1 list2 ...]",
Short: "List lists defined in config file.", Short: "Prints lists defined in config file.",
Long: "List lists defined in config file", Long: "Prints lists defined in config file. Pass no arguments to print all lists",
Run: ListCommandLists, Run: ListCommandLists,
} }
) )
var listsToList []string
var cmdsToList []string
func init() { func init() {
listCmd.AddCommand(listCmds, listCmdLists) listCmd.AddCommand(listCmds, listCmdLists)
parseS3Config()
} }
@@ -46,13 +46,6 @@ func ListCommands(cmd *cobra.Command, args []string) {
// - cmds // - cmds
// - lists // - lists
// - if none, list all commands // - if none, list all commands
if len(args) > 0 {
cmdsToList = args
} else {
logging.ExitWithMSG("Error: list cmds subcommand needs commands to list", 1, nil)
}
parseS3Config()
opts := backy.NewConfigOptions(configFile, opts := backy.NewConfigOptions(configFile,
backy.SetLogFile(logFile), backy.SetLogFile(logFile),
@@ -61,21 +54,28 @@ func ListCommands(cmd *cobra.Command, args []string) {
opts.InitConfig() opts.InitConfig()
opts.ParseConfigurationFile() opts.ParseConfigurationFile()
for _, v := range cmdsToList { if len(args) > 0 {
opts.ListCommand(v) for _, v := range args {
opts.ListCommand(v)
}
os.Exit(0)
}
if len(opts.Cmds) == 0 {
fmt.Println("No commands defined in config file")
os.Exit(1)
}
for c := range opts.Cmds {
println()
println()
println("---------------------------------------------------------------------------------")
opts.ListCommand(c)
} }
} }
func ListCommandLists(cmd *cobra.Command, args []string) { func ListCommandLists(cmd *cobra.Command, args []string) {
parseS3Config()
if len(args) > 0 {
listsToList = args
} else {
logging.ExitWithMSG("Error: lists subcommand needs lists", 1, nil)
}
opts := backy.NewConfigOptions(configFile, opts := backy.NewConfigOptions(configFile,
backy.SetLogFile(logFile), backy.SetLogFile(logFile),
backy.SetHostsConfigFile(hostsConfigFile)) backy.SetHostsConfigFile(hostsConfigFile))
@@ -83,8 +83,22 @@ func ListCommandLists(cmd *cobra.Command, args []string) {
opts.InitConfig() opts.InitConfig()
opts.ParseConfigurationFile() opts.ParseConfigurationFile()
for _, v := range listsToList { if len(args) > 0 {
opts.ListCommandList(v) for _, v := range args {
opts.ListCommandList(v)
}
os.Exit(0)
}
if len(opts.CmdConfigLists) == 0 {
fmt.Println("No command lists defined in config file")
os.Exit(1)
}
for c := range opts.CmdConfigLists {
println()
println()
println("---------------------------------------------------------------------------------")
opts.ListCommandList(c)
} }
} }
+1 -1
View File
@@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
const versionStr = "0.12.1" const versionStr = "0.13.1"
var ( var (
versionCmd = &cobra.Command{ versionCmd = &cobra.Command{
+6 -6
View File
@@ -88,7 +88,7 @@ Usage:
backy exec [command] backy exec [command]
Available Commands: Available Commands:
host Runs command defined in config file on the hosts in order specified. host Specify command(s) defined in config file on the host in order specified.
hosts Runs command defined in config file on the hosts in order specified. hosts Runs command defined in config file on the hosts in order specified.
Flags: Flags:
@@ -153,14 +153,14 @@ Global Flags:
## list ## list
``` ```
List commands, lists, or hosts defined in config file List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists
Usage: Usage:
backy list [command] backy list [command]
Available Commands: Available Commands:
cmds List commands defined in config file. cmds Prints commands defined in config file.
lists List lists defined in config file. lists Prints lists defined in config file.
Flags: Flags:
-h, --help help for list -h, --help help for list
@@ -178,7 +178,7 @@ Use "backy list [command] --help" for more information about a command.
## list cmds ## list cmds
``` ```
List commands defined in config file Prints commands defined in config file. Pass no arguments to print all commands
Usage: Usage:
backy list cmds [cmd1 cmd2 cmd3...] [flags] backy list cmds [cmd1 cmd2 cmd3...] [flags]
@@ -197,7 +197,7 @@ Global Flags:
## list lists ## list lists
``` ```
List lists defined in config file Prints lists defined in config file. Pass no arguments to print all lists
Usage: Usage:
backy list lists [list1 list2 ...] [flags] backy list lists [list1 list2 ...] [flags]
+13 -4
View File
@@ -2,14 +2,23 @@
title: Exec title: Exec
--- ---
The `exec` subcommand can do some things that the configuration file can't do yet. The command `exec host` can execute commands on many hosts. ~~The `exec` subcommand can do some things that the configuration file can't do yet. The command `exec host` can execute commands on many hosts.~~
{{% notice info %}}
For now only the `exec host` sub-command is implemented. More to come.
{{% /notice %}}
{{% notice warning %}}
The config file's `hosts` are overridden by this command. Hooks are not altered. For consistent results, create a local config file with only the hosts you want to use.
{{% /notice %}}
`exec host` takes the following arguments: `exec host` takes the following arguments:
```sh ```sh
-c, --commands strings Accepts space-separated names of commands. -c, --command stringArray Accepts space-separated names of commands. Specify multiple times for multiple commands.
-h, --help help for host -h, --help help for host
-m, --hosts strings Accepts space-separated names of hosts. -m, --hosts stringArray Accepts space-separated names of hosts. Specify multiple times for multiple hosts.
``` ```
The commands have to be defined in the config file. The hosts need to at least be in the ssh_config(5) file. The commands have to be defined in the config file. The hosts need to at least be in the ssh_config(5) file.
+3 -3
View File
@@ -3,7 +3,7 @@ title: List
--- ---
List commands, lists, or hosts defined in config file List commands, lists, or hosts defined in config file. The subcommands take zero or more arguments to print specific commands or lists
Usage: Usage:
``` ```
@@ -11,8 +11,8 @@ Usage:
``` ```
Available Commands: Available Commands:
cmds List commands defined in config file. cmds Prints commands defined in config file.
lists List lists defined in config file. lists Prints lists defined in config file.
Flags: Flags:
``` ```
-1
View File
@@ -944,7 +944,6 @@ func (cmd *Command) ExecuteHooks(hookType string, opts *ConfigOpts) {
cmdLogger.Info().Msgf("Running error hook command %s", v) cmdLogger.Info().Msgf("Running error hook command %s", v)
// URGENT: Never returns // URGENT: Never returns
_, _ = errCmd.RunCmd(cmdLogger, opts) _, _ = errCmd.RunCmd(cmdLogger, opts)
return
} }
case "success": case "success":
+4
View File
@@ -101,6 +101,10 @@ func (r *RemoteFileCommandExecutor) copyFile(source, destination string, Perms f
return nil return nil
} }
if sshClient == nil {
return fmt.Errorf("SSH Client is nil")
}
client, err := sftp.NewClient(sshClient) client, err := sftp.NewClient(sshClient)
if err != nil { if err != nil {
+50 -12
View File
@@ -36,6 +36,8 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
// print the command's information // print the command's information
if cmdFound { if cmdFound {
print("Backy Command: ")
println(cmd)
println("Command: ") println("Command: ")
print(cmdInfo.Cmd) print(cmdInfo.Cmd)
@@ -45,17 +47,57 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
print(v) // print command arg print(v) // print command arg
} }
// is it remote or local if cmdInfo.Type.String() != "" {
if !IsHostLocal(cmdInfo.Host) {
println() println()
print("Host: ", cmdInfo.Host) print("Type: ", cmdInfo.Type.String())
println() println()
}
if cmdInfo.Hosts != nil {
for n, h := range cmdInfo.Hosts {
println()
fmt.Printf("Host %d: %s", n, h)
println()
}
} else { } else {
println() // is it remote or local
print("Host: Runs on Local Machine\n\n") if !IsHostLocal(cmdInfo.Host) {
println()
print("Host: ", cmdInfo.Host)
println()
} else {
println()
print("Host: Runs on Local Machine\n\n")
}
}
if len(cmdInfo.Environment) > 0 {
fmt.Print("Environment: ")
for _, env := range cmdInfo.Environment {
fmt.Printf("%s ", env)
}
fmt.Println()
}
if cmdInfo.PackageManager != "" {
fmt.Printf("\nPackage Manager: %s\n", cmdInfo.PackageManager)
if len(cmdInfo.Packages) > 0 {
for _, pkg := range cmdInfo.Packages {
fmt.Printf("Package: %v\n", pkg)
}
}
}
if cmdInfo.Username != "" || cmdInfo.UserID != "" {
fmt.Println("\nUser Settings:")
fmt.Printf("Username: %s\n", cmdInfo.Username)
fmt.Printf("UserID: %s\n", cmdInfo.UserID)
} }
if cmdInfo.Dir != nil { if cmdInfo.Dir != nil {
@@ -64,11 +106,6 @@ func (opts *ConfigOpts) ListCommand(cmd string) {
println() println()
} }
if cmdInfo.Type.String() != "" {
print("Type: ", cmdInfo.Type.String())
println()
}
} else { } else {
fmt.Printf("Command %s not found. Check spelling.\n", cmd) fmt.Printf("Command %s not found. Check spelling.\n", cmd)
@@ -100,9 +137,10 @@ func (opts *ConfigOpts) ListCommandList(list string) {
println("List: ", list) println("List: ", list)
println() println()
for _, v := range listInfo.Order { for n, cmd := range listInfo.Order {
println() println()
opts.ListCommand(v) fmt.Printf("Command %d: \n", n+1)
opts.ListCommand(cmd)
} }
} else { } else {
+12
View File
@@ -2,6 +2,7 @@ package backy
import ( import (
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"time" "time"
) )
@@ -75,6 +76,17 @@ func (metricFile *MetricFile) SaveToFile() error {
} }
func LoadMetricsFromFile(filename string) (*MetricFile, error) { func LoadMetricsFromFile(filename string) (*MetricFile, error) {
if err := testFile(filename); err != nil {
_, createErr := os.Create(filename)
if createErr != nil {
return nil, fmt.Errorf("error creating file and root cause: %w", err)
}
metricData := NewMetricsFromFile(filename)
data, _ := json.MarshalIndent(metricData, "", " ")
os.WriteFile(filename, data, 0600)
return metricData, nil
}
jsonData, err := os.ReadFile(filename) jsonData, err := os.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
+57 -62
View File
@@ -1,67 +1,62 @@
package backy package backy
// import ( import (
// "testing" "testing"
// "time" "time"
// ) )
// func TestAddingMetricsForCommand(t *testing.T) { func TestAddingMetricsForCommand(t *testing.T) {
// Create a new MetricFile
metricFile := NewMetricsFromFile("test_metrics.json")
metricFile, err := LoadMetricsFromFile(metricFile.Filename)
if err != nil {
t.Fatalf("Failed to load metrics from file: %v", err)
}
// Add metrics for a command
commandName := "test_command"
if metricFile.CommandMetrics != nil {
if _, exists := metricFile.CommandMetrics[commandName]; !exists {
metricFile.CommandMetrics[commandName] = NewMetrics()
}
}
// Update the metrics for the command
executionTime := 1.8 // Example execution time in seconds
success := true // Example success status
metricFile.CommandMetrics[commandName].Update(success, executionTime, time.Now())
// Check if the metrics were updated correctly
if metricFile.CommandMetrics[commandName].SuccessfulExecutions > 50 {
t.Errorf("Expected 1 successful execution, got %d", metricFile.CommandMetrics[commandName].SuccessfulExecutions)
}
if metricFile.CommandMetrics[commandName].TotalExecutions > 50 {
t.Errorf("Expected 1 total execution, got %d", metricFile.CommandMetrics[commandName].TotalExecutions)
}
if metricFile.CommandMetrics[commandName].TotalExecutionTime != executionTime {
t.Errorf("Expected execution time %f, got %f", executionTime, metricFile.CommandMetrics[commandName].TotalExecutionTime)
}
err = metricFile.SaveToFile()
if err != nil {
t.Errorf("Failed to save metrics to file: %v", err)
}
listName := "test_list"
if _, exists := metricFile.ListMetrics[listName]; !exists {
metricFile.ListMetrics[listName] = NewMetrics()
}
// Update the metrics for the list
metricFile.ListMetrics[listName].Update(success, executionTime, time.Now())
if metricFile.ListMetrics[listName].SuccessfulExecutions > 50 {
t.Errorf("Expected 1 successful execution for list, got %d", metricFile.ListMetrics[listName].SuccessfulExecutions)
}
if metricFile.ListMetrics[listName].TotalExecutions > 50 {
t.Errorf("Expected 1 total execution for list, got %d", metricFile.ListMetrics[listName].TotalExecutions)
}
if metricFile.ListMetrics[listName].TotalExecutionTime > executionTime {
t.Errorf("Expected execution time %f for list, got %f", executionTime, metricFile.ListMetrics[listName].TotalExecutionTime)
}
// // Create a new MetricFile // Save the metrics to a file
// metricFile := NewMetricsFromFile("test_metrics.json") err = metricFile.SaveToFile()
if err != nil {
t.Errorf("Failed to save metrics to file: %v", err)
}
// metricFile, err := LoadMetricsFromFile(metricFile.Filename) }
// if err != nil {
// t.Errorf("Failed to load metrics from file: %v", err)
// }
// // Add metrics for a command
// commandName := "test_command"
// if _, exists := metricFile.CommandMetrics[commandName]; !exists {
// metricFile.CommandMetrics[commandName] = NewMetrics()
// }
// // Update the metrics for the command
// executionTime := 1.8 // Example execution time in seconds
// success := true // Example success status
// metricFile.CommandMetrics[commandName].Update(success, executionTime, time.Now())
// // Check if the metrics were updated correctly
// if metricFile.CommandMetrics[commandName].SuccessfulExecutions > 50 {
// t.Errorf("Expected 1 successful execution, got %d", metricFile.CommandMetrics[commandName].SuccessfulExecutions)
// }
// if metricFile.CommandMetrics[commandName].TotalExecutions > 50 {
// t.Errorf("Expected 1 total execution, got %d", metricFile.CommandMetrics[commandName].TotalExecutions)
// }
// // if metricFile.CommandMetrics[commandName].TotalExecutionTime != executionTime {
// // t.Errorf("Expected execution time %f, got %f", executionTime, metricFile.CommandMetrics[commandName].TotalExecutionTime)
// // }
// err = metricFile.SaveToFile()
// if err != nil {
// t.Errorf("Failed to save metrics to file: %v", err)
// }
// listName := "test_list"
// if _, exists := metricFile.ListMetrics[listName]; !exists {
// metricFile.ListMetrics[listName] = NewMetrics()
// }
// // Update the metrics for the list
// metricFile.ListMetrics[listName].Update(success, executionTime, time.Now())
// if metricFile.ListMetrics[listName].SuccessfulExecutions > 50 {
// t.Errorf("Expected 1 successful execution for list, got %d", metricFile.ListMetrics[listName].SuccessfulExecutions)
// }
// if metricFile.ListMetrics[listName].TotalExecutions > 50 {
// t.Errorf("Expected 1 total execution for list, got %d", metricFile.ListMetrics[listName].TotalExecutions)
// }
// // if metricFile.ListMetrics[listName].TotalExecutionTime > executionTime {
// // t.Errorf("Expected execution time %f for list, got %f", executionTime, metricFile.ListMetrics[listName].TotalExecutionTime)
// // }
// // Save the metrics to a file
// err = metricFile.SaveToFile()
// if err != nil {
// t.Errorf("Failed to save metrics to file: %v", err)
// }
// }
+3 -2
View File
@@ -723,7 +723,8 @@ func (command *Command) prepareScriptBuffer() (*bytes.Buffer, error) {
buffer.WriteString(command.Cmd) buffer.WriteString(command.Cmd)
for _, arg := range command.Args { for _, arg := range command.Args {
buffer.WriteString(" " + arg) buffer.WriteString(" ")
buffer.WriteString(arg)
} }
buffer.WriteByte('\n') buffer.WriteByte('\n')
return &buffer, nil return &buffer, nil
@@ -847,7 +848,7 @@ func DoesHostHaveHostName(host string) (bool, string) {
} }
func IsHostLocal(host string) bool { func IsHostLocal(host string) bool {
host = strings.ToLower(host) host = strings.ToLower(strings.TrimSpace(host))
return host == "127.0.0.1" || host == "localhost" || host == "" return host == "127.0.0.1" || host == "localhost" || host == ""
} }
+5 -7
View File
@@ -1,15 +1,13 @@
Command list {{.listName }} failed. Backy Command: {{.CmdName}}.
The command run was {{.CmdName}}. The command executed: {{.Command}} {{ if .Args }} {{- range .Args}} {{.}} {{end}} {{end}}
The command executed was {{.Command}} {{ if .Args }} {{- range .Args}} {{.}} {{end}} {{end}} {{ if .Err }} Error: {{ .Err }}{{ end }}
{{ if .Err }} The error was {{ .Err }}{{ end }} {{ if .Output }} Output: {{- range .Output}} {{.}} {{end}} {{end}}
{{ if .Output }} The output was: {{- range .Output}} {{.}} {{end}} {{end}}
{{ if .CmdsRan }} {{ if .CmdsRan }}
The following commands ran: Commands executed (last one failed):
{{- range .CmdsRan}} {{- range .CmdsRan}}
- {{. -}} - {{. -}}
{{end}} {{end}}
+1 -3
View File
@@ -1,6 +1,4 @@
Command list {{ .listName }} completed successfully. Commands executed:
The following commands ran:
{{- range .CmdsRan}} {{- range .CmdsRan}}
- {{. -}} - {{. -}}
{{end}} {{end}}
+5
View File
@@ -226,6 +226,11 @@ func CheckConfigValues(config *koanf.Koanf, file string) {
func collectOutput(buf *bytes.Buffer, commandName string, logger, globalLogger zerolog.Logger, wantOutput bool) []string { func collectOutput(buf *bytes.Buffer, commandName string, logger, globalLogger zerolog.Logger, wantOutput bool) []string {
var outputArr []string var outputArr []string
copyBuf := bytes.NewBuffer(buf.Bytes()) copyBuf := bytes.NewBuffer(buf.Bytes())
if buf.Len() == 0 {
logger.Info().Str("cmd", commandName).Msg("NO OUTPUT!!!!!!!")
globalLogger.Info().Str("cmd", commandName).Msg("NO OUTPUT!!!!!!!")
return outputArr
}
scanner := bufio.NewScanner(copyBuf) scanner := bufio.NewScanner(copyBuf)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
+8 -8
View File
@@ -1,8 +1,8 @@
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14 # localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14 [localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDZi/dbBsgthhSj9kf4SIxTZCX+thRmvSgQ+ogC0yyHvoQrIio0Pu2MA66S779LE7Zng3CJ1lxiesffqdiTdcv8mrlnwHlzshNy4trl85NOawdNpwwlY8aiYQkxifXqAXSoHlgG+AlrbLcZ4q0YbeLnwgB3eEywN+eCHuJdetNr1Lq9NkqiGKltBIVYxHofEkUj+jBTJCyPDcp7/CKSYXhcELCaSw8vBkB7+PfsYYy0+TPXspiu/euLZfiagh6ik2fMcfc0k5P4Yn0GRt5Uk4jr85lob7ucO6Q2VPJpfavSjetcqa/oL6peEOJ4/SQzzrbxmh0I/5Tq76lTtrFT+R+tfqn0+n11FkNhS85IulmpdD2kLPGE6yH+vfjYrYD5TdH0qiOltqfXuiJCekRryoU9oWS/VSDi7aEVPryCQWf0IUqj05F/sa4RRtO0k7UOYL8NwJTS8q02sCt+/W+nX03f6XNoG7K57MZlYnREUGS6scqnvktb9bwKBunDZePMwZk=
[localhost]:2222 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDATufWA1HRnNayIQLjSpA2+P9N6h0WF+jP+abMaINlZkiHFnFVDAoqD5/onVXymskrgQaKEYmBOs+Kv0t+Acvdor2IcvYgFueSm+jkslpSK/uuf1mx0gVJO77S2BIjqyWtUzVv96Iy4Gjt2RsrnalgYNYmi3OyPkG0IUA+3Im+2gztSECCy+nW3R/vaoPLwr4kImpLlrijcSHc4mHOY6BurrcWKNuGrsvTAOKgUZqlya6uDd+yD7fUfsmL1MqBKwZqfP3JAdp/Dd+laNNGcvEM4WhzYFSPfhqblewD0rjbto9MSOSXLyQz5RPmdITj/m5M4lj2ECmcI2gzraDMoj8ZkuJAss50oX6fmVUZestN5jlz7Y7XKEvXuH8qfLHKwaOUTZlcGbfAMz6uSrh8DNT6KzRG4j5nZ9Z5pTn1huz/p6jnJUGuHt2Ez3EK+isM+sHS6TntXavIkebaq7ErcBCO8A1fZFZlhlHoI9o9W62tMY7gbtlGodW8dKxK89+1a88= # localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14 [localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEy0ReNz4EO68wUiQGHmkKOwLQhAUhY9oCyzpyROUWM0FWQId124SKPPcLD3DcYHQpofdG8h11NZ8Dyk1g/mW8M=
[localhost]:2222 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBK9fEYfiGGgu0Eh7X2JT4jR4+utcfpm6Ee+Cer1x/XbMHzCPZg6YmYy6OaCSms/0VJ/QWxD+0HlsO7sqO5oeO60= # localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14 # localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
[localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKT5+Cbi/ynOAPzwv0IaOVBtGFYtW33LIvNUuBKYqqyJ # localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.16
# localhost:2222 SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.14 [localhost]:2222 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIArPKqZ3cHWzv0o6RUko8TNpr95olBJAzHRiPIvYD7bH
+1
View File
@@ -4,4 +4,5 @@ set -euo pipefail
DIR="$(cd "$(dirname "$0")" && pwd)" DIR="$(cd "$(dirname "$0")" && pwd)"
echo "Starting SSH test container (building if needed)..." echo "Starting SSH test container (building if needed)..."
docker compose -f "$DIR/compose.yml" up -d --build docker compose -f "$DIR/compose.yml" up -d --build
ssh-keyscan -p 2222 localhost > "$DIR/known_hosts"
echo "Container started on localhost:2222" echo "Container started on localhost:2222"