feat: better CLI, build and serve mode

dev
Antonio De Lucreziis 2 years ago
parent f5d34a7c51
commit 5f837b5e89

@ -0,0 +1 @@
package main

@ -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 <SUBCOMMAND> [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> 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> Path to configuration file (default is "Cabretfile.yaml")
-v, --verbose Be more verbose
-w, --watch <pattern> List of paths to watch for rebuild
-m, --mount <path> Mount a file or folder on the default static file
server, this will mount file or folder at "<path>"
on route "<path>".
-m, --mount <from>:<to> Mount a file or folder on the default static file
server, this will mount file or folder at "<from>"
on route "<to>".
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
<script type="module" src="/__cabret__/live-reload.js" async></script>
`
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,
})
}

@ -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()
}

@ -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!
</p>
<p>
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!
</p>

@ -5,6 +5,8 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{- if .Title -}}{{ .Title }} | {{ else }}{{ end -}} My Blog</title>
<script type="module" src="/__cabret__/live-reload.js" async></script>
</head>
<body>
{{ .Content }}

@ -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?
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?

@ -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
)

@ -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=

Loading…
Cancel
Save