feat: support scp publishing
See https://git.vvvvvvaria.org/decentral1se/distribusi-go/issues/4
This commit is contained in:
parent
c77022b97f
commit
bba820173d
47
README.md
47
README.md
|
@ -119,6 +119,16 @@ ls /tmp/ | grep -i distribusi
|
|||
distribusi-go-20-25-492637114356
|
||||
```
|
||||
|
||||
If you have SSH access to a server, you can publish your files with `-P/--publish`.
|
||||
|
||||
It works just like [scp] does:
|
||||
|
||||
```
|
||||
./distribusi -p <path> -P <server>:<remote-path>
|
||||
```
|
||||
|
||||
See [this short guide](#ssh-guide-for-the-publishing) for help with SSH issues.
|
||||
|
||||
:v:
|
||||
|
||||
## Hacking
|
||||
|
@ -154,6 +164,42 @@ Binaries are in `dist/`.
|
|||
|
||||
[`LICENSE`](./LICENSE)
|
||||
|
||||
## Tips
|
||||
|
||||
### SSH guide for `-P/--publish`
|
||||
|
||||
If you already know & use `scp` then `-P/--publish` should Just Work :tm: If
|
||||
not, read on for some tips on how to configure your SSH client to help
|
||||
`distribusi-go` make SSH connections successfully.
|
||||
|
||||
The simplest way to make sure `distribusi` can make an SSH connection with your
|
||||
server is to match a `~/.ssh/config` entry with the `-P <server>:...` server
|
||||
value you pass on the command-line.
|
||||
|
||||
If you want to run `./distribusi -p <path> -P varia.zone:/var/www/`, then a
|
||||
matching `~/.ssh/config` entry might look like this:
|
||||
|
||||
```ssh
|
||||
Host varia.zone
|
||||
Hostname varia.zone
|
||||
User decentral1se
|
||||
Port 22
|
||||
IdentityFile ~/.ssh/<ssh-secret-key-part>
|
||||
```
|
||||
|
||||
This tells `distribusi-go` everything it needs to know to make a successful SSH
|
||||
connection. Run with `-d/--debug` for extra help figuring out what connection
|
||||
details are being used.
|
||||
|
||||
If you have a secret protected SSH key then make sure you've got a running
|
||||
`ssh-agent` and have added the key with the following:
|
||||
|
||||
```
|
||||
eval $(ssh-agent -k)
|
||||
ssh-add ~/.ssh/<ssh-secret-key-part>
|
||||
ssh-add -L # see loaded keys
|
||||
```
|
||||
|
||||
[Go]: https://go.dev
|
||||
[Pillow]: https://pillow.readthedocs.io/en/stable/installation.html#external-libraries
|
||||
[distribusi]: https://git.vvvvvvaria.org/varia/distribusi
|
||||
|
@ -165,3 +211,4 @@ Binaries are in `dist/`.
|
|||
[the entire listing]: https://vvvvvvaria.org/~decentral1se/distribusi-go/
|
||||
[this book]: https://www.gopl.io/
|
||||
[this ticket]: https://git.vvvvvvaria.org/decentral1se/distribusi-go/issues/1
|
||||
[scp]: https://linux.die.net/man/1/scp
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"io"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
@ -25,9 +26,13 @@ import (
|
|||
"github.com/disintegration/imaging"
|
||||
"github.com/gabriel-vasile/mimetype"
|
||||
"github.com/k0kubun/go-ansi"
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"github.com/povsister/scp"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
// Version is the current version of distribusi-go.
|
||||
|
@ -211,6 +216,12 @@ Example:
|
|||
Usage: "path to distribusify",
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "publish",
|
||||
Aliases: []string{"P"},
|
||||
Usage: `publish to a server using scp (e.g. "varia.zone:public_html")`,
|
||||
Required: false,
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "serve",
|
||||
Aliases: []string{"s"},
|
||||
|
@ -253,6 +264,10 @@ Example:
|
|||
return nil
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
if c.Bool("serve") && c.String("publish") != "" {
|
||||
logrus.Fatal("woops, can't publish & serve at the same time?")
|
||||
}
|
||||
|
||||
root, err := filepath.Abs(c.String("path"))
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
|
@ -300,6 +315,16 @@ Example:
|
|||
fmt.Println("done!")
|
||||
}
|
||||
|
||||
pubTarget := c.String("publish")
|
||||
if pubTarget != "" {
|
||||
logrus.Debugf("attempting to publish files to %s", pubTarget)
|
||||
|
||||
if err := scpPublish(c, root, pubTarget); err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ch <- nil
|
||||
return
|
||||
}()
|
||||
|
@ -879,3 +904,67 @@ func mkProgressBar(c *cli.Context) *progressbar.ProgressBar {
|
|||
|
||||
return bar
|
||||
}
|
||||
|
||||
// scpPublish initates a scp-like publishing interface. We do our best here to
|
||||
// simply work with existing local work station SSH client configurations. It
|
||||
// is mostly up to folks to configure their own shit. We don't do anything
|
||||
// fancy here.
|
||||
func scpPublish(c *cli.Context, root, pubTarget string) error {
|
||||
split := strings.Split(pubTarget, ":")
|
||||
server, remotePath := split[0], split[1]
|
||||
|
||||
logrus.Debugf("parsed server: %s remotePath: %s from %s", server, remotePath, pubTarget)
|
||||
|
||||
if hostname := ssh_config.Get(server, "Hostname"); hostname == "" {
|
||||
return fmt.Errorf("missing Hostname entry for %s in ~/.ssh/config, cannot continue", server)
|
||||
}
|
||||
|
||||
user := ssh_config.Get(server, "User")
|
||||
port := ssh_config.Get(server, "Port")
|
||||
|
||||
logrus.Debugf("read user: %s, port: %s for %s in ~/.ssh/config", user, port, server)
|
||||
|
||||
sshConf := &ssh.ClientConfig{
|
||||
User: user,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // awful, i know
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
identityFile := ssh_config.Get(server, "IdentityFile")
|
||||
if identityFile != "" && identityFile != "~/.ssh/identity" {
|
||||
sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agentCl := agent.NewClient(sshAgent)
|
||||
authMethod := ssh.PublicKeysCallback(agentCl.Signers)
|
||||
sshConf.Auth = []ssh.AuthMethod{authMethod}
|
||||
|
||||
logrus.Debugf("read identityFile: %s for %s in ~/.ssh/config, using ssh-agent for auth", identityFile, server)
|
||||
}
|
||||
|
||||
logrus.Debug("attempting to construct SSH client for publishing logic")
|
||||
|
||||
serverAndPort := fmt.Sprintf("%s:%s", server, port)
|
||||
scpClient, err := scp.NewClient(serverAndPort, sshConf, &scp.ClientOption{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer scpClient.Close()
|
||||
|
||||
opts := &scp.DirTransferOption{
|
||||
Context: c.Context,
|
||||
Timeout: 10 * time.Minute,
|
||||
}
|
||||
|
||||
fmt.Printf(fmt.Sprintf("publishing %s to %s...", filepath.Base(root), server))
|
||||
|
||||
if err := scpClient.CopyDirToRemote(root, remotePath, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(" done!")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -16,9 +16,11 @@ require (
|
|||
require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/povsister/scp v0.0.0-20210427074412-33febfd9f13e // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
|
|
7
go.sum
7
go.sum
|
@ -16,6 +16,8 @@ github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0Fn
|
|||
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kevinburke/ssh_config v1.1.0 h1:pH/t1WS9NzT8go394IqZeJTMHVm6Cr6ZJ6AQ+mdNo/o=
|
||||
github.com/kevinburke/ssh_config v1.1.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
|
@ -24,6 +26,8 @@ github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2Em
|
|||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/povsister/scp v0.0.0-20210427074412-33febfd9f13e h1:VtsDti2SgX7M7jy0QAyGgb162PeHLrOaNxmcYOtaGsY=
|
||||
github.com/povsister/scp v0.0.0-20210427074412-33febfd9f13e/go.mod h1:i1Au86ZXK0ZalQNyBp2njCcyhSCR/QP/AMfILip+zNI=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
|
@ -40,10 +44,12 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
@ -58,6 +64,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
|
|||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
Loading…
Reference in New Issue