diff --git a/.golangci.yaml b/.golangci.yaml index c01d3c7..a3c8de0 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -46,6 +46,7 @@ formatters: - gci - gofmt - goimports + - golines exclusions: generated: lax paths: diff --git a/main.go b/main.go index edfeccf..5f54cde 100644 --- a/main.go +++ b/main.go @@ -84,9 +84,14 @@ func main() { EnvVars: []string{"PLUGIN_SSH_KEY", "PLUGIN_KEY", "SSH_KEY", "INPUT_KEY"}, }, &cli.StringFlag{ - Name: "ssh-passphrase", - Usage: "The purpose of the passphrase is usually to encrypt the private key.", - EnvVars: []string{"PLUGIN_SSH_PASSPHRASE", "PLUGIN_PASSPHRASE", "SSH_PASSPHRASE", "INPUT_PASSPHRASE"}, + Name: "ssh-passphrase", + Usage: "The purpose of the passphrase is usually to encrypt the private key.", + EnvVars: []string{ + "PLUGIN_SSH_PASSPHRASE", + "PLUGIN_PASSPHRASE", + "SSH_PASSPHRASE", + "INPUT_PASSPHRASE", + }, }, &cli.StringFlag{ Name: "key-path", @@ -100,9 +105,13 @@ func main() { EnvVars: []string{"PLUGIN_CIPHERS", "SSH_CIPHERS", "INPUT_CIPHERS"}, }, &cli.BoolFlag{ - Name: "useInsecureCipher", - Usage: "include more ciphers with use_insecure_cipher", - EnvVars: []string{"PLUGIN_USE_INSECURE_CIPHER", "SSH_USE_INSECURE_CIPHER", "INPUT_USE_INSECURE_CIPHER"}, + Name: "useInsecureCipher", + Usage: "include more ciphers with use_insecure_cipher", + EnvVars: []string{ + "PLUGIN_USE_INSECURE_CIPHER", + "SSH_USE_INSECURE_CIPHER", + "INPUT_USE_INSECURE_CIPHER", + }, }, &cli.StringFlag{ Name: "fingerprint", @@ -118,8 +127,12 @@ func main() { Name: "command.timeout", Aliases: []string{"T"}, Usage: "command timeout", - EnvVars: []string{"PLUGIN_COMMAND_TIMEOUT", "SSH_COMMAND_TIMEOUT", "INPUT_COMMAND_TIMEOUT"}, - Value: 10 * time.Minute, + EnvVars: []string{ + "PLUGIN_COMMAND_TIMEOUT", + "SSH_COMMAND_TIMEOUT", + "INPUT_COMMAND_TIMEOUT", + }, + Value: 10 * time.Minute, }, &cli.StringSliceFlag{ Name: "script", @@ -154,36 +167,63 @@ func main() { Value: "22", }, &cli.StringFlag{ - Name: "proxy.protocol", - Usage: "The IP protocol to use for the proxy. Valid values are \"tcp\". \"tcp4\" or \"tcp6\". Default to tcp.", - EnvVars: []string{"PLUGIN_PROXY_PROTOCOL", "SSH_PROXY_PROTOCOL", "INPUT_PROXY_PROTOCOL"}, - Value: "tcp", + Name: "proxy.protocol", + Usage: "The IP protocol to use for the proxy. Valid values are \"tcp\". \"tcp4\" or \"tcp6\". Default to tcp.", + EnvVars: []string{ + "PLUGIN_PROXY_PROTOCOL", + "SSH_PROXY_PROTOCOL", + "INPUT_PROXY_PROTOCOL", + }, + Value: "tcp", }, &cli.StringFlag{ - Name: "proxy.username", - Usage: "connect as user of proxy", - EnvVars: []string{"PLUGIN_PROXY_USERNAME", "PLUGIN_PROXY_USER", "PROXY_SSH_USERNAME", "INPUT_PROXY_USERNAME"}, - Value: "root", + Name: "proxy.username", + Usage: "connect as user of proxy", + EnvVars: []string{ + "PLUGIN_PROXY_USERNAME", + "PLUGIN_PROXY_USER", + "PROXY_SSH_USERNAME", + "INPUT_PROXY_USERNAME", + }, + Value: "root", }, &cli.StringFlag{ - Name: "proxy.password", - Usage: "user password of proxy", - EnvVars: []string{"PLUGIN_PROXY_PASSWORD", "PROXY_SSH_PASSWORD", "INPUT_PROXY_PASSWORD"}, + Name: "proxy.password", + Usage: "user password of proxy", + EnvVars: []string{ + "PLUGIN_PROXY_PASSWORD", + "PROXY_SSH_PASSWORD", + "INPUT_PROXY_PASSWORD", + }, }, &cli.StringFlag{ - Name: "proxy.ssh-key", - Usage: "private ssh key of proxy", - EnvVars: []string{"PLUGIN_PROXY_SSH_KEY", "PLUGIN_PROXY_KEY", "PROXY_SSH_KEY", "INPUT_PROXY_KEY"}, + Name: "proxy.ssh-key", + Usage: "private ssh key of proxy", + EnvVars: []string{ + "PLUGIN_PROXY_SSH_KEY", + "PLUGIN_PROXY_KEY", + "PROXY_SSH_KEY", + "INPUT_PROXY_KEY", + }, }, &cli.StringFlag{ - Name: "proxy.ssh-passphrase", - Usage: "The purpose of the passphrase is usually to encrypt the private key.", - EnvVars: []string{"PLUGIN_PROXY_SSH_PASSPHRASE", "PLUGIN_PROXY_PASSPHRASE", "PROXY_SSH_PASSPHRASE", "INPUT_PROXY_PASSPHRASE"}, + Name: "proxy.ssh-passphrase", + Usage: "The purpose of the passphrase is usually to encrypt the private key.", + EnvVars: []string{ + "PLUGIN_PROXY_SSH_PASSPHRASE", + "PLUGIN_PROXY_PASSPHRASE", + "PROXY_SSH_PASSPHRASE", + "INPUT_PROXY_PASSPHRASE", + }, }, &cli.StringFlag{ - Name: "proxy.key-path", - Usage: "ssh private key path of proxy", - EnvVars: []string{"PLUGIN_PROXY_KEY_PATH", "PROXY_SSH_KEY_PATH", "INPUT_PROXY_KEY_PATH"}, + Name: "proxy.key-path", + Usage: "ssh private key path of proxy", + EnvVars: []string{ + "PLUGIN_PROXY_KEY_PATH", + "PROXY_SSH_KEY_PATH", + "INPUT_PROXY_KEY_PATH", + }, }, &cli.DurationFlag{ Name: "proxy.timeout", @@ -196,14 +236,23 @@ func main() { EnvVars: []string{"PLUGIN_PROXY_CIPHERS", "PROXY_SSH_CIPHERS", "INPUT_PROXY_CIPHERS"}, }, &cli.BoolFlag{ - Name: "proxy.useInsecureCipher", - Usage: "include more ciphers with use_insecure_cipher", - EnvVars: []string{"PLUGIN_PROXY_USE_INSECURE_CIPHER", "PROXY_SSH_USE_INSECURE_CIPHER", "INPUT_PROXY_USE_INSECURE_CIPHER"}, + Name: "proxy.useInsecureCipher", + Usage: "include more ciphers with use_insecure_cipher", + EnvVars: []string{ + "PLUGIN_PROXY_USE_INSECURE_CIPHER", + "PROXY_SSH_USE_INSECURE_CIPHER", + "INPUT_PROXY_USE_INSECURE_CIPHER", + }, }, &cli.StringFlag{ - Name: "proxy.fingerprint", - Usage: "fingerprint SHA256 of the host public key, default is to skip verification", - EnvVars: []string{"PLUGIN_PROXY_FINGERPRINT", "PROXY_SSH_FINGERPRINT", "PROXY_FINGERPRINT", "INPUT_PROXY_FINGERPRINT"}, + Name: "proxy.fingerprint", + Usage: "fingerprint SHA256 of the host public key, default is to skip verification", + EnvVars: []string{ + "PLUGIN_PROXY_FINGERPRINT", + "PROXY_SSH_FINGERPRINT", + "PROXY_FINGERPRINT", + "INPUT_PROXY_FINGERPRINT", + }, }, &cli.StringSliceFlag{ Name: "envs", diff --git a/plugin.go b/plugin.go index ea0d4c4..87d89f8 100644 --- a/plugin.go +++ b/plugin.go @@ -15,9 +15,11 @@ import ( var ( errMissingHost = errors.New("error: missing server host") - errMissingPasswordOrKey = errors.New("error: can't connect without a private SSH key or password") - errCommandTimeOut = errors.New("error: command timeout") - envsFormat = "export {NAME}={VALUE}" + errMissingPasswordOrKey = errors.New( + "error: can't connect without a private SSH key or password", + ) + errCommandTimeOut = errors.New("error: command timeout") + envsFormat = "export {NAME}={VALUE}" ) type ( @@ -119,7 +121,10 @@ func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) { for _, key := range p.Config.Envs { key = strings.ToUpper(key) if val, found := os.LookupEnv(key); found { - env = append(env, p.format(p.Config.EnvsFormat, "{NAME}", key, "{VALUE}", escapeArg(val))) + env = append( + env, + p.format(p.Config.EnvsFormat, "{NAME}", key, "{VALUE}", escapeArg(val)), + ) } } @@ -131,7 +136,10 @@ func (p Plugin) exec(host string, wg *sync.WaitGroup, errChannel chan error) { p.log(host, "======END======") } - stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream(strings.Join(p.Config.Script, "\n"), p.Config.CommandTimeout) + stdoutChan, stderrChan, doneChan, errChan, err := ssh.Stream( + strings.Join(p.Config.Script, "\n"), + p.Config.CommandTimeout, + ) if err != nil { errChannel <- err return @@ -257,7 +265,10 @@ func (p Plugin) scriptCommands() []string { } commands = append(commands, cmd) if p.Config.ScriptStop && cmd[(len(cmd)-1):] != "\\" { - commands = append(commands, "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;") + commands = append( + commands, + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + ) } } diff --git a/plugin_test.go b/plugin_test.go index 78e3354..abd6d74 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -153,11 +153,15 @@ func TestSSHIPv6OnlyError(t *testing.T) { func TestStreamFromSSHCommand(t *testing.T) { plugin := Plugin{ Config: Config{ - Host: []string{"localhost", "127.0.0.1"}, - Username: "drone-scp", - Port: 22, - KeyPath: "./tests/.ssh/id_rsa", - Script: []string{"whoami", "for i in {1..5}; do echo ${i}; sleep 1; done", "echo 'done'"}, + Host: []string{"localhost", "127.0.0.1"}, + Username: "drone-scp", + Port: 22, + KeyPath: "./tests/.ssh/id_rsa", + Script: []string{ + "whoami", + "for i in {1..5}; do echo ${i}; sleep 1; done", + "echo 'done'", + }, CommandTimeout: 60 * time.Second, }, } @@ -289,13 +293,20 @@ func TestSetExistingENV(t *testing.T) { os.Setenv("BAR", "") plugin := Plugin{ Config: Config{ - Host: []string{"localhost"}, - Username: "drone-scp", - Port: 22, - KeyPath: "./tests/.ssh/id_rsa", - Envs: []string{"foo", "bar", "baz"}, - Debug: true, - Script: []string{"export FOO", "export BAR", "export BAZ", "env | grep -q '^FOO=Value for foo$'", "env | grep -q '^BAR=$'", "if env | grep -q BAZ; then false; else true; fi"}, + Host: []string{"localhost"}, + Username: "drone-scp", + Port: 22, + KeyPath: "./tests/.ssh/id_rsa", + Envs: []string{"foo", "bar", "baz"}, + Debug: true, + Script: []string{ + "export FOO", + "export BAR", + "export BAZ", + "env | grep -q '^FOO=Value for foo$'", + "env | grep -q '^BAR=$'", + "if env | grep -q BAZ; then false; else true; fi", + }, CommandTimeout: 1 * time.Second, Proxy: easyssh.DefaultConfig{ Server: "localhost", @@ -313,11 +324,15 @@ func TestSetExistingENV(t *testing.T) { func TestSyncMode(t *testing.T) { plugin := Plugin{ Config: Config{ - Host: []string{"localhost", "127.0.0.1"}, - Username: "drone-scp", - Port: 22, - KeyPath: "./tests/.ssh/id_rsa", - Script: []string{"whoami", "for i in {1..3}; do echo ${i}; sleep 1; done", "echo 'done'"}, + Host: []string{"localhost", "127.0.0.1"}, + Username: "drone-scp", + Port: 22, + KeyPath: "./tests/.ssh/id_rsa", + Script: []string{ + "whoami", + "for i in {1..3}; do echo ${i}; sleep 1; done", + "echo 'done'", + }, CommandTimeout: 60 * time.Second, Sync: true, }, @@ -671,7 +686,12 @@ func TestPlugin_scriptCommands(t *testing.T) { ScriptStop: true, }, }, - want: []string{"mkdir a", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir b", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;"}, + want: []string{ + "mkdir a", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + "mkdir b", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + }, }, { name: "normal testing 2", @@ -681,7 +701,14 @@ func TestPlugin_scriptCommands(t *testing.T) { ScriptStop: true, }, }, - want: []string{"mkdir a", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir c", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir b", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;"}, + want: []string{ + "mkdir a", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + "mkdir c", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + "mkdir b", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + }, }, // See: https://github.com/appleboy/ssh-action/issues/75#issuecomment-668314271 { @@ -692,7 +719,13 @@ func TestPlugin_scriptCommands(t *testing.T) { ScriptStop: true, }, }, - want: []string{"ls \\", "-lah", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", "mkdir a", "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;"}, + want: []string{ + "ls \\", + "-lah", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + "mkdir a", + "DRONE_SSH_PREV_COMMAND_EXIT_CODE=$? ; if [ $DRONE_SSH_PREV_COMMAND_EXIT_CODE -ne 0 ]; then exit $DRONE_SSH_PREV_COMMAND_EXIT_CODE; fi;", + }, }, { name: "trim space", @@ -938,12 +971,19 @@ func runSSHContainerTest(t *testing.T, cfg SSHTestConfig) { WaitingFor: wait.ForListeningPort("2222/tcp").WithStartupTimeout(180 * time.Second), } - sshContainer, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ - ContainerRequest: req, - Started: true, - }) + sshContainer, err := testcontainers.GenericContainer( + ctx, + testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }, + ) if err != nil { - t.Skipf("Could not start container with image %s: %v. Check Docker environment and image availability. Skipping test.", req.Image, err) + t.Skipf( + "Could not start container with image %s: %v. Check Docker environment and image availability. Skipping test.", + req.Image, + err, + ) } defer func() { if err := sshContainer.Terminate(ctx); err != nil {