mirror of
https://github.com/appleboy/drone-scp.git
synced 2026-06-04 18:23:59 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb2dd5543b | |||
| 8b963288b3 | |||
| bd2ddc2b6c | |||
| d098811ced | |||
| de6f344960 | |||
| cf87fecefa | |||
| 665c665298 | |||
| c663c07449 |
@@ -71,6 +71,10 @@ builds:
|
||||
{{- else }}{{ .Arch }}{{ end }}
|
||||
{{- if .Arm }}-{{ .Arm }}{{ end }}
|
||||
no_unique_dist_dir: true
|
||||
hooks:
|
||||
post:
|
||||
- cmd: xz -k -9 {{ .Path }}
|
||||
dir: ./dist/
|
||||
|
||||
archives:
|
||||
- format: binary
|
||||
@@ -79,6 +83,17 @@ archives:
|
||||
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
extra_files:
|
||||
- glob: ./**.xz
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}"
|
||||
|
||||
release:
|
||||
# You can add extra pre-existing files to the release.
|
||||
# The filename on the release will be the last part of the path (base).
|
||||
# If another file with the same name exists, the last one found will be used.
|
||||
#
|
||||
# Templates: allowed
|
||||
extra_files:
|
||||
- glob: ./**.xz
|
||||
|
||||
+10
-3
@@ -1,22 +1,29 @@
|
||||
package main
|
||||
|
||||
// This function returns the appropriate command for removing a file/directory based on the operating system.
|
||||
func rmcmd(os, target string) string {
|
||||
switch os {
|
||||
case "windows":
|
||||
// On Windows, use DEL command to delete files and folders recursively
|
||||
return "DEL /F /S " + target
|
||||
case "unix":
|
||||
return "rm -rf '" + target + "'"
|
||||
// On Unix-based systems, use rm command to delete files and folders recursively
|
||||
return "rm -rf " + target
|
||||
}
|
||||
// Return an empty string if the operating system is not recognized
|
||||
return ""
|
||||
}
|
||||
|
||||
// This function returns the appropriate command for creating a directory based on the operating system.
|
||||
func mkdircmd(os, target string) string {
|
||||
switch os {
|
||||
case "windows":
|
||||
// On Windows, use mkdir command to create directory and check if it exists
|
||||
return "if not exist " + target + " mkdir " + target
|
||||
case "unix":
|
||||
return "mkdir -p '" + target + "'"
|
||||
// On Unix-based systems, use mkdir command with -p option to create directories recursively
|
||||
return "mkdir -p " + target
|
||||
}
|
||||
|
||||
// Return an empty string if the operating system is not recognized
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// Unit tests for rmcmd and mkdircmd
|
||||
func TestCommands(t *testing.T) {
|
||||
// Test rmcmd on Windows
|
||||
os1 := "windows"
|
||||
target1 := "C:\\path\\to\\file"
|
||||
expected1 := "DEL /F /S " + target1
|
||||
actual1 := rmcmd(os1, target1)
|
||||
if actual1 != expected1 {
|
||||
t.Errorf("rmcmd(%s, %s) = %s; expected %s", os1, target1, actual1, expected1)
|
||||
}
|
||||
|
||||
// Test rmcmd on Unix-based system
|
||||
os2 := "unix"
|
||||
target2 := "/path/to/folder"
|
||||
expected2 := "rm -rf " + target2
|
||||
actual2 := rmcmd(os2, target2)
|
||||
if actual2 != expected2 {
|
||||
t.Errorf("rmcmd(%s, %s) = %s; expected %s", os2, target2, actual2, expected2)
|
||||
}
|
||||
|
||||
// Test mkdircmd on Windows
|
||||
os3 := "windows"
|
||||
target3 := "C:\\path\\to\\folder"
|
||||
expected3 := "if not exist " + target3 + " mkdir " + target3
|
||||
actual3 := mkdircmd(os3, target3)
|
||||
if actual3 != expected3 {
|
||||
t.Errorf("mkdircmd(%s, %s) = %s; expected %s", os3, target3, actual3, expected3)
|
||||
}
|
||||
|
||||
// Test mkdircmd on Unix-based system
|
||||
os4 := "unix"
|
||||
target4 := "/path/to/folder"
|
||||
expected4 := "mkdir -p " + target4
|
||||
actual4 := mkdircmd(os4, target4)
|
||||
if actual4 != expected4 {
|
||||
t.Errorf("mkdircmd(%s, %s) = %s; expected %s", os4, target4, actual4, expected4)
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/urfave/cli/v2 v2.25.1
|
||||
github.com/urfave/cli/v2 v2.25.3
|
||||
golang.org/x/crypto v0.8.0
|
||||
)
|
||||
|
||||
@@ -22,6 +22,6 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -32,8 +32,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/urfave/cli/v2 v2.25.1 h1:zw8dSP7ghX0Gmm8vugrs6q9Ku0wzweqPyshy+syu9Gw=
|
||||
github.com/urfave/cli/v2 v2.25.1/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
|
||||
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -46,8 +46,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/appleboy/easyssh-proxy"
|
||||
@@ -24,7 +25,7 @@ func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Drone SCP"
|
||||
app.Usage = "Copy files and artifacts via SSH."
|
||||
app.Copyright = "Copyright (c) 2020 Bo-Yi Wu"
|
||||
app.Copyright = "Copyright (c) " + strconv.Itoa(time.Now().Year()) + " Bo-Yi Wu"
|
||||
app.Version = Version
|
||||
app.Authors = []*cli.Author{
|
||||
{
|
||||
|
||||
@@ -209,7 +209,7 @@ func (p *Plugin) buildUnTarArgs(target string) []string {
|
||||
|
||||
args = append(args,
|
||||
"-C",
|
||||
"'"+target+"'",
|
||||
target,
|
||||
)
|
||||
|
||||
return args
|
||||
@@ -236,6 +236,8 @@ func (p *Plugin) Exec() error {
|
||||
dir := os.TempDir()
|
||||
src := filepath.Join(dir, p.DestFile)
|
||||
|
||||
// show current version
|
||||
fmt.Println("drone-scp version: " + Version)
|
||||
// run archive command
|
||||
fmt.Println("tar all files into " + src)
|
||||
args := p.buildTarArgs(src)
|
||||
@@ -254,14 +256,15 @@ func (p *Plugin) Exec() error {
|
||||
errChannel := make(chan error)
|
||||
finished := make(chan struct{})
|
||||
for _, host := range hosts {
|
||||
go func(host string) {
|
||||
go func(h string) {
|
||||
defer wg.Done()
|
||||
host, port := p.hostPort(h)
|
||||
// Create MakeConfig instance with remote username, server address and path to private key.
|
||||
ssh := &easyssh.MakeConfig{
|
||||
Server: host,
|
||||
User: p.Config.Username,
|
||||
Password: p.Config.Password,
|
||||
Port: p.Config.Port,
|
||||
Port: port,
|
||||
Key: p.Config.Key,
|
||||
KeyPath: p.Config.KeyPath,
|
||||
Passphrase: p.Config.Passphrase,
|
||||
@@ -303,6 +306,7 @@ func (p *Plugin) Exec() error {
|
||||
}
|
||||
|
||||
for _, target := range p.Config.Target {
|
||||
target = strings.Replace(target, " ", "\\ ", -1)
|
||||
// remove target folder before upload data
|
||||
if p.Config.Remove {
|
||||
p.log(host, "Remove target folder:", target)
|
||||
@@ -385,6 +389,22 @@ func (p *Plugin) Exec() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function takes a Plugin struct and a host string and returns the host and port as separate strings.
|
||||
func (p Plugin) hostPort(host string) (string, string) {
|
||||
// Split the host string by colon (":") to get the host and port
|
||||
hosts := strings.Split(host, ":")
|
||||
// Get the default port from the Plugin's Config field
|
||||
port := p.Config.Port
|
||||
// If the host string contains a port (i.e. it has more than one element after splitting), set the port to that value
|
||||
if len(hosts) > 1 {
|
||||
host = hosts[0]
|
||||
port = hosts[1]
|
||||
}
|
||||
|
||||
// Return the host and port as separate strings
|
||||
return host, port
|
||||
}
|
||||
|
||||
func trimValues(keys []string) []string {
|
||||
var newKeys []string
|
||||
|
||||
|
||||
+72
-6
@@ -615,7 +615,7 @@ func TestPlugin_buildUnTarArgs(t *testing.T) {
|
||||
args: args{
|
||||
target: "foo",
|
||||
},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "-C", "'foo'"},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "-C", "foo"},
|
||||
},
|
||||
{
|
||||
name: "strip components",
|
||||
@@ -631,7 +631,7 @@ func TestPlugin_buildUnTarArgs(t *testing.T) {
|
||||
args: args{
|
||||
target: "foo",
|
||||
},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "-C", "'foo'"},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "-C", "foo"},
|
||||
},
|
||||
{
|
||||
name: "overwrite",
|
||||
@@ -647,7 +647,7 @@ func TestPlugin_buildUnTarArgs(t *testing.T) {
|
||||
args: args{
|
||||
target: "foo",
|
||||
},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "-C", "'foo'"},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "-C", "foo"},
|
||||
},
|
||||
{
|
||||
name: "unlink first",
|
||||
@@ -663,7 +663,7 @@ func TestPlugin_buildUnTarArgs(t *testing.T) {
|
||||
args: args{
|
||||
target: "foo",
|
||||
},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "--unlink-first", "-C", "'foo'"},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "--unlink-first", "-C", "foo"},
|
||||
},
|
||||
{
|
||||
name: "output folder path with space",
|
||||
@@ -677,9 +677,9 @@ func TestPlugin_buildUnTarArgs(t *testing.T) {
|
||||
DestFile: "foo.tar.gz",
|
||||
},
|
||||
args: args{
|
||||
target: "foo bar",
|
||||
target: "foo\\ bar",
|
||||
},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "--unlink-first", "-C", "'foo bar'"},
|
||||
want: []string{"tar", "-zxf", "foo.tar.gz", "--strip-components", "2", "--overwrite", "--unlink-first", "-C", "foo\\ bar"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -804,3 +804,69 @@ func TestTargetFolderWithSpaces(t *testing.T) {
|
||||
t.Fatalf("SCP-error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostPortString(t *testing.T) {
|
||||
if os.Getenv("SSH_AUTH_SOCK") != "" {
|
||||
if err := exec.Command("eval", "`ssh-agent -k`").Run(); err != nil {
|
||||
t.Fatalf("exec: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
u, err := user.Lookup("drone-scp")
|
||||
if err != nil {
|
||||
t.Fatalf("Lookup: %v", err)
|
||||
}
|
||||
|
||||
plugin := Plugin{
|
||||
Config: Config{
|
||||
Host: []string{"localhost:22", "localhost:22"},
|
||||
Username: "drone-scp",
|
||||
Port: "8080",
|
||||
KeyPath: "tests/.ssh/id_rsa",
|
||||
Source: []string{"tests/global/*"},
|
||||
StripComponents: 2,
|
||||
Target: []string{filepath.Join(u.HomeDir, "1234")},
|
||||
CommandTimeout: 60 * time.Second,
|
||||
TarExec: "tar",
|
||||
},
|
||||
}
|
||||
|
||||
err = plugin.Exec()
|
||||
assert.Nil(t, err)
|
||||
|
||||
// check file exist
|
||||
if _, err := os.Stat(filepath.Join(u.HomeDir, "1234", "c.txt")); os.IsNotExist(err) {
|
||||
t.Fatalf("SCP-error: %v", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(filepath.Join(u.HomeDir, "1234", "d.txt")); os.IsNotExist(err) {
|
||||
t.Fatalf("SCP-error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Unit test for hostPort
|
||||
func TestHostPort(t *testing.T) {
|
||||
p := Plugin{
|
||||
Config: Config{
|
||||
Port: "8080",
|
||||
},
|
||||
}
|
||||
|
||||
// Test case 1: host string with port
|
||||
host1 := "example.com:1234"
|
||||
expectedHost1 := "example.com"
|
||||
expectedPort1 := "1234"
|
||||
actualHost1, actualPort1 := p.hostPort(host1)
|
||||
if actualHost1 != expectedHost1 || actualPort1 != expectedPort1 {
|
||||
t.Errorf("hostPort(%s) = (%s, %s); expected (%s, %s)", host1, actualHost1, actualPort1, expectedHost1, expectedPort1)
|
||||
}
|
||||
|
||||
// Test case 2: host string without port
|
||||
host2 := "example.com"
|
||||
expectedHost2 := "example.com"
|
||||
expectedPort2 := "8080" // default port
|
||||
actualHost2, actualPort2 := p.hostPort(host2)
|
||||
if actualHost2 != expectedHost2 || actualPort2 != expectedPort2 {
|
||||
t.Errorf("hostPort(%s) = (%s, %s); expected (%s, %s)", host2, actualHost2, actualPort2, expectedHost2, expectedPort2)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user