New Upstream Release - golang-github-charmbracelet-wish
Ready changes
Summary
Merged new upstream version: 1.1.1 (was: 0.6.0).
Diff
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 011f8ad..3081cf0 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -4,7 +4,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
- time: "08:00"
labels:
- "dependencies"
commit-message:
@@ -14,7 +13,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
- time: "08:00"
labels:
- "dependencies"
commit-message:
@@ -24,7 +22,6 @@ updates:
directory: "/"
schedule:
interval: "daily"
- time: "08:00"
labels:
- "dependencies"
commit-message:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 3686566..bd84ac3 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -6,24 +6,17 @@ on:
jobs:
build:
- strategy:
- matrix:
- go-version: [~1.17, ^1]
+ uses: charmbracelet/meta/.github/workflows/build.yml@main
+
+ codecov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
-
- - name: Set up Go
- uses: actions/setup-go@v3
+ - uses: actions/setup-go@v4
with:
- go-version: ${{ matrix.go-version }}
-
- - name: Build
- run: go build -v ./...
-
- - name: Test
- run: go test -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt ./... -timeout 5m
-
+ go-version: '^1'
+ cache: true
+ - run: go test -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt ./... -timeout 5m
- uses: codecov/codecov-action@v3
with:
file: ./coverage.txt
diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml
index 1d75f05..afb97fa 100644
--- a/.github/workflows/examples.yml
+++ b/.github/workflows/examples.yml
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - uses: actions/setup-go@v3
+ - uses: actions/setup-go@v4
with:
go-version: '^1'
cache: true
diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml
index 55b3662..9bd4548 100644
--- a/.github/workflows/goreleaser.yml
+++ b/.github/workflows/goreleaser.yml
@@ -17,3 +17,14 @@ jobs:
docker_token: ${{ secrets.DOCKERHUB_TOKEN }}
gh_pat: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
goreleaser_key: ${{ secrets.GORELEASER_KEY }}
+ twitter_consumer_key: ${{ secrets.TWITTER_CONSUMER_KEY }}
+ twitter_consumer_secret: ${{ secrets.TWITTER_CONSUMER_SECRET }}
+ twitter_access_token: ${{ secrets.TWITTER_ACCESS_TOKEN }}
+ twitter_access_token_secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
+ mastodon_client_id: ${{ secrets.MASTODON_CLIENT_ID }}
+ mastodon_client_secret: ${{ secrets.MASTODON_CLIENT_SECRET }}
+ mastodon_access_token: ${{ secrets.MASTODON_ACCESS_TOKEN }}
+ discord_webhook_id: ${{ secrets.DISCORD_WEBHOOK_ID }}
+ discord_webhook_token: ${{ secrets.DISCORD_WEBHOOK_TOKEN }}
+
+# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index d0bb996..2f13cc3 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -9,8 +9,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - name: golangci-lint
- uses: golangci/golangci-lint-action@v3
+ - uses: actions/setup-go@v4
+ with:
+ go-version: ^1
+ cache: true
+ - uses: golangci/golangci-lint-action@v3
with:
# Optional: golangci-lint command line arguments.
args: --issues-exit-code=0
diff --git a/.golangci.yml b/.golangci.yml
index 85cfb6d..7783944 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -32,3 +32,12 @@ linters:
- unconvert
- unparam
- whitespace
+ - depguard
+
+
+linters-settings:
+ depguard:
+ list-type: 'denylist'
+ packages-with-error-message:
+ - 'github.com/gliderlabs/ssh': 'use github.com/charmbracelet/ssh instead'
+
diff --git a/.goreleaser.yml b/.goreleaser.yml
index eea5c74..9f9bd92 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,3 +1,8 @@
includes:
- from_url:
- url: charmbracelet/meta/main/goreleaser-lib.yaml
\ No newline at end of file
+ url: charmbracelet/meta/main/goreleaser-lib.yaml
+ - from_url:
+ url: charmbracelet/meta/main/goreleaser-announce.yaml
+
+# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
+
diff --git a/README.md b/README.md
index aceb8a5..bcbee5e 100755
--- a/README.md
+++ b/README.md
@@ -111,6 +111,57 @@ Host localhost
UserKnownHostsFile /dev/null
```
+## How it works?
+
+Wish uses [gliderlabs/ssh][gliderlabs/ssh] to implement its SSH server, and
+the OpenSSH is never used nor needed — you can even uninstall it if you want to.
+
+Incidentally, there's no risk of accidentally sharing a shell because there's no
+default behavior that does that on Wish.
+
+## Running with SystemD
+
+If you want to run a Wish app with `systemd`, you can create an unit like so:
+
+`/etc/systemd/system/myapp.service`:
+```service
+[Unit]
+Description=My App
+After=network.target
+
+[Service]
+Type=simple
+User=myapp
+Group=myapp
+WorkingDirectory=/home/myapp/
+ExecStart=/usr/bin/myapp
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
+```
+
+You can tune the values bellow, and once you're happy with them, you can run:
+
+```bash
+# need to run this every time you change the unit file
+sudo systemctl daemon-reload
+
+# start/restart/stop/etc:
+sudo systemctl start myapp
+```
+
+If you use a new user for each app (which is good), you'll need to create them
+first:
+
+```bash
+useradd --system --user-group --create-home myapp
+```
+
+That should do it.
+
+###
+
## Feedback
We’d love to hear your thoughts on this project. Feel free to drop us a note!
diff --git a/accesscontrol/accesscontrol.go b/accesscontrol/accesscontrol.go
index a268577..5a8b310 100644
--- a/accesscontrol/accesscontrol.go
+++ b/accesscontrol/accesscontrol.go
@@ -4,8 +4,8 @@ package accesscontrol
import (
"fmt"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// Middleware will exit 1 connections trying to execute commands that are not allowed.
diff --git a/accesscontrol/accesscontrol_test.go b/accesscontrol/accesscontrol_test.go
index 42f6f35..580ec97 100644
--- a/accesscontrol/accesscontrol_test.go
+++ b/accesscontrol/accesscontrol_test.go
@@ -4,9 +4,9 @@ import (
"fmt"
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/accesscontrol"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/activeterm/activeterm.go b/activeterm/activeterm.go
index 38cd2e5..8ed590c 100644
--- a/activeterm/activeterm.go
+++ b/activeterm/activeterm.go
@@ -4,8 +4,8 @@ package activeterm
import (
"fmt"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// Middleware will exit 1 connections trying with no active terminals.
diff --git a/activeterm/activeterm_test.go b/activeterm/activeterm_test.go
index 2bf17fd..e0cebd3 100644
--- a/activeterm/activeterm_test.go
+++ b/activeterm/activeterm_test.go
@@ -3,9 +3,9 @@ package activeterm_test
import (
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/activeterm"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/bubbletea/tea.go b/bubbletea/tea.go
index a4fe896..ff17737 100644
--- a/bubbletea/tea.go
+++ b/bubbletea/tea.go
@@ -2,12 +2,11 @@
package bubbletea
import (
- "log"
-
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
"github.com/muesli/termenv"
)
@@ -67,7 +66,6 @@ func MiddlewareWithProgramHandler(bth ProgramHandler, cp termenv.Profile) wish.M
return func(sh ssh.Handler) ssh.Handler {
lipgloss.SetColorProfile(cp)
return func(s ssh.Session) {
- errc := make(chan error, 1)
p := bth(s)
if p != nil {
_, windowChanges, _ := s.Pty()
@@ -77,19 +75,18 @@ func MiddlewareWithProgramHandler(bth ProgramHandler, cp termenv.Profile) wish.M
case <-s.Context().Done():
if p != nil {
p.Quit()
+ return
}
case w := <-windowChanges:
if p != nil {
p.Send(tea.WindowSizeMsg{Width: w.Width, Height: w.Height})
}
- case err := <-errc:
- if err != nil {
- log.Print(err)
- }
}
}
}()
- errc <- p.Start()
+ if _, err := p.Run(); err != nil {
+ log.Error("app exit with error", "error", err)
+ }
// p.Kill() will force kill the program if it's still running,
// and restore the terminal to its original state in case of a
// tui crash
diff --git a/comment/comment.go b/comment/comment.go
index 3e883f4..8b45153 100644
--- a/comment/comment.go
+++ b/comment/comment.go
@@ -1,8 +1,8 @@
package comment
import (
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// Middleware prints a comment at the end of the session.
diff --git a/comment/comment_test.go b/comment/comment_test.go
index 05b46e2..841a5e9 100644
--- a/comment/comment_test.go
+++ b/comment/comment_test.go
@@ -3,8 +3,8 @@ package comment
import (
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/debian/changelog b/debian/changelog
index ccaa22a..2adac9e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,8 +1,12 @@
-golang-github-charmbracelet-wish (0.6.0-1) UNRELEASED; urgency=medium
+golang-github-charmbracelet-wish (1.1.1-1) UNRELEASED; urgency=medium
+ [ Martin Dosch ]
* New upstream release.
- -- Martin Dosch <martin@mdosch.de> Sun, 30 Oct 2022 09:37:45 +0000
+ [ Debian Janitor ]
+ * New upstream release.
+
+ -- Martin Dosch <martin@mdosch.de> Mon, 05 Jun 2023 11:25:40 -0000
golang-github-charmbracelet-wish (0.1.1-2) unstable; urgency=medium
diff --git a/elapsed/elapsed.go b/elapsed/elapsed.go
index 24993f3..5b0885b 100644
--- a/elapsed/elapsed.go
+++ b/elapsed/elapsed.go
@@ -3,8 +3,8 @@ package timer
import (
"time"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// MiddlewareWithFormat returns a middleware that logs the elapsed time of the
diff --git a/elapsed/elapsed_test.go b/elapsed/elapsed_test.go
index 7802fcd..efb5305 100644
--- a/elapsed/elapsed_test.go
+++ b/elapsed/elapsed_test.go
@@ -4,14 +4,12 @@ import (
"testing"
"time"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
-var (
- waitDuration = time.Second
-)
+var waitDuration = time.Second
func TestMiddleware(t *testing.T) {
t.Run("recover session", func(t *testing.T) {
diff --git a/examples/bubbletea/main.go b/examples/bubbletea/main.go
index e14b0a0..989a9d7 100644
--- a/examples/bubbletea/main.go
+++ b/examples/bubbletea/main.go
@@ -5,18 +5,19 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
lm "github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
)
const (
@@ -34,24 +35,24 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/examples/bubbleteaprogram/main.go b/examples/bubbleteaprogram/main.go
index 9fcc372..174851e 100644
--- a/examples/bubbleteaprogram/main.go
+++ b/examples/bubbleteaprogram/main.go
@@ -5,18 +5,19 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
tea "github.com/charmbracelet/bubbletea"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
lm "github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
"github.com/muesli/termenv"
)
@@ -35,24 +36,24 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/examples/cobra/main.go b/examples/cobra/main.go
index 0f74dd1..cb91eda 100644
--- a/examples/cobra/main.go
+++ b/examples/cobra/main.go
@@ -2,16 +2,17 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
"github.com/spf13/cobra"
)
@@ -57,7 +58,7 @@ func main() {
rootCmd.SetErr(s.Stderr())
rootCmd.CompletionOptions.DisableDefaultCmd = true
if err := rootCmd.Execute(); err != nil {
- s.Exit(1)
+ _ = s.Exit(1)
return
}
@@ -68,23 +69,23 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/examples/git/main.go b/examples/git/main.go
index c54dab6..4125d87 100644
--- a/examples/git/main.go
+++ b/examples/git/main.go
@@ -5,18 +5,19 @@ package main
import (
"context"
+ "errors"
"fmt"
"io/fs"
- "log"
"os"
"os/signal"
"syscall"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
gm "github.com/charmbracelet/wish/git"
lm "github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
)
const (
@@ -34,11 +35,11 @@ func (a app) AuthRepo(repo string, pk ssh.PublicKey) gm.AccessLevel {
}
func (a app) Push(repo string, pk ssh.PublicKey) {
- log.Printf("pushed %s", repo)
+ log.Info("push", "repo", repo)
}
func (a app) Fetch(repo string, pk ssh.PublicKey) {
- log.Printf("fetch %s", repo)
+ log.Info("fetch", "repo", repo)
}
func passHandler(ctx ssh.Context, password string) bool {
@@ -65,24 +66,24 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
@@ -96,7 +97,7 @@ func gitListMiddleware(h ssh.Handler) ssh.Handler {
if len(s.Command()) == 0 {
des, err := os.ReadDir(repoDir)
if err != nil && err != fs.ErrNotExist {
- log.Println(err)
+ log.Error("invalid repository", "error", err)
}
if len(des) > 0 {
fmt.Fprintf(s, "\n### Repo Menu ###\n\n")
diff --git a/examples/go.mod b/examples/go.mod
index a1f5ebd..2f31522 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -3,47 +3,53 @@ module examples
go 1.18
require (
- github.com/charmbracelet/bubbletea v0.22.1
+ github.com/charmbracelet/bubbletea v0.23.2
+ github.com/charmbracelet/log v0.1.2
+ github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103
github.com/charmbracelet/wish v0.5.0
- github.com/gliderlabs/ssh v0.3.5
- github.com/muesli/termenv v0.12.0
+ github.com/muesli/termenv v0.15.1
github.com/spf13/cobra v1.5.0
)
require (
- github.com/Microsoft/go-winio v0.4.16 // indirect
- github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
- github.com/acomagu/bufpipe v1.0.3 // indirect
+ github.com/Microsoft/go-winio v0.5.2 // indirect
+ github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
+ github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/caarlos0/sshmarshal v0.1.0 // indirect
- github.com/charmbracelet/keygen v0.3.0 // indirect
- github.com/charmbracelet/lipgloss v0.6.0 // indirect
+ github.com/charmbracelet/keygen v0.4.1 // indirect
+ github.com/charmbracelet/lipgloss v0.7.1 // indirect
+ github.com/cloudflare/circl v1.1.0 // indirect
github.com/containerd/console v1.0.3 // indirect
- github.com/emirpasic/gods v1.12.0 // indirect
+ github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
- github.com/go-git/go-billy/v5 v5.3.1 // indirect
- github.com/go-git/go-git/v5 v5.4.2 // indirect
- github.com/imdario/mergo v0.3.12 // indirect
+ github.com/go-git/go-billy/v5 v5.4.1 // indirect
+ github.com/go-git/go-git/v5 v5.6.1 // indirect
+ github.com/go-logfmt/logfmt v0.6.0 // indirect
+ github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
+ github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.16 // indirect
+ github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
+ github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
+ github.com/skeema/knownhosts v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- github.com/xanzy/ssh-agent v0.3.0 // indirect
- golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d // indirect
- golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
- golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
- golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
- golang.org/x/text v0.3.7 // indirect
+ github.com/xanzy/ssh-agent v0.3.3 // indirect
+ golang.org/x/crypto v0.8.0 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sync v0.1.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/term v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
diff --git a/examples/go.sum b/examples/go.sum
index c770cea..2ec754a 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -1,23 +1,32 @@
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
-github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
+github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
+github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/sshmarshal v0.1.0 h1:zTCZrDORFfWh526Tsb7vCm3+Yg/SfW/Ub8aQDeosk0I=
github.com/caarlos0/sshmarshal v0.1.0/go.mod h1:7Pd/0mmq9x/JCzKauogNjSQEhivBclCQHfr9dlpDIyA=
-github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24=
-github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0=
-github.com/charmbracelet/keygen v0.3.0 h1:mXpsQcH7DDlST5TddmXNXjS0L7ECk4/kLQYyBcsan2Y=
-github.com/charmbracelet/keygen v0.3.0/go.mod h1:1ukgO8806O25lUZ5s0IrNur+RlwTBERlezdgW71F5rM=
-github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
+github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps=
+github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM=
+github.com/charmbracelet/keygen v0.4.1 h1:ylwHCcCrb4UL2nHrUvVwME+/RFACcX1sjopOrIkc14g=
+github.com/charmbracelet/keygen v0.4.1/go.mod h1:4e4FT3HSdLU/u83RfJWvzJIaVb8aX4MxtDlfXwpDJaI=
github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
+github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
+github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
+github.com/charmbracelet/log v0.1.2 h1:xmKMxo0T/lcftgggQOhUkS32exku2/ID55FGYbr4nKQ=
+github.com/charmbracelet/log v0.1.2/go.mod h1:86XdIdmrubqtL/6u0z+jGFol1bQejBGG/qPSTwGZuQQ=
+github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 h1:wpHMERIN0pQZE635jWwT1dISgfjbpUcEma+fbPKSMCU=
+github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103/go.mod h1:0Vm2/8yBljiLDnGJHU8ehswfawrEybGk33j5ssqKQVM=
+github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
+github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -25,33 +34,32 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
-github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
-github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
-github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
-github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
+github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
+github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
+github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
+github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk=
+github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=
+github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
+github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
+github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -62,18 +70,18 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
-github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
+github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
@@ -82,11 +90,12 @@ github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIW
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
-github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
-github.com/muesli/termenv v0.12.0 h1:KuQRUE3PgxRFWhq4gHvZtPSLCGDqM5q/cYr1pZ39ytc=
-github.com/muesli/termenv v0.12.0/go.mod h1:WCCv32tusQ/EEZ5S8oUIIrC/nIuBcxCVqlN4Xfkv+7A=
+github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8=
+github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
+github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -97,56 +106,94 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
+github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
-github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
-golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -156,8 +203,9 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/examples/identity/main.go b/examples/identity/main.go
index 13c9f17..36f0976 100644
--- a/examples/identity/main.go
+++ b/examples/identity/main.go
@@ -2,16 +2,17 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
)
const (
@@ -45,23 +46,23 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/examples/scp/main.go b/examples/scp/main.go
index d0086d7..77d6232 100644
--- a/examples/scp/main.go
+++ b/examples/scp/main.go
@@ -4,19 +4,23 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/scp"
)
-const host = "localhost"
-const port = 23234
+const (
+ host = "localhost"
+ port = 23235
+)
func main() {
handler := scp.NewFileSystemHandler("./examples/scp/testdata")
@@ -28,23 +32,23 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/examples/simple/main.go b/examples/simple/main.go
index 2faa474..163d788 100644
--- a/examples/simple/main.go
+++ b/examples/simple/main.go
@@ -2,16 +2,17 @@ package main
import (
"context"
+ "errors"
"fmt"
- "log"
"os"
"os/signal"
"syscall"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
"github.com/charmbracelet/wish/logging"
- "github.com/gliderlabs/ssh"
)
const (
@@ -34,23 +35,23 @@ func main() {
),
)
if err != nil {
- log.Fatalln(err)
+ log.Error("could not start server", "error", err)
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
- log.Printf("Starting SSH server on %s:%d", host, port)
+ log.Info("Starting SSH server", "host", host, "port", port)
go func() {
- if err = s.ListenAndServe(); err != nil {
- log.Fatalln(err)
+ if err = s.ListenAndServe(); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not start server", "error", err)
}
}()
<-done
- log.Println("Stopping SSH server")
+ log.Info("Stopping SSH server")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer func() { cancel() }()
- if err := s.Shutdown(ctx); err != nil {
- log.Fatalln(err)
+ if err := s.Shutdown(ctx); err != nil && !errors.Is(err, ssh.ErrServerClosed) {
+ log.Error("could not stop server", "error", err)
}
}
diff --git a/git/git.go b/git/git.go
index 811675f..dc5c012 100644
--- a/git/git.go
+++ b/git/git.go
@@ -3,14 +3,14 @@ package git
import (
"errors"
"fmt"
- "log"
"os"
"os/exec"
"path/filepath"
"strings"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
)
@@ -70,9 +70,13 @@ func Middleware(repoDir string, gh Hooks) wish.Middleware {
cmd := s.Command()
if len(cmd) == 2 {
gc := cmd[0]
- repo := cmd[1] // cmd[1] should be `/REPO`
+ // repo should be in the form of "repo.git" or "user/repo.git"
+ repo := strings.TrimSuffix(strings.TrimPrefix(cmd[1], "/"), "/")
repo = filepath.Clean(repo)
- repo = filepath.Base(repo)
+ if n := strings.Count(repo, "/"); n > 1 {
+ Fatal(s, ErrInvalidRepo)
+ return
+ }
pk := s.PublicKey()
access := gh.AuthRepo(repo, pk)
switch gc {
@@ -99,7 +103,7 @@ func Middleware(repoDir string, gh Hooks) wish.Middleware {
case nil:
gh.Fetch(repo, pk)
default:
- log.Printf("unknown git error: %s", err)
+ log.Error("unknown git error", "error", err)
Fatal(s, ErrSystemMalfunction)
}
default:
@@ -139,6 +143,7 @@ func gitPack(s ssh.Session, gitCmd string, repoDir string, repo string) error {
if err != nil {
return err
}
+ // Needed for git dumb http server
return runGit(s, rp, "update-server-info")
default:
return fmt.Errorf("unknown git command: %s", gitCmd)
@@ -171,7 +176,7 @@ func ensureRepo(dir string, repo string) error {
return err
}
if !exists {
- err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0700))
+ err = os.MkdirAll(dir, os.ModeDir|os.FileMode(0o700))
if err != nil {
return err
}
diff --git a/git/git_test.go b/git/git_test.go
index 65aa9e8..aa59d93 100644
--- a/git/git_test.go
+++ b/git/git_test.go
@@ -3,19 +3,20 @@ package git
import (
"fmt"
"net"
- "os"
"os/exec"
"path/filepath"
+ "runtime"
"sync"
"testing"
"github.com/charmbracelet/keygen"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
func TestGitMiddleware(t *testing.T) {
pubkey, pkPath := createKeyPair(t)
+ hkPath := filepath.Join(t.TempDir(), "id_ed25519")
l, err := net.Listen("tcp", "127.0.0.1:0")
requireNoError(t, err)
@@ -33,9 +34,12 @@ func TestGitMiddleware(t *testing.T) {
{pubkey, "repo5", NoAccess},
{pubkey, "repo6", ReadOnlyAccess},
{pubkey, "repo7", AdminAccess},
+ {pubkey, "abc/repo1", AdminAccess},
+ {pubkey, "abc/def/repo1", AdminAccess},
},
}
srv, err := wish.NewServer(
+ wish.WithHostKeyPath(hkPath),
wish.WithMiddleware(Middleware(repoDir, hooks)),
wish.WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
return true
@@ -63,6 +67,34 @@ func TestGitMiddleware(t *testing.T) {
requireHasAction(t, hooks.pushes, pubkey, "repo2")
})
+ t.Run("create repo in subdir", func(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("permission issues")
+ }
+ cwd := t.TempDir()
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "init", "-b", "main"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "remote", "add", "origin", remote+"/abc/repo1"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "commit", "--allow-empty", "-m", "initial commit"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "push", "origin", "main"))
+ requireHasAction(t, hooks.pushes, pubkey, "abc/repo1")
+ })
+
+ t.Run("create wrong repo", func(t *testing.T) {
+ cwd := t.TempDir()
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "init", "-b", "main"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "remote", "add", "origin", remote+"//../../repo1"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "commit", "--allow-empty", "-m", "initial commit"))
+ requireError(t, runGitHelper(t, pkPath, cwd, "push", "origin", "main"))
+ })
+
+ t.Run("create wrong repo in subdir", func(t *testing.T) {
+ cwd := t.TempDir()
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "init", "-b", "main"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "remote", "add", "origin", remote+"/abc/def/repo1"))
+ requireNoError(t, runGitHelper(t, pkPath, cwd, "commit", "--allow-empty", "-m", "initial commit"))
+ requireError(t, runGitHelper(t, pkPath, cwd, "push", "origin", "main"))
+ })
+
t.Run("create and clone repo", func(t *testing.T) {
cwd := t.TempDir()
requireNoError(t, runGitHelper(t, pkPath, cwd, "init", "-b", "main"))
@@ -77,7 +109,7 @@ func TestGitMiddleware(t *testing.T) {
requireHasAction(t, hooks.fetches, pubkey, "repo3")
})
- t.Run("clone repo that doesnt exist", func(t *testing.T) {
+ t.Run("clone repo that doesn't exist", func(t *testing.T) {
cwd := t.TempDir()
requireError(t, runGitHelper(t, pkPath, cwd, "clone", remote+"/repo4"))
})
@@ -125,7 +157,7 @@ func runGitHelper(t *testing.T, pk, cwd string, args ...string) error {
cmd := exec.Command("git", allArgs...)
cmd.Dir = cwd
- cmd.Env = []string{fmt.Sprintf(`GIT_SSH_COMMAND=ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i %s -F /dev/null`, pk)}
+ cmd.Env = []string{fmt.Sprintf(`GIT_SSH_COMMAND=ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "%s" -F /dev/null`, pk)}
out, err := cmd.CombinedOutput()
if err != nil {
t.Log("git out:", string(out))
@@ -163,15 +195,10 @@ func requireHasAction(t *testing.T, actions []action, key ssh.PublicKey, repo st
func createKeyPair(t *testing.T) (ssh.PublicKey, string) {
t.Helper()
- keyDir := t.TempDir()
- _, err := keygen.NewWithWrite(filepath.Join(keyDir, "id"), nil, keygen.Ed25519)
- requireNoError(t, err)
- pk := filepath.Join(keyDir, "id_ed25519")
- pubBytes, err := os.ReadFile(filepath.Join(keyDir, "id_ed25519.pub"))
- requireNoError(t, err)
- pubkey, _, _, _, err := ssh.ParseAuthorizedKey(pubBytes)
+ pk := filepath.Join(t.TempDir(), "id_ed25519")
+ kp, err := keygen.New(pk, keygen.WithKeyType(keygen.Ed25519), keygen.WithWrite())
requireNoError(t, err)
- return pubkey, pk
+ return kp.PublicKey(), pk
}
type accessDetails struct {
diff --git a/go.mod b/go.mod
index 3a1f187..8e6ea67 100644
--- a/go.mod
+++ b/go.mod
@@ -1,49 +1,54 @@
module github.com/charmbracelet/wish
-go 1.17
+go 1.18
require (
- github.com/charmbracelet/bubbletea v0.22.1
- github.com/charmbracelet/keygen v0.3.0
- github.com/charmbracelet/lipgloss v0.6.0
- github.com/gliderlabs/ssh v0.3.5
- github.com/go-git/go-git/v5 v5.4.2
- github.com/hashicorp/golang-lru v0.5.4
- github.com/matryer/is v1.4.0
- github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739
- golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
- golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
- golang.org/x/time v0.0.0-20220411224347-583f2d630306
+ github.com/charmbracelet/bubbletea v0.23.2
+ github.com/charmbracelet/keygen v0.4.2
+ github.com/charmbracelet/lipgloss v0.7.1
+ github.com/charmbracelet/log v0.2.1
+ github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103
+ github.com/go-git/go-git/v5 v5.6.1
+ github.com/google/go-cmp v0.5.9
+ github.com/hashicorp/golang-lru/v2 v2.0.2
+ github.com/matryer/is v1.4.1
+ github.com/muesli/termenv v0.15.1
+ golang.org/x/crypto v0.8.0
+ golang.org/x/sync v0.1.0
+ golang.org/x/time v0.3.0
)
require (
- github.com/Microsoft/go-winio v0.4.16 // indirect
- github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
- github.com/acomagu/bufpipe v1.0.3 // indirect
+ github.com/Microsoft/go-winio v0.5.2 // indirect
+ github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
+ github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
+ github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/caarlos0/sshmarshal v0.1.0 // indirect
+ github.com/cloudflare/circl v1.1.0 // indirect
github.com/containerd/console v1.0.3 // indirect
- github.com/emirpasic/gods v1.12.0 // indirect
+ github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
- github.com/go-git/go-billy/v5 v5.3.1 // indirect
- github.com/google/go-cmp v0.5.5 // indirect
- github.com/imdario/mergo v0.3.12 // indirect
+ github.com/go-git/go-billy/v5 v5.4.1 // indirect
+ github.com/go-logfmt/logfmt v0.6.0 // indirect
+ github.com/imdario/mergo v0.3.13 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
+ github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-isatty v0.0.16 // indirect
+ github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
+ github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
+ github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sergi/go-diff v1.1.0 // indirect
- github.com/xanzy/ssh-agent v0.3.0 // indirect
- golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
- golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
- golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
- golang.org/x/text v0.3.7 // indirect
+ github.com/skeema/knownhosts v1.1.0 // indirect
+ github.com/xanzy/ssh-agent v0.3.3 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/term v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
diff --git a/go.sum b/go.sum
index cd5b936..80f81a4 100644
--- a/go.sum
+++ b/go.sum
@@ -1,58 +1,65 @@
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ=
-github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
-github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
+github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
+github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
+github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
+github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
+github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/caarlos0/sshmarshal v0.0.0-20220308164159-9ddb9f83c6b3/go.mod h1:7Pd/0mmq9x/JCzKauogNjSQEhivBclCQHfr9dlpDIyA=
+github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
+github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/sshmarshal v0.1.0 h1:zTCZrDORFfWh526Tsb7vCm3+Yg/SfW/Ub8aQDeosk0I=
github.com/caarlos0/sshmarshal v0.1.0/go.mod h1:7Pd/0mmq9x/JCzKauogNjSQEhivBclCQHfr9dlpDIyA=
-github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24=
-github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0=
-github.com/charmbracelet/keygen v0.3.0 h1:mXpsQcH7DDlST5TddmXNXjS0L7ECk4/kLQYyBcsan2Y=
-github.com/charmbracelet/keygen v0.3.0/go.mod h1:1ukgO8806O25lUZ5s0IrNur+RlwTBERlezdgW71F5rM=
-github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
-github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk=
+github.com/charmbracelet/bubbletea v0.23.2 h1:vuUJ9HJ7b/COy4I30e8xDVQ+VRDUEFykIjryPfgsdps=
+github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM=
+github.com/charmbracelet/keygen v0.4.1 h1:ylwHCcCrb4UL2nHrUvVwME+/RFACcX1sjopOrIkc14g=
+github.com/charmbracelet/keygen v0.4.1/go.mod h1:4e4FT3HSdLU/u83RfJWvzJIaVb8aX4MxtDlfXwpDJaI=
+github.com/charmbracelet/keygen v0.4.2 h1:TNHua2MlXc6W1dQB2iW4msSZGKlb8RtxtmYDWUs4iRw=
+github.com/charmbracelet/keygen v0.4.2/go.mod h1:4e4FT3HSdLU/u83RfJWvzJIaVb8aX4MxtDlfXwpDJaI=
+github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
+github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
+github.com/charmbracelet/log v0.2.1 h1:1z7jpkk4yKyjwlmKmKMM5qnEDSpV32E7XtWhuv0mTZE=
+github.com/charmbracelet/log v0.2.1/go.mod h1:GwFfjewhcVDWLrpAbY5A0Hin9YOlEn40eWT4PNaxFT4=
+github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 h1:wpHMERIN0pQZE635jWwT1dISgfjbpUcEma+fbPKSMCU=
+github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103/go.mod h1:0Vm2/8yBljiLDnGJHU8ehswfawrEybGk33j5ssqKQVM=
+github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
+github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
-github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
-github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
+github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
+github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
-github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
-github.com/go-git/go-git/v5 v5.4.2 h1:BXyZu9t0VkbiHtqrsvdq39UDhGJTl1h55VW6CSC4aY4=
-github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
+github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
+github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
+github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
+github.com/go-git/go-git/v5 v5.6.1 h1:q4ZRqQl4pR/ZJHc1L5CFjGA1a10u76aV1iC+nh+bHsk=
+github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=
+github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
+github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU=
+github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
+github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
-github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
+github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -63,31 +70,30 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
-github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
-github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
+github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
+github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
+github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
-github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI=
-github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
+github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8=
+github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
+github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
+github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -97,58 +103,87 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
+github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
-github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
-golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
+github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
+github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
+github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
+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/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
-golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -158,7 +193,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/logging/logging.go b/logging/logging.go
index e083ae0..8bd69b8 100644
--- a/logging/logging.go
+++ b/logging/logging.go
@@ -1,11 +1,11 @@
package logging
import (
- "log"
"time"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// Middleware provides basic connection logging. Connects are logged with the
@@ -15,7 +15,7 @@ import (
//
// The logger is set to the std default logger.
func Middleware() wish.Middleware {
- return MiddlewareWithLogger(log.Default())
+ return MiddlewareWithLogger(log.StandardLog())
}
// Logger is the interface that wraps the basic Log method.
@@ -27,15 +27,28 @@ type Logger interface {
// remote address, invoked command, TERM setting, window dimensions and if the
// auth was public key based. Disconnect will log the remote address and
// connection duration.
-func MiddlewareWithLogger(l Logger) wish.Middleware {
+func MiddlewareWithLogger(logger Logger) wish.Middleware {
return func(sh ssh.Handler) ssh.Handler {
return func(s ssh.Session) {
ct := time.Now()
hpk := s.PublicKey() != nil
pty, _, _ := s.Pty()
- l.Printf("%s connect %s %v %v %s %v %v\n", s.User(), s.RemoteAddr().String(), hpk, s.Command(), pty.Term, pty.Window.Width, pty.Window.Height)
+ logger.Printf(
+ "%s connect %s %v %v %s %v %v",
+ s.User(),
+ s.RemoteAddr().String(),
+ hpk,
+ s.Command(),
+ pty.Term,
+ pty.Window.Width,
+ pty.Window.Height,
+ )
sh(s)
- l.Printf("%s disconnect %s\n", s.RemoteAddr().String(), time.Since(ct))
+ logger.Printf(
+ "%s disconnect %s\n",
+ s.RemoteAddr().String(),
+ time.Since(ct),
+ )
}
}
}
diff --git a/logging/logging_test.go b/logging/logging_test.go
index 9b61e60..9dcb6e4 100644
--- a/logging/logging_test.go
+++ b/logging/logging_test.go
@@ -3,9 +3,9 @@ package logging_test
import (
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/logging"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/options.go b/options.go
index 0d9254b..5272def 100644
--- a/options.go
+++ b/options.go
@@ -4,15 +4,14 @@ import (
"bufio"
"bytes"
"errors"
- "fmt"
"io"
"os"
- "path/filepath"
"strings"
"time"
"github.com/charmbracelet/keygen"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
gossh "golang.org/x/crypto/ssh"
)
@@ -51,15 +50,12 @@ func WithMiddleware(mw ...Middleware) ssh.Option {
// WithHostKeyFile returns an ssh.Option that sets the path to the private.
func WithHostKeyPath(path string) ssh.Option {
if _, err := os.Stat(path); os.IsNotExist(err) {
- dir, f := filepath.Split(path)
- n := strings.TrimSuffix(f, "_ed25519")
- _, err := keygen.NewWithWrite(filepath.Join(dir, n), nil, keygen.Ed25519)
+ _, err := keygen.New(path, keygen.WithKeyType(keygen.Ed25519), keygen.WithWrite())
if err != nil {
return func(*ssh.Server) error {
return err
}
}
- path = filepath.Join(dir, n+"_ed25519")
}
return ssh.HostKeyFile(path)
}
@@ -72,17 +68,13 @@ func WithHostKeyPEM(pem []byte) ssh.Option {
// WithAuthorizedKeys allows to use a SSH authorized_keys file to allowlist users.
func WithAuthorizedKeys(path string) ssh.Option {
return func(s *ssh.Server) error {
- keys, err := parseAuthorizedKeys(path)
- if err != nil {
+ if _, err := os.Stat(path); err != nil {
return err
}
return WithPublicKeyAuth(func(_ ssh.Context, key ssh.PublicKey) bool {
- for _, upk := range keys {
- if ssh.KeysEqual(upk, key) {
- return true
- }
- }
- return false
+ return isAuthorized(path, func(k ssh.PublicKey) bool {
+ return ssh.KeysEqual(key, k)
+ })
})(s)
}
}
@@ -92,11 +84,9 @@ func WithAuthorizedKeys(path string) ssh.Option {
// Analogous to the TrustedUserCAKeys OpenSSH option.
func WithTrustedUserCAKeys(path string) ssh.Option {
return func(s *ssh.Server) error {
- cas, err := parseAuthorizedKeys(path)
- if err != nil {
+ if _, err := os.Stat(path); err != nil {
return err
}
-
return WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
cert, ok := key.(*gossh.Certificate)
if !ok {
@@ -104,38 +94,33 @@ func WithTrustedUserCAKeys(path string) ssh.Option {
return false
}
- checker := &gossh.CertChecker{
- IsUserAuthority: func(auth gossh.PublicKey) bool {
- for _, ca := range cas {
- if bytes.Equal(auth.Marshal(), ca.Marshal()) {
- // its a cert signed by one of the CAs
- return true
- }
- }
- // it is a cert, but signed by another CA
- return false
- },
- }
+ return isAuthorized(path, func(k ssh.PublicKey) bool {
+ checker := &gossh.CertChecker{
+ IsUserAuthority: func(auth gossh.PublicKey) bool {
+ // its a cert signed by one of the CAs
+ return bytes.Equal(auth.Marshal(), k.Marshal())
+ },
+ }
- if !checker.IsUserAuthority(cert.SignatureKey) {
- return false
- }
+ if !checker.IsUserAuthority(cert.SignatureKey) {
+ return false
+ }
- if err := checker.CheckCert(ctx.User(), cert); err != nil {
- return false
- }
+ if err := checker.CheckCert(ctx.User(), cert); err != nil {
+ return false
+ }
- return true
+ return true
+ })
})(s)
}
}
-func parseAuthorizedKeys(path string) ([]ssh.PublicKey, error) {
- var keys []ssh.PublicKey
-
+func isAuthorized(path string, checker func(k ssh.PublicKey) bool) bool {
f, err := os.Open(path)
if err != nil {
- return keys, fmt.Errorf("failed to parse %q: %w", path, err)
+ log.Warn("failed to parse", "path", path, "error", err)
+ return false
}
defer f.Close() // nolint: errcheck
@@ -146,15 +131,25 @@ func parseAuthorizedKeys(path string) ([]ssh.PublicKey, error) {
if errors.Is(err, io.EOF) {
break
}
- return keys, fmt.Errorf("failed to parse %q: %w", path, err)
+ log.Warn("failed to parse", "path", path, "error", err)
+ return false
+ }
+ if strings.TrimSpace(string(line)) == "" {
+ continue
+ }
+ if bytes.HasPrefix(line, []byte{'#'}) {
+ continue
}
upk, _, _, _, err := ssh.ParseAuthorizedKey(line)
if err != nil {
- return keys, fmt.Errorf("failed to parse %q: %w", path, err)
+ log.Warn("failed to parse", "path", path, "error", err)
+ return false
+ }
+ if checker(upk) {
+ return true
}
- keys = append(keys, upk)
}
- return keys, nil
+ return false
}
// WithPublicKeyAuth returns an ssh.Option that sets the public key auth handler.
diff --git a/options_test.go b/options_test.go
index 5108944..99e18ae 100755
--- a/options_test.go
+++ b/options_test.go
@@ -8,8 +8,8 @@ import (
"testing"
"time"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
@@ -25,23 +25,17 @@ func TestWithMaxTimeout(t *testing.T) {
requireEqual(t, time.Second, s.MaxTimeout)
}
-func TestParseAuthorizedKeys(t *testing.T) {
+func TestIsAuthorized(t *testing.T) {
t.Run("valid", func(t *testing.T) {
- keys, err := parseAuthorizedKeys("testdata/authorized_keys")
- requireNoError(t, err)
- requireEqual(t, 6, len(keys))
+ requireEqual(t, true, isAuthorized("testdata/authorized_keys", func(k ssh.PublicKey) bool { return true }))
})
t.Run("invalid", func(t *testing.T) {
- keys, err := parseAuthorizedKeys("testdata/invalid_authorized_keys")
- requireEqual(t, `failed to parse "testdata/invalid_authorized_keys": ssh: no key found`, err.Error())
- requireEqual(t, 0, len(keys))
+ requireEqual(t, false, isAuthorized("testdata/invalid_authorized_keys", func(k ssh.PublicKey) bool { return true }))
})
t.Run("file not found", func(t *testing.T) {
- keys, err := parseAuthorizedKeys("testdata/nope_authorized_keys")
- requireEqual(t, `failed to parse "testdata/nope_authorized_keys": open testdata/nope_authorized_keys: no such file or directory`, err.Error())
- requireEqual(t, 0, len(keys))
+ requireEqual(t, false, isAuthorized("testdata/nope_authorized_keys", func(k ssh.PublicKey) bool { return true }))
})
}
@@ -65,12 +59,18 @@ func TestWithAuthorizedKeys(t *testing.T) {
t.Run("invalid", func(t *testing.T) {
s := ssh.Server{}
- requireEqual(
+ requireNoError(
t,
- `failed to parse "testdata/invalid_authorized_keys": ssh: no key found`,
- WithAuthorizedKeys("testdata/invalid_authorized_keys")(&s).Error(),
+ WithAuthorizedKeys("testdata/invalid_authorized_keys")(&s),
)
})
+
+ t.Run("file not found", func(t *testing.T) {
+ s := ssh.Server{}
+ if err := WithAuthorizedKeys("testdata/nope_authorized_keys")(&s); err == nil {
+ t.Fatal("expected an error, got nil")
+ }
+ })
}
func TestWithTrustedUserCAKeys(t *testing.T) {
@@ -102,7 +102,7 @@ func TestWithTrustedUserCAKeys(t *testing.T) {
t.Run("invalid ca key", func(t *testing.T) {
s := &ssh.Server{}
if err := WithTrustedUserCAKeys("testdata/invalid-path")(s); err == nil {
- t.Fatal("expedted an error, got nil")
+ t.Fatal("expected an error, got nil")
}
})
diff --git a/ratelimiter/ratelimiter.go b/ratelimiter/ratelimiter.go
index 64b966e..7b9d2b7 100644
--- a/ratelimiter/ratelimiter.go
+++ b/ratelimiter/ratelimiter.go
@@ -3,12 +3,12 @@ package ratelimiter
import (
"errors"
- "log"
"net"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
- lru "github.com/hashicorp/golang-lru"
+ lru "github.com/hashicorp/golang-lru/v2"
"golang.org/x/time/rate"
)
@@ -48,7 +48,7 @@ func NewRateLimiter(r rate.Limit, burst int, maxEntries int) RateLimiter {
maxEntries = 1
}
// only possible error is if maxEntries is <= 0, which is prevented above.
- cache, _ := lru.New(maxEntries)
+ cache, _ := lru.New[string, *rate.Limiter](maxEntries)
return &limiters{
rate: r,
burst: burst,
@@ -57,7 +57,7 @@ func NewRateLimiter(r rate.Limit, burst int, maxEntries int) RateLimiter {
}
type limiters struct {
- cache *lru.Cache
+ cache *lru.Cache[string, *rate.Limiter]
rate rate.Limit
burst int
}
@@ -74,14 +74,14 @@ func (r *limiters) Allow(s ssh.Session) error {
var allowed bool
limiter, ok := r.cache.Get(key)
if ok {
- allowed = limiter.(*rate.Limiter).Allow()
+ allowed = limiter.Allow()
} else {
limiter := rate.NewLimiter(r.rate, r.burst)
allowed = limiter.Allow()
r.cache.Add(key, limiter)
}
- log.Printf("rate limiter key: %q, allowed? %v", key, allowed)
+ log.Debug("rate limiter key", "key", key, "allowed", allowed)
if allowed {
return nil
}
diff --git a/ratelimiter/ratelimiter_test.go b/ratelimiter/ratelimiter_test.go
index 5c426a1..018c38b 100644
--- a/ratelimiter/ratelimiter_test.go
+++ b/ratelimiter/ratelimiter_test.go
@@ -4,8 +4,8 @@ import (
"testing"
"time"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
"golang.org/x/sync/errgroup"
"golang.org/x/time/rate"
)
diff --git a/recover/recover.go b/recover/recover.go
index 6dff73d..769309c 100644
--- a/recover/recover.go
+++ b/recover/recover.go
@@ -1,11 +1,11 @@
package recover
import (
- "log"
"runtime/debug"
+ "github.com/charmbracelet/log"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// Middleware is a wish middleware that recovers from panics and log to stderr.
@@ -13,11 +13,16 @@ func Middleware(mw ...wish.Middleware) wish.Middleware {
return MiddlewareWithLogger(nil, mw...)
}
+// Logger is the interface that wraps the basic Log method.
+type Logger interface {
+ Printf(format string, v ...interface{})
+}
+
// MiddlewareWithLogger is a wish middleware that recovers from panics and log to
// the provided logger.
-func MiddlewareWithLogger(logger *log.Logger, mw ...wish.Middleware) wish.Middleware {
+func MiddlewareWithLogger(logger Logger, mw ...wish.Middleware) wish.Middleware {
if logger == nil {
- logger = log.Default()
+ logger = log.StandardLog()
}
h := func(ssh.Session) {}
for _, m := range mw {
@@ -28,7 +33,11 @@ func MiddlewareWithLogger(logger *log.Logger, mw ...wish.Middleware) wish.Middle
func() {
defer func() {
if r := recover(); r != nil {
- logger.Printf("panic: %v\n%s", r, string(debug.Stack()))
+ logger.Printf(
+ "panic: %v\n%s",
+ r,
+ string(debug.Stack()),
+ )
}
}()
h(s)
diff --git a/recover/recover_test.go b/recover/recover_test.go
index bfd941c..2eac95c 100644
--- a/recover/recover_test.go
+++ b/recover/recover_test.go
@@ -3,8 +3,8 @@ package recover
import (
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
gossh "golang.org/x/crypto/ssh"
)
diff --git a/scp/copy_from_client.go b/scp/copy_from_client.go
index ebc8301..d2d01bb 100644
--- a/scp/copy_from_client.go
+++ b/scp/copy_from_client.go
@@ -9,8 +9,9 @@ import (
"path/filepath"
"regexp"
"strconv"
+ "strings"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
var (
@@ -39,22 +40,23 @@ func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) err
)
for {
- line, _, err := r.ReadLine()
+ line, err := r.ReadString('\n')
if err != nil {
if errors.Is(err, io.EOF) {
break
}
return fmt.Errorf("failed to read line: %w", err)
}
+ line = strings.TrimSuffix(line, "\n")
- if matches := reTimestamp.FindAllStringSubmatch(string(line), 2); matches != nil {
+ if matches := reTimestamp.FindAllStringSubmatch(line, 2); matches != nil {
mtime, err = strconv.ParseInt(matches[0][1], 10, 64)
if err != nil {
- return parseError{string(line)}
+ return parseError{line}
}
atime, err = strconv.ParseInt(matches[0][2], 10, 64)
if err != nil {
- return parseError{string(line)}
+ return parseError{line}
}
// accepts the header
@@ -62,19 +64,19 @@ func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) err
continue
}
- if matches := reNewFile.FindAllStringSubmatch(string(line), 3); matches != nil {
+ if matches := reNewFile.FindAllStringSubmatch(line, 3); matches != nil {
if len(matches) != 1 || len(matches[0]) != 4 {
- return parseError{string(line)}
+ return parseError{line}
}
mode, err := strconv.ParseUint(matches[0][1], 8, 32)
if err != nil {
- return parseError{string(line)}
+ return parseError{line}
}
size, err := strconv.ParseInt(matches[0][2], 10, 64)
if err != nil {
- return parseError{string(line)}
+ return parseError{line}
}
name := matches[0][3]
@@ -107,14 +109,14 @@ func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) err
continue
}
- if matches := reNewFolder.FindAllStringSubmatch(string(line), 2); matches != nil {
+ if matches := reNewFolder.FindAllStringSubmatch(line, 2); matches != nil {
if len(matches) != 1 || len(matches[0]) != 3 {
- return parseError{string(line)}
+ return parseError{line}
}
mode, err := strconv.ParseUint(matches[0][1], 8, 32)
if err != nil {
- return parseError{string(line)}
+ return parseError{line}
}
name := matches[0][2]
@@ -136,7 +138,7 @@ func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) err
continue
}
- if string(line) == "E" {
+ if line == "E" {
path = filepath.Dir(path)
// says 'hey im done'
@@ -144,7 +146,7 @@ func copyFromClient(s ssh.Session, info Info, handler CopyFromClientHandler) err
continue
}
- return fmt.Errorf("unhandled input: %q", string(line))
+ return fmt.Errorf("unhandled input: %q", line)
}
_, _ = s.Write(NULL)
diff --git a/scp/copy_to_client.go b/scp/copy_to_client.go
index d6aeefb..70ef867 100644
--- a/scp/copy_to_client.go
+++ b/scp/copy_to_client.go
@@ -4,7 +4,7 @@ import (
"fmt"
"io/fs"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
func copyToClient(s ssh.Session, info Info, handler CopyToClientHandler) error {
@@ -18,7 +18,9 @@ func copyToClient(s ssh.Session, info Info, handler CopyToClientHandler) error {
rootEntry := &RootEntry{}
var closers []func() error
- defer closeAll(closers)
+ defer func() {
+ closeAll(closers)
+ }()
for _, match := range matches {
if !info.Recursive {
diff --git a/scp/filesystem.go b/scp/filesystem.go
index d4b1569..72f4903 100644
--- a/scp/filesystem.go
+++ b/scp/filesystem.go
@@ -9,7 +9,7 @@ import (
"strings"
"time"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
type fileSystemHandler struct{ root string }
@@ -61,7 +61,14 @@ func (h *fileSystemHandler) Glob(_ ssh.Session, s string) ([]string, error) {
}
func (h *fileSystemHandler) WalkDir(_ ssh.Session, path string, fn fs.WalkDirFunc) error {
- return filepath.WalkDir(h.prefixed(path), fn)
+ return filepath.WalkDir(h.prefixed(path), func(path string, d fs.DirEntry, err error) error {
+ // if h.root is ./foo/bar, we don't want to server `bar` as the root,
+ // but instead its contents.
+ if path == h.root {
+ return err
+ }
+ return fn(path, d, err)
+ })
}
func (h *fileSystemHandler) NewDirEntry(_ ssh.Session, name string) (*DirEntry, error) {
@@ -113,9 +120,13 @@ func (h *fileSystemHandler) Write(_ ssh.Session, entry *FileEntry) (int64, error
if err != nil {
return 0, fmt.Errorf("failed to open file: %q: %w", entry.Filepath, err)
}
+ defer f.Close() //nolint:errcheck
written, err := io.Copy(f, entry.Reader)
if err != nil {
return 0, fmt.Errorf("failed to write file: %q: %w", entry.Filepath, err)
}
+ if err := f.Close(); err != nil {
+ return 0, fmt.Errorf("failed to close file: %q: %w", entry.Filepath, err)
+ }
return written, h.chtimes(entry.Filepath, entry.Mtime, entry.Atime)
}
diff --git a/scp/filesystem_test.go b/scp/filesystem_test.go
index 1bf3f31..a0ced6a 100644
--- a/scp/filesystem_test.go
+++ b/scp/filesystem_test.go
@@ -71,8 +71,8 @@ func TestFilesystem(t *testing.T) {
session := setup(t, h, nil)
bts, err := session.CombinedOutput("scp -r -f a")
- requireEqualGolden(t, bts)
is.NoErr(err)
+ requireEqualGolden(t, bts)
})
t.Run("recursive glob", func(t *testing.T) {
@@ -88,8 +88,8 @@ func TestFilesystem(t *testing.T) {
session := setup(t, h, nil)
bts, err := session.CombinedOutput("scp -r -f a/*")
- requireEqualGolden(t, bts)
is.NoErr(err)
+ requireEqualGolden(t, bts)
})
t.Run("recursive invalid file", func(t *testing.T) {
@@ -102,6 +102,23 @@ func TestFilesystem(t *testing.T) {
_, err := session.CombinedOutput("scp -r -f a")
is.True(err != nil)
})
+
+ t.Run("recursive folder", func(t *testing.T) {
+ is := is.New(t)
+
+ dir := t.TempDir()
+ h := NewFileSystemHandler(dir)
+
+ is.NoErr(os.MkdirAll(filepath.Join(dir, "a/b/c/d/e"), 0o755))
+ is.NoErr(os.WriteFile(filepath.Join(dir, "a/b/c.txt"), []byte("c text file"), 0o644))
+ is.NoErr(os.WriteFile(filepath.Join(dir, "a/b/c/d/e/e.txt"), []byte("e text file"), 0o644))
+ chtimesTree(t, dir, atime, mtime)
+
+ session := setup(t, h, nil)
+ bts, err := session.CombinedOutput("scp -r -f /")
+ is.NoErr(err)
+ requireEqualGolden(t, bts)
+ })
})
t.Run("scp -t", func(t *testing.T) {
@@ -209,7 +226,7 @@ func TestFilesystem(t *testing.T) {
err := h.Mkdir(nil, &DirEntry{
Name: "foo",
Filepath: "foo/bar/baz",
- Mode: 0755,
+ Mode: 0o755,
})
is.True(err != nil) // should err
})
@@ -222,7 +239,7 @@ func TestFilesystem(t *testing.T) {
_, err := h.Write(nil, &FileEntry{
Name: "foo.txt",
Filepath: "baz/foo.txt",
- Mode: 0644,
+ Mode: 0o644,
Size: 10,
})
is.True(err != nil) // should err
@@ -234,23 +251,21 @@ func TestFilesystem(t *testing.T) {
_, err := h.Write(nil, &FileEntry{
Name: "foo.txt",
Filepath: "foo.txt",
- Mode: 0644,
+ Mode: 0o644,
Size: 10,
Reader: iotest.ErrReader(fmt.Errorf("fake err")),
})
is.True(err != nil) // should err
})
})
-
})
-
}
func chtimesTree(tb testing.TB, dir string, atime, mtime time.Time) {
- is := is.New(tb)
-
- filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
- is.NoErr(os.Chtimes(path, atime, mtime))
- return nil
- })
+ is.New(tb).NoErr(filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ return os.Chtimes(path, atime, mtime)
+ }))
}
diff --git a/scp/fs.go b/scp/fs.go
index fe3aa0a..1c9fede 100644
--- a/scp/fs.go
+++ b/scp/fs.go
@@ -4,7 +4,7 @@ import (
"fmt"
"io/fs"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
type fsHandler struct{ fsys fs.FS }
@@ -26,6 +26,7 @@ func (h *fsHandler) WalkDir(_ ssh.Session, path string, fn fs.WalkDirFunc) error
}
func (h *fsHandler) NewDirEntry(_ ssh.Session, path string) (*DirEntry, error) {
+ path = normalizePath(path)
info, err := fs.Stat(h.fsys, path)
if err != nil {
return nil, fmt.Errorf("failed to open dir: %q: %w", path, err)
diff --git a/scp/fs_test.go b/scp/fs_test.go
index 8471b81..2c03982 100644
--- a/scp/fs_test.go
+++ b/scp/fs_test.go
@@ -67,8 +67,8 @@ func TestFS(t *testing.T) {
session := setup(t, h, nil)
bts, err := session.CombinedOutput("scp -r -f a")
- requireEqualGolden(t, bts)
is.NoErr(err)
+ requireEqualGolden(t, bts)
})
t.Run("recursive glob", func(t *testing.T) {
@@ -84,8 +84,25 @@ func TestFS(t *testing.T) {
session := setup(t, h, nil)
bts, err := session.CombinedOutput("scp -r -f a/*")
+ is.NoErr(err)
requireEqualGolden(t, bts)
+ })
+
+ t.Run("recursive folder", func(t *testing.T) {
+ is := is.New(t)
+
+ dir := t.TempDir()
+ h := NewFileSystemHandler(dir)
+
+ is.NoErr(os.MkdirAll(filepath.Join(dir, "a/b/c/d/e"), 0o755))
+ is.NoErr(os.WriteFile(filepath.Join(dir, "a/b/c.txt"), []byte("c text file"), 0o644))
+ is.NoErr(os.WriteFile(filepath.Join(dir, "a/b/c/d/e/e.txt"), []byte("e text file"), 0o644))
+ chtimesTree(t, dir, atime, mtime)
+
+ session := setup(t, h, nil)
+ bts, err := session.CombinedOutput("scp -r -f /")
is.NoErr(err)
+ requireEqualGolden(t, bts)
})
t.Run("recursive invalid file", func(t *testing.T) {
diff --git a/scp/scp.go b/scp/scp.go
index 03d3f71..21e6370 100644
--- a/scp/scp.go
+++ b/scp/scp.go
@@ -6,11 +6,12 @@ import (
"io"
"io/fs"
"path/filepath"
+ "runtime"
"strconv"
"strings"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish"
- "github.com/gliderlabs/ssh"
)
// CopyToClientHandler is a handler that can be implemented to handle files
@@ -19,7 +20,7 @@ type CopyToClientHandler interface {
// Glob should be implemented if you want to provide server-side globbing
// support.
//
- // A minimal implementation to disable it ist to return `[]string{s}, nil`.
+ // A minimal implementation to disable it is to return `[]string{s}, nil`.
//
// Note: if your other functions expect a relative path, make sure that
// your Glob implementation returns relative paths as well.
@@ -150,7 +151,7 @@ type RootEntry []Entry
// Appennd the given entry to a child directory, or the the itself if
// none matches.
func (e *RootEntry) Append(entry Entry) {
- parent := filepath.Dir(entry.path())
+ parent := normalizePath(filepath.Dir(entry.path()))
for _, child := range *e {
switch dir := child.(type) {
@@ -159,7 +160,7 @@ func (e *RootEntry) Append(entry Entry) {
dir.Children = append(dir.Children, entry)
return
}
- if strings.HasPrefix(parent, dir.Filepath) {
+ if strings.HasPrefix(parent, normalizePath(dir.Filepath)) {
dir.Append(entry)
return
}
@@ -220,7 +221,7 @@ func (e *DirEntry) Write(w io.Writer) error {
// Appends an entry to the folder or their children.
func (e *DirEntry) Append(entry Entry) {
- parent := filepath.Dir(entry.path())
+ parent := normalizePath(filepath.Dir(entry.path()))
for _, child := range e.Children {
switch dir := child.(type) {
@@ -229,7 +230,7 @@ func (e *DirEntry) Append(entry Entry) {
dir.Children = append(dir.Children, entry)
return
}
- if strings.HasPrefix(parent, dir.path()) {
+ if strings.HasPrefix(parent, normalizePath(dir.path())) {
dir.Append(entry)
return
}
@@ -257,7 +258,7 @@ type Info struct {
// Ok is true if the current session is a SCP.
Ok bool
- // Recursice is true if its a recursive SCP.
+ // Recursive is true if its a recursive SCP.
Recursive bool
// Path is the server path of the scp operation.
@@ -294,3 +295,11 @@ func GetInfo(cmd []string) Info {
func octalPerms(info fs.FileMode) string {
return "0" + strconv.FormatUint(uint64(info.Perm()), 8)
}
+
+func normalizePath(p string) string {
+ p = filepath.Clean(p)
+ if runtime.GOOS == "windows" {
+ return strings.ReplaceAll(p, "\\", "/")
+ }
+ return p
+}
diff --git a/scp/scp_test.go b/scp/scp_test.go
index a24c1a2..6fe34d4 100644
--- a/scp/scp_test.go
+++ b/scp/scp_test.go
@@ -4,10 +4,12 @@ import (
"bytes"
"os"
"path/filepath"
+ "runtime"
"testing"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
+ "github.com/google/go-cmp/cmp"
"github.com/matryer/is"
gossh "golang.org/x/crypto/ssh"
)
@@ -115,7 +117,6 @@ func setup(tb testing.TB, rh CopyToClientHandler, wh CopyFromClientHandler) *gos
return testsession.New(tb, &ssh.Server{
Handler: Middleware(rh, wh)(func(s ssh.Session) {
s.Exit(0)
- s.Close()
}),
}, nil)
}
@@ -124,7 +125,17 @@ func requireEqualGolden(tb testing.TB, out []byte) {
tb.Helper()
is := is.New(tb)
- out = bytes.ReplaceAll(out, NULL, []byte("<NULL>"))
+ fixOutput := func(bts []byte) []byte {
+ bts = bytes.ReplaceAll(bts, []byte("\r"), []byte(""))
+ if runtime.GOOS == "windows" {
+ // perms always come different on Windows because, well, its Windows.
+ bts = bytes.ReplaceAll(bts, []byte("0666"), []byte("0644"))
+ bts = bytes.ReplaceAll(bts, []byte("0777"), []byte("0755"))
+ }
+ return bytes.ReplaceAll(bts, NULL, []byte("<NULL>"))
+ }
+
+ out = fixOutput(out)
golden := "testdata/" + tb.Name() + ".test"
if os.Getenv("UPDATE") != "" {
is.NoErr(os.MkdirAll(filepath.Dir(golden), 0o755))
@@ -133,7 +144,9 @@ func requireEqualGolden(tb testing.TB, out []byte) {
gbts, err := os.ReadFile(golden)
is.NoErr(err)
+ gbts = fixOutput(gbts)
- gbts = bytes.ReplaceAll(gbts, NULL, []byte("<NULL>"))
- is.Equal(string(gbts), string(out))
+ if diff := cmp.Diff(string(gbts), string(out)); diff != "" {
+ tb.Fatal("files do not match:", diff)
+ }
}
diff --git a/scp/testdata/TestFS/recursive_folder.test b/scp/testdata/TestFS/recursive_folder.test
new file mode 100644
index 0000000..5621c00
--- /dev/null
+++ b/scp/testdata/TestFS/recursive_folder.test
@@ -0,0 +1,19 @@
+T1323853868 0 1323853868 0
+D0755 0 a
+T1323853868 0 1323853868 0
+D0755 0 b
+T1323853868 0 1323853868 0
+D0755 0 c
+T1323853868 0 1323853868 0
+D0755 0 d
+T1323853868 0 1323853868 0
+D0755 0 e
+T1323853868 0 1323853868 0
+C0644 11 e.txt
+e text file<NULL>E
+E
+E
+T1323853868 0 1323853868 0
+C0644 11 c.txt
+c text file<NULL>E
+E
diff --git a/scp/testdata/TestFilesystem/scp_-f/recursive_folder.test b/scp/testdata/TestFilesystem/scp_-f/recursive_folder.test
new file mode 100644
index 0000000..5621c00
--- /dev/null
+++ b/scp/testdata/TestFilesystem/scp_-f/recursive_folder.test
@@ -0,0 +1,19 @@
+T1323853868 0 1323853868 0
+D0755 0 a
+T1323853868 0 1323853868 0
+D0755 0 b
+T1323853868 0 1323853868 0
+D0755 0 c
+T1323853868 0 1323853868 0
+D0755 0 d
+T1323853868 0 1323853868 0
+D0755 0 e
+T1323853868 0 1323853868 0
+C0644 11 e.txt
+e text file<NULL>E
+E
+E
+T1323853868 0 1323853868 0
+C0644 11 c.txt
+c text file<NULL>E
+E
diff --git a/testdata/authorized_keys b/testdata/authorized_keys
index 2f08859..225dc32 100644
--- a/testdata/authorized_keys
+++ b/testdata/authorized_keys
@@ -3,4 +3,6 @@ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQChxV3pJRnXP7crH+4xxH8skCF/Bs8JX8VTjlS4dpLY
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCdJkpQAr3zhC+grKMexj8zgJIuAQ/2LR59RvXemEAovd671Et356cmHnCDmUvUlH/70xQdyL3n68tzu2ZEzKheQP5vz05CAFXTi7rlMvhtz632mLMPlU3lGuP+A6rzqNSnTtrIa2Q3Fe2ir6N+ad782J8g6frGJaVfA/G7j/M1JwyDJWzUS3HvDHDO+qFze71h0/o9W1+VoRaSfD67BzPQumkEkt/CilSPU8VKRP3q/FIeIrgTBhNh17SX/qlnyrJipDTF1QtXUOK4H5TsEE0S13z8a4Wo37kRWQPxdjWyfX9tBjsN86n+R7OGSXXdi10n9THrisdgx2GKsk1HjY+u5YlDpDysFLBs6j4nWeTxnrjgx6HUqvMk3mdqrAKHTglt34OUQtB463GMgCW85w+ni8ebPKlt5YQsXalilcoI4K7fakyXe+o9Y0sCwE3SLXEJhtd/Esz1pVzvMBCshpRknBPFh/gs/i1YuL0SJqI2BGBFs0d/ARwqUQSoXXBTJPc= k5@test
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7InQIj/ROngoWWb6kXTcTJd8+u5skDfGm8JJxRugMB k2@test
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDgteu96TZLd3iG11D5NqBsQRvhW2I6iD/ycwOiWFjFyv4MAHaDFiIazbeQSbi++1+5vspeNuv9AKJFgG0SpnjMLQM0rJb5DIsuRxGOAS/oh82yNCxYcW2+eXcqUDL4V+fZ6eIqtSIBrPQY89/CbZ4nFtw7+941gmFa2+7Wj9vLk4GTiyu/jQsbGnAZUCMvce1jFZ9XDMYSYzXEtkqhBT6eYDd7xMQejovszJfPqlKDxpMZxpaDsQGf+00IJPZUUxkX62eAmrlX1q4XO+m2zIjGpf/gdNKHEMXQrvBWdvwg0rat2i+PCW4Rbwx7wHBBWPRqEPjcVTfwvOWoZGGU3TSX8M7Gcj+ZvAD/uV7DWcNi61Obtw/6PXYvKFZWcZ1sHxUTI84CUcVLSL55hOtJqCuJXmUdKdBcJLyo3NValIjIZn+ljn6biVAr00nGo06nO4j2eTE2ZLOZFEB6rHuf1iaT18EiJgnJGrB7HY4+KUoUIzmvzQrKxxLbIe957hnx+TE= k6@test
+
+# a commented line, and the previous line was empty
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMJlb/qf2B2kMNdBxfpCQqI2ctPcsOkdZGVh5zTRhKtH k3@test
diff --git a/testsession/testsession.go b/testsession/testsession.go
index 40c7798..5e10a28 100644
--- a/testsession/testsession.go
+++ b/testsession/testsession.go
@@ -1,13 +1,13 @@
// Package testsession provides utilities to test SSH sessions.
//
-// more or less copied from gliderlabs/ssh tests
+// more or less copied from charmbracelet/ssh tests
package testsession
import (
"net"
"testing"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
gossh "golang.org/x/crypto/ssh"
)
@@ -45,6 +45,8 @@ func newLocalListener(tb testing.TB) net.Listener {
tb.Fatalf("failed to listen on a port: %v", err)
}
}
+
+ tb.Cleanup(func() { _ = l.Close() })
return l
}
diff --git a/testsession/testsession_test.go b/testsession/testsession_test.go
index ab11191..d302909 100644
--- a/testsession/testsession_test.go
+++ b/testsession/testsession_test.go
@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
func TestSession(t *testing.T) {
diff --git a/wish.go b/wish.go
index 51ac209..dbfc8f2 100644
--- a/wish.go
+++ b/wish.go
@@ -5,7 +5,7 @@ import (
"io"
"github.com/charmbracelet/keygen"
- "github.com/gliderlabs/ssh"
+ "github.com/charmbracelet/ssh"
)
// Middleware is a function that takes an ssh.Handler and returns an
@@ -26,11 +26,11 @@ func NewServer(ops ...ssh.Option) (*ssh.Server, error) {
}
}
if len(s.HostSigners) == 0 {
- k, err := keygen.New("", nil, keygen.Ed25519)
+ k, err := keygen.New("id_ed25519", keygen.WithKeyType(keygen.Ed25519), keygen.WithWrite())
if err != nil {
return nil, err
}
- err = s.SetOption(WithHostKeyPEM(k.PrivateKeyPEM()))
+ err = s.SetOption(WithHostKeyPEM(k.RawPrivateKey()))
if err != nil {
return nil, err
}
diff --git a/wish_test.go b/wish_test.go
index 32cccc9..f9f77e2 100755
--- a/wish_test.go
+++ b/wish_test.go
@@ -1,30 +1,33 @@
-// go:genarate mockgen -package mocks -destination mocks/session.go github.com/gliderlabs/ssh Session
+// go:generate mockgen -package mocks -destination mocks/session.go github.com/charmbracelet/ssh Session
package wish
import (
"bytes"
"errors"
+ "path/filepath"
"strings"
"testing"
"time"
+ "github.com/charmbracelet/ssh"
"github.com/charmbracelet/wish/testsession"
- "github.com/gliderlabs/ssh"
)
func TestNewServer(t *testing.T) {
- _, err := NewServer()
+ fp := filepath.Join(t.TempDir(), "id_ed25519")
+ _, err := NewServer(WithHostKeyPath(fp))
if err != nil {
t.Fatal(err)
}
}
func TestNewServerWithOptions(t *testing.T) {
- _, err := NewServer(
+ fp := filepath.Join(t.TempDir(), "id_ed25519")
+ if _, err := NewServer(
+ WithHostKeyPath(fp),
WithMaxTimeout(time.Second),
WithAddress(":2222"),
- )
- if err != nil {
+ ); err != nil {
t.Fatal(err)
}
}