From 5f837b5e89dbe7f576d82b0d0854b72a35a9da14 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Sat, 14 Jan 2023 02:09:58 +0100 Subject: [PATCH] feat: better CLI, build and serve mode --- cmd/cabret/build.go | 1 + cmd/cabret/main.go | 172 +++++++++++++++++++++++++++++-- cmd/cabret/serve.go | 165 +++++++++++++++++++++++++++++ examples/basic/index.html | 4 + examples/basic/layouts/base.html | 2 + examples/basic/posts/post-1.md | 3 +- go.mod | 8 ++ go.sum | 48 +++++++++ 8 files changed, 396 insertions(+), 7 deletions(-) create mode 100644 cmd/cabret/build.go create mode 100644 cmd/cabret/serve.go diff --git a/cmd/cabret/build.go b/cmd/cabret/build.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/cmd/cabret/build.go @@ -0,0 +1 @@ +package main diff --git a/cmd/cabret/main.go b/cmd/cabret/main.go index 35f070e..80a8697 100644 --- a/cmd/cabret/main.go +++ b/cmd/cabret/main.go @@ -1,25 +1,185 @@ package main import ( + "fmt" "log" + "os" + "strings" + "github.com/alecthomas/repr" "github.com/aziis98/cabret/config" "github.com/aziis98/cabret/runner" "github.com/spf13/pflag" ) +const HelpMessage = `usage: cabret [OPTIONS...] +subcommands: + build [OPTIONS...] Builds the current project + serve [OPTIONS...] Starts an http server and watches files for changes + +` + func main() { - log.SetFlags(0) + flagSet := pflag.FlagSet{ + Usage: func() { + fmt.Print(HelpMessage) + }, + } + if err := flagSet.Parse(os.Args[1:]); err != nil { + if err != pflag.ErrHelp { + log.Fatal(err) + } + return + } + + switch flagSet.Arg(0) { + case "serve": + if err := serveSubcommand(os.Args[2:]); err != nil { + if err != pflag.ErrHelp { + log.Fatal(err) + } + return + } + + case "build": + if err := buildSubcommand(os.Args[2:]); err != nil { + if err != pflag.ErrHelp { + log.Fatal(err) + } + return + } + + default: + fmt.Print(HelpMessage) + + } +} + +const BuildHelpMessage = `usage: cabret build [OPTIONS...] +options: - optConfig := pflag.StringP("config", "c", "Cabretfile.yaml", `which configuration file to use`) - pflag.Parse() + -c, --config Path to configuration file (default is "Cabretfile.yaml") + -v, --verbose Be more verbose - cabretfile, err := config.ReadCabretfile(*optConfig) +` + +func buildSubcommand(args []string) error { + flagSet := pflag.FlagSet{ + Usage: func() { fmt.Print(BuildHelpMessage) }, + } + + var configFile string + flagSet.StringVarP(&configFile, "config", "c", + "Cabretfile.yaml", + `path to configuration file`, + ) + + var verbose bool + flagSet.BoolVarP(&verbose, "verbose", "v", + false, + `verbose output`, + ) + + if err := flagSet.Parse(args); err != nil { + return err + } + + cabretfile, err := config.ReadCabretfile(configFile) if err != nil { - log.Fatal(err) + return err } if err := runner.RunConfig(cabretfile); err != nil { - log.Fatal(err) + return err + } + + return nil +} + +const ServeHelpMessage = `usage: cabret serve [OPTIONS...] +options: + + -c, --config Path to configuration file (default is "Cabretfile.yaml") + -v, --verbose Be more verbose + -w, --watch List of paths to watch for rebuild + -m, --mount Mount a file or folder on the default static file + server, this will mount file or folder at "" + on route "". + -m, --mount : Mount a file or folder on the default static file + server, this will mount file or folder at "" + on route "". + +By default the mount list is "public/:/", "dist/:/", "out/:/". + +The server will also serve a special js script on "/cabret/live-reload.js" that +will add live reload on file change for development, this can be included with +the following tag + + + +` + +func serveSubcommand(args []string) error { + flagSet := pflag.FlagSet{ + Usage: func() { fmt.Print(ServeHelpMessage) }, } + + var configFile string + flagSet.StringVarP(&configFile, "config", "c", + "Cabretfile.yaml", + `path to configuration file`, + ) + + var verbose bool + flagSet.BoolVarP(&verbose, "verbose", "v", + false, + `verbose output`, + ) + + var watchedPatterns []string + flagSet.StringArrayVarP(&watchedPatterns, "watch", "w", + []string{"src/**/"}, + `list of paths to watch for rebuild`, + ) + + var mounts []string + flagSet.StringArrayVarP(&mounts, "mount", "m", + []string{"public/:/", "out/:/", "dist/:/"}, + `list of paths to mount for the static file server`, + ) + + err := flagSet.Parse(args) + if err != nil { + return err + } + + serveMounts := make([]serveMount, len(mounts)) + for i, m := range mounts { + path, route, found := strings.Cut(m, ":") + if !found { + if strings.HasPrefix(path, "/") { + return fmt.Errorf(`cannot mount an absolute path without a route: "%s"`, path) + } + + route = "/" + path + } + + if !strings.HasPrefix(route, "/") { + return fmt.Errorf(`route must start with a slash: "%s"`, route) + } + + serveMounts[i] = serveMount{path, route} + } + + log.Printf(`configFile = %v`, repr.String(configFile)) + log.Printf(`verbose = %v`, repr.String(verbose)) + log.Printf(`watchedPatterns = %v`, repr.String(watchedPatterns)) + log.Printf(`serveMounts = %v`, repr.String(serveMounts)) + + return serve(serveConfig{ + configFile, + verbose, + watchedPatterns, + serveMounts, + }) } diff --git a/cmd/cabret/serve.go b/cmd/cabret/serve.go new file mode 100644 index 0000000..8ea7578 --- /dev/null +++ b/cmd/cabret/serve.go @@ -0,0 +1,165 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/http" + "path/filepath" + "strings" + "time" + + "github.com/aziis98/cabret/config" + "github.com/aziis98/cabret/runner" + "github.com/fsnotify/fsnotify" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" + "github.com/gobwas/ws" + "github.com/gobwas/ws/wsutil" +) + +type serveMount struct { + path, route string +} + +type serveConfig struct { + configFile string + verbose bool + watchedPatterns []string + mounts []serveMount +} + +const LiveReloadScript = ` +const ws = new WebSocket("ws://" + location.host + "/__cabret__/websocket") +ws.addEventListener("message", e => { + console.log("Server: " + e.data) + if (e.data === "reload") { + location.reload() + } +}) +ws.addEventListener("close", e => { + console.log("Connection closed") + location.reload() +}) + +console.log("Live reload enabled") +` + +func serve(c serveConfig) error { + cabretFile, err := config.ReadCabretfile(c.configFile) + if err != nil { + return err + } + + listeners := map[net.Conn]struct{}{} + + // Create new watcher. + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + // Start listening for events. + go func() { + lastRebuild := time.Now() + + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + if event.Has(fsnotify.Write) { + log.Printf(`[Watcher] event: %s`, event) + + if lastRebuild.Add(50 * time.Millisecond).After(time.Now()) { + log.Printf(`[Watcher] too fast, skipping`) + continue + } + + // TODO: Make this incremental + + // trigger full rebuild + err := runner.RunConfig(cabretFile) + if err != nil { + log.Fatal(err) + return + } + + // broadcast refresh + for conn := range listeners { + err := wsutil.WriteServerText(conn, []byte("reload")) + if err != nil { + log.Printf(`[LiveReload] got "%v", removing listener`, err) + delete(listeners, conn) + } + } + + lastRebuild = time.Now() + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error:", err) + } + } + }() + + // register watchers + for _, pattern := range c.watchedPatterns { + matches, err := filepath.Glob(pattern) + if err != nil { + return err + } + + for _, m := range matches { + log.Printf(`[Watcher] registering "%s"`, m) + + if err := watcher.Add(m); err != nil { + return fmt.Errorf(`cannot register pattern "%s": %w`, pattern, err) + } + } + } + + r := chi.NewRouter() + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + r.Use(middleware.NoCache) + + r.Get("/__cabret__/live-reload.js", func(w http.ResponseWriter, r *http.Request) { + http.ServeContent(w, r, "live-reload.js", time.Time{}, strings.NewReader(LiveReloadScript)) + }) + + r.Get("/__cabret__/websocket", func(w http.ResponseWriter, r *http.Request) { + conn, _, _, err := ws.UpgradeHTTP(r, w) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + listeners[conn] = struct{}{} + }) + + for _, m := range c.mounts { + if strings.HasSuffix(m.route, "/") { + log.Printf(`[FileServer] mounting directory "%s" on "%s"`, m.path, m.route) + r.Handle(m.route+"*", http.StripPrefix(m.route, http.FileServer(http.Dir(m.path)))) + } else { + log.Printf(`[FileServer] mounting file "%s" on "%s"`, m.path, m.route) + r.Get(m.route, func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, m.path) + }) + } + } + + if err := runner.RunConfig(cabretFile); err != nil { + return err + } + + server := &http.Server{Addr: ":5000", Handler: r} + + log.Printf(`[FileServer] starting on ":5000"`) + return server.ListenAndServe() +} diff --git a/examples/basic/index.html b/examples/basic/index.html index d9ab390..64ab3b7 100644 --- a/examples/basic/index.html +++ b/examples/basic/index.html @@ -4,3 +4,7 @@ Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima, eveniet, dolorum amet, cupiditate quae excepturi aspernatur dolor voluptatem obcaecati ratione quas? Et explicabo illum iure eius porro, dolor quos doloremque!

+

+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima, eveniet, dolorum amet, cupiditate quae excepturi aspernatur dolor + voluptatem obcaecati ratione quas? Et explicabo illum iure eius porro, dolor quos doloremque! +

diff --git a/examples/basic/layouts/base.html b/examples/basic/layouts/base.html index 37d6dcd..4691aa0 100644 --- a/examples/basic/layouts/base.html +++ b/examples/basic/layouts/base.html @@ -5,6 +5,8 @@ {{- if .Title -}}{{ .Title }} | {{ else }}{{ end -}} My Blog + + {{ .Content }} diff --git a/examples/basic/posts/post-1.md b/examples/basic/posts/post-1.md index 908df5f..4d3eee2 100644 --- a/examples/basic/posts/post-1.md +++ b/examples/basic/posts/post-1.md @@ -9,4 +9,5 @@ tags: [a, b] Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi? -Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi? \ No newline at end of file +Lorem ipsum dolor sit amet consectetur, adipisicing elit. Beatae ratione eos saepe veritatis, temporibus nemo vero rerum earum voluptatibus minus voluptatem neque consequatur necessitatibus, error magnam deserunt! Ad, accusantium excepturi? + diff --git a/go.mod b/go.mod index e33d014..42dfc59 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,15 @@ require ( github.com/bradleyjkemp/cupaloy v2.3.0+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.13.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-chi/chi/v5 v5.0.8 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.1.0 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/iancoleman/strcase v0.2.0 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/klauspost/compress v1.10.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.16 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -26,4 +33,5 @@ require ( gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect + nhooyr.io/websocket v1.8.7 // indirect ) diff --git a/go.sum b/go.sum index a7d2042..a855baa 100644 --- a/go.sum +++ b/go.sum @@ -4,15 +4,46 @@ github.com/alecthomas/repr v0.1.1 h1:87P60cSmareLAxMc4Hro0r2RBY4ROm0dYwkJNpS4pPs github.com/alecthomas/repr v0.1.1/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/bradleyjkemp/cupaloy v2.3.0+incompatible h1:UafIjBvWQmS9i/xRg+CamMrnLTKNzo+bdmT/oH34c2Y= github.com/bradleyjkemp/cupaloy v2.3.0+incompatible/go.mod h1:Au1Xw1sgaJ5iSFktEhYsS0dbQiS1B0/XMXl+42y9Ilk= +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/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0= +github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -23,6 +54,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky 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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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= @@ -31,6 +64,11 @@ github.com/rakyll/gotest v0.0.6 h1:hBTqkO3jiuwYW/M9gL4bu0oTYcm8J6knQAAPUsJsz1I= github.com/rakyll/gotest v0.0.6/go.mod h1:SkoesdNCWmiD4R2dljIUcfSnNdVZ12y8qK4ojDkc2Sc= 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/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M= github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= @@ -43,18 +81,28 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/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-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=