diff --git a/Makefile b/Makefile index a98717e3..657176de 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,9 @@ go.vet: go.fmt: @ test -z "$(shell go fmt $(GO_SOURCE))" +go.staticcheck: + @ go run honnef.co/go/tools/cmd/staticcheck $(GO_SOURCE) + go.install: @ go build --trimpath \ --ldflags="-s -w \ @@ -169,8 +172,8 @@ budjs.test: ## test: test.dev -test.dev: go.tools go.generate go.fmt go.vet budjs.check budjs.test go.test -test.all: go.tools go.generate go.vet budjs.check budjs.test go.test +test.dev: go.tools go.generate go.fmt go.vet go.staticcheck budjs.check budjs.test go.test +test.all: go.tools go.generate go.vet go.staticcheck budjs.check budjs.test go.test ## # CI diff --git a/framework/controller/controllerrt/request/unmarshal.go b/framework/controller/controllerrt/request/unmarshal.go index 79851379..27fe7f6b 100644 --- a/framework/controller/controllerrt/request/unmarshal.go +++ b/framework/controller/controllerrt/request/unmarshal.go @@ -3,7 +3,6 @@ package request import ( "encoding/json" "io" - "io/ioutil" "mime" "net/http" "net/url" @@ -60,7 +59,7 @@ func unmarshalForm(r *http.Request, v interface{}) error { } func unmarshalJSON(r io.Reader, v interface{}) error { - data, err := ioutil.ReadAll(r) + data, err := io.ReadAll(r) if err != nil { return err } diff --git a/framework/web/welcome/welcome.go b/framework/web/welcome/welcome.go index ba5c8646..1999dcb2 100644 --- a/framework/web/welcome/welcome.go +++ b/framework/web/welcome/welcome.go @@ -2,7 +2,6 @@ package welcome import ( "embed" - _ "embed" "io/fs" "net/http" diff --git a/go.mod b/go.mod index 55acf025..6f7057d7 100644 --- a/go.mod +++ b/go.mod @@ -26,11 +26,13 @@ require ( golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/tools v0.1.11-0.20220513221640-090b14e8501f + honnef.co/go/tools v0.3.3 rogchap.com/v8go v0.7.0 src.techknowlogick.com/xgo v1.4.1-0.20220413212431-091a0a22b814 ) require ( + github.com/BurntSushi/toml v0.4.1 // indirect github.com/andybalholm/cascadia v1.3.1 // indirect github.com/gedex/inflector v0.0.0-20170307190818-16278e9db813 // indirect github.com/kr/pretty v0.3.0 // indirect @@ -40,6 +42,7 @@ require ( github.com/pointlander/jetset v1.0.1-0.20190518214125-eee7eff80bd4 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/sergi/go-diff v1.2.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect diff --git a/go.sum b/go.sum index 812919ec..d5b806bd 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U= @@ -87,6 +89,8 @@ github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= go.kuoruan.net/v8go-polyfills v0.5.1-0.20220727011656-c74c5b408ebd h1:lMfOO39WTD+CxBPmqZvLdISrLVsEjgNfWoV4viBt15M= go.kuoruan.net/v8go-polyfills v0.5.1-0.20220727011656-c74c5b408ebd/go.mod h1:egHzK8RIHR7dPOYzhnRsomClFTVmYCtvhTWqec4JXaY= +golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e h1:qyrTQ++p1afMkO4DPEeLGq/3oTsdlvdH4vqZUBWzUKM= +golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -115,6 +119,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA= +honnef.co/go/tools v0.3.3/go.mod h1:jzwdWgg7Jdq75wlfblQxO4neNaFFSvgc1tD5Wv8U0Yw= rogchap.com/v8go v0.7.0 h1:kgjbiO4zE5itA962ze6Hqmbs4HgZbGzmueCXsZtremg= rogchap.com/v8go v0.7.0/go.mod h1:MxgP3pL2MW4dpme/72QRs8sgNMmM0pRc8DPhcuLWPAs= src.techknowlogick.com/xgo v1.4.1-0.20220413212431-091a0a22b814 h1:/oIyHjKnlyQ3yFzxq7uin83l6h0sHXT7Z+9TpP9wr8s= diff --git a/internal/cli/testcli/testcli.go b/internal/cli/testcli/testcli.go index ceabbc6d..aab9b2b9 100644 --- a/internal/cli/testcli/testcli.go +++ b/internal/cli/testcli/testcli.go @@ -249,9 +249,9 @@ func checkDate(res *http.Response) error { return err } // Date should be within 1 minute. In reality, it should be almost instant - elapsed := time.Now().Sub(dt) + elapsed := time.Since(dt) if elapsed > time.Minute { - return fmt.Errorf("Date header is too old %s", elapsed) + return fmt.Errorf("date header is too old %s", elapsed) } return nil } diff --git a/internal/cli/toolv8/toolv8.go b/internal/cli/toolv8/toolv8.go index 05f7658c..159aad45 100644 --- a/internal/cli/toolv8/toolv8.go +++ b/internal/cli/toolv8/toolv8.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" v8 "github.com/livebud/bud/package/js/v8" ) @@ -37,7 +36,7 @@ func (c *Command) Run(ctx context.Context) error { } func (c *Command) getScript() (string, error) { - code, err := ioutil.ReadAll(c.Stdin) + code, err := io.ReadAll(c.Stdin) if err != nil { return "", err } diff --git a/internal/dsync/set/set.go b/internal/dsync/set/set.go index 43403d2f..46b3ddd6 100644 --- a/internal/dsync/set/set.go +++ b/internal/dsync/set/set.go @@ -12,7 +12,6 @@ import ( var ( // helpful to not write everywhere struct{}{} - keyExists = struct{}{} nonExistent fs.DirEntry ) diff --git a/internal/golden/golden.go b/internal/golden/golden.go index 2556113e..56f7a0f2 100644 --- a/internal/golden/golden.go +++ b/internal/golden/golden.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/json" "flag" - "io/ioutil" "os" "path/filepath" "testing" @@ -40,7 +39,7 @@ func Test(t testing.TB, actual *txtar.Archive) { t.Helper() filename := filepath.Join("testdata", t.Name()+".golden") formatted := txtar.Format(actual) - expected, err := ioutil.ReadFile(filename) + expected, err := os.ReadFile(filename) if err != nil { if len(formatted) == 0 { return @@ -63,7 +62,7 @@ func writeFile(name string, data []byte) error { if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil { return err } - return ioutil.WriteFile(name, data, 0644) + return os.WriteFile(name, data, 0644) } // TestString diffs two strings diff --git a/internal/is/is_internal.go b/internal/is/is_internal.go index 67ac97b2..20436142 100644 --- a/internal/is/is_internal.go +++ b/internal/is/is_internal.go @@ -48,6 +48,7 @@ type I struct { func (is *I) log(args ...interface{}) { s := is.decorate(fmt.Sprint(args...)) + //lint:ignore SA1006 - s can be a formatted string fmt.Fprintf(is.writer, s) is.fail() } diff --git a/internal/is/is_test.go b/internal/is/is_test.go index 0e332d4e..f299dcd7 100644 --- a/internal/is/is_test.go +++ b/internal/is/is_test.go @@ -348,6 +348,7 @@ func TestTrueOk(t *testing.T) { out := new(bytes.Buffer) is.writer = out is.colorful = false + //lint:ignore SA4000 this is a test is.True(1 == 1) if m.failed { t.Fatalf(`expected no failure`) @@ -363,11 +364,12 @@ func TestTrueMessage(t *testing.T) { out := new(bytes.Buffer) is.writer = out is.colorful = false + //lint:ignore SA4000 this is a test is.True(1 != 1, "expected %d and %d to be equal", 1, 1) if !m.failed { t.Fatalf(`expected is.True to fail`) } - expect := "is_test.go:366: not true: 1 != 1. expected 1 and 1 to be equal\n" + expect := "is_test.go:368: not true: 1 != 1. expected 1 and 1 to be equal\n" if out.String() != expect { t.Fatalf("expected %q, got %q", expect, out) } @@ -383,7 +385,7 @@ func TestInMessage(t *testing.T) { if !m.failed { t.Fatalf(`expected is.In to fail`) } - expect := "is_test.go:382: hi not in hello. hello doesn't contain hi\n" + expect := "is_test.go:384: hi not in hello. hello doesn't contain hi\n" if out.String() != expect { t.Fatalf("expected %q, got %q", expect, out) } @@ -399,7 +401,7 @@ func TestFail(t *testing.T) { if !m.failed { t.Fatalf(`expected is.Fail to fail`) } - expect := "is_test.go:398: failed\n" + expect := "is_test.go:400: failed\n" if out.String() != expect { t.Fatalf("expected %q, got %q", expect, out) } @@ -415,7 +417,7 @@ func TestFailMessage(t *testing.T) { if !m.failed { t.Fatalf(`expected is.Fail to fail`) } - expect := "is_test.go:414: failed. context from \"component\" should have exited\n" + expect := "is_test.go:416: failed. context from \"component\" should have exited\n" if out.String() != expect { t.Fatalf("expected %q, got %q", expect, out) } diff --git a/internal/snapshot/snapshot.go b/internal/snapshot/snapshot.go index 363bebff..a027106f 100644 --- a/internal/snapshot/snapshot.go +++ b/internal/snapshot/snapshot.go @@ -1,15 +1,12 @@ package snapshot import ( - "encoding/base64" "io/fs" "os" "path/filepath" "github.com/livebud/bud/internal/dirhash" "github.com/livebud/bud/internal/targz" - - "github.com/cespare/xxhash" ) func cachePath(key string) (string, error) { @@ -20,12 +17,6 @@ func cachePath(key string) (string, error) { return filepath.Join(dir, key+".tar.gz"), nil } -func hash(input string) string { - hash := xxhash.New() - hash.Write([]byte(input)) - return base64.RawURLEncoding.EncodeToString(hash.Sum(nil)) -} - // Hash a filesystem func Hash(fsys fs.FS) (string, error) { return dirhash.Hash(fsys) diff --git a/internal/testdir/testdir.go b/internal/testdir/testdir.go index 80bcdcc3..8edfb8f2 100644 --- a/internal/testdir/testdir.go +++ b/internal/testdir/testdir.go @@ -62,33 +62,6 @@ type Dir struct { NodeModules map[string]string // name[version] } -func merge(mapfs fstest.MapFS, fsys fs.FS, base ...string) error { - basePath := path.Join(base...) - return fs.WalkDir(fsys, ".", func(filePath string, de fs.DirEntry, err error) error { - if err != nil { - return err - } - fi, err := de.Info() - if err != nil { - return err - } - fullPath := path.Join(basePath, filePath) - mapfs[fullPath] = &fstest.MapFile{ - ModTime: fi.ModTime(), - Mode: fi.Mode(), - } - if de.IsDir() { - return nil - } - data, err := fs.ReadFile(fsys, filePath) - if err != nil { - return err - } - mapfs[fullPath].Data = data - return nil - }) -} - func (d *Dir) mapfs() (fstest.MapFS, error) { mapfs := fstest.MapFS{} // Loop over files diff --git a/internal/testdir/testdir_test.go b/internal/testdir/testdir_test.go index dbc76b62..58c01c73 100644 --- a/internal/testdir/testdir_test.go +++ b/internal/testdir/testdir_test.go @@ -2,7 +2,6 @@ package testdir_test import ( "context" - "io/ioutil" "os" "path/filepath" "testing" @@ -92,18 +91,18 @@ func TestOverwrite(t *testing.T) { is.NoErr(td.Write(ctx)) is.NoErr(td.Exists("controller/controller.go")) is.NoErr(td.Exists("view/index.svelte")) - controller1, err := ioutil.ReadFile(td.Path("controller/controller.go")) + controller1, err := os.ReadFile(td.Path("controller/controller.go")) is.NoErr(err) - view1, err := ioutil.ReadFile(td.Path("view/index.svelte")) + view1, err := os.ReadFile(td.Path("view/index.svelte")) is.NoErr(err) is.Equal(string(view1), `

hello

`) td.Files["view/index.svelte"] = `

hi

` is.NoErr(td.Write(ctx)) is.NoErr(td.Exists("controller/controller.go")) is.NoErr(td.Exists("view/index.svelte")) - controller2, err := ioutil.ReadFile(td.Path("controller/controller.go")) + controller2, err := os.ReadFile(td.Path("controller/controller.go")) is.NoErr(err) - view2, err := ioutil.ReadFile(td.Path("view/index.svelte")) + view2, err := os.ReadFile(td.Path("view/index.svelte")) is.NoErr(err) is.Equal(string(controller1), string(controller2)) is.Equal(string(view2), `

hi

`) diff --git a/package/budfs/genfs/genfs_test.go b/package/budfs/genfs/genfs_test.go index 304f8d32..de6061a1 100644 --- a/package/budfs/genfs/genfs_test.go +++ b/package/budfs/genfs/genfs_test.go @@ -2,8 +2,8 @@ package genfs_test import ( "errors" + "io" "io/fs" - "io/ioutil" "net/http" "net/http/httptest" "os" @@ -595,7 +595,7 @@ func TestHTTP(t *testing.T) { handler(w, r) response := w.Result() - body, err := ioutil.ReadAll(response.Body) + body, err := io.ReadAll(response.Body) is.NoErr(err) is.Equal(string(body), `bud/view/_index.svelte's data`) is.Equal(response.StatusCode, 200) diff --git a/package/budfs/treefs/treefs.go b/package/budfs/treefs/treefs.go index 2f1787d2..1d591d8a 100644 --- a/package/budfs/treefs/treefs.go +++ b/package/budfs/treefs/treefs.go @@ -172,7 +172,6 @@ func (n *Node) print(tp treeprint.Tree) { cp := tp.AddBranch(formatNode(child)) child.print(cp) } - return } func (n *Node) Find(path string) (node *Node, found bool) { diff --git a/package/commander/subcommand.go b/package/commander/subcommand.go index c96a9f95..b65a199a 100644 --- a/package/commander/subcommand.go +++ b/package/commander/subcommand.go @@ -5,12 +5,12 @@ import ( "errors" "flag" "fmt" - "io/ioutil" + "io" ) func newSubcommand(config *config, name, usage string) *Subcommand { fset := flag.NewFlagSet(name, flag.ContinueOnError) - fset.SetOutput(ioutil.Discard) + fset.SetOutput(io.Discard) return &Subcommand{ config: config, fset: fset, diff --git a/package/di/di_test.go b/package/di/di_test.go index 32f924a3..bb2723ed 100644 --- a/package/di/di_test.go +++ b/package/di/di_test.go @@ -3,7 +3,6 @@ package di_test import ( "bytes" "context" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -81,7 +80,7 @@ func runTest(t testing.TB, test Test) { err = os.MkdirAll(targetDir, 0755) is.NoErr(err) outPath := filepath.Join(targetDir, "di.go") - err = ioutil.WriteFile(outPath, []byte(code), 0644) + err = os.WriteFile(outPath, []byte(code), 0644) is.NoErr(err) stdout, err := goRun(ctx, modCache.Directory(), appDir) is.NoErr(err) diff --git a/package/di/finder.go b/package/di/finder.go index bca71feb..31e72d6d 100644 --- a/package/di/finder.go +++ b/package/di/finder.go @@ -76,5 +76,5 @@ func (i *Injector) Find(currModule *gomod.Module, dep Dependency) (Declaration, return decl, nil } // TODO: add breadcrumbs to help with finding the root of this error - return nil, fmt.Errorf("di: unclear how to provide %s.", dep.ID()) + return nil, fmt.Errorf("di: unclear how to provide %s", dep.ID()) } diff --git a/package/gomod/file.go b/package/gomod/file.go index b4ed1650..43331549 100644 --- a/package/gomod/file.go +++ b/package/gomod/file.go @@ -42,9 +42,7 @@ func (f *File) AddReplace(oldPath, oldVers, newPath, newVers string) error { // Return a list of replaces func (f *File) Replaces() (reps []*Replace) { reps = make([]*Replace, len(f.file.Replace)) - for i, rep := range f.file.Replace { - reps[i] = rep - } + copy(reps, f.file.Replace) // Consistent ordering regardless of modfile formatting sort.Slice(reps, func(i, j int) bool { return reps[i].Old.Path < reps[j].Old.Path @@ -55,9 +53,7 @@ func (f *File) Replaces() (reps []*Replace) { // Return a list of requires func (f *File) Requires() (reqs []*Require) { reqs = make([]*Require, len(f.file.Require)) - for i, req := range f.file.Require { - reqs[i] = req - } + copy(reqs, f.file.Require) // Consistent ordering regardless of modfile formatting sort.Slice(reqs, func(i, j int) bool { switch { diff --git a/package/hot/client.go b/package/hot/client.go index db5c7cee..1330e631 100644 --- a/package/hot/client.go +++ b/package/hot/client.go @@ -93,6 +93,8 @@ func (s *Stream) loop() { } func (s *Stream) Next(ctx context.Context) (*Event, error) { + ticker := time.NewTicker(time.Second) + defer ticker.Stop() for { select { case <-ctx.Done(): @@ -101,7 +103,7 @@ func (s *Stream) Next(ctx context.Context) (*Event, error) { return evt, nil case err := <-s.errorCh: return nil, err - case <-time.Tick(time.Second): + case <-ticker.C: s.log.Debug("hot: client waiting for next event") } } @@ -115,10 +117,10 @@ func (s *Stream) close() (err error) { err = errs.Join(err, s.res.Body.Close()) close(s.closeCh) // Drain event channel - if err := <-s.errorCh; err != nil { + if e := <-s.errorCh; e != nil { // Closed errors are expected since we closed the body - if !errors.Is(err, net.ErrClosed) { - err = errs.Join(err, err) + if !isExpectedCloseError(e) { + err = errs.Join(err, e) } } close(s.errorCh) @@ -126,6 +128,19 @@ func (s *Stream) close() (err error) { return err } +// isExpectedCloseError returns true if the close error is expected +func isExpectedCloseError(err error) bool { + if errors.Is(err, net.ErrClosed) { + return true + } + // Unfortunately this error is not exported + // https://github.com/golang/go/blob/f4274e64aac99aaa9af05988f2f8c36c47554889/src/net/http/transport.go#L2636 + if err.Error() == "http: read on closed response body" { + return true + } + return false +} + func parseLine(line []byte) (key []byte, value []byte) { key = line colon := bytes.IndexByte(line, ':') diff --git a/package/modcache/modcache.go b/package/modcache/modcache.go index 62389fce..a1c09377 100644 --- a/package/modcache/modcache.go +++ b/package/modcache/modcache.go @@ -1,27 +1,15 @@ package modcache import ( - "archive/zip" - "bytes" "errors" "fmt" "go/build" - "io" - "io/fs" - "io/ioutil" "os" "path/filepath" "strings" - "testing/fstest" - "time" - "golang.org/x/sync/errgroup" - - "golang.org/x/mod/modfile" "golang.org/x/mod/module" "golang.org/x/mod/semver" - "golang.org/x/mod/sumdb/dirhash" - modzip "golang.org/x/mod/zip" ) // Default loads a module cache from the default location @@ -43,28 +31,6 @@ func (c *Cache) Directory(subpaths ...string) string { return filepath.Join(append([]string{c.cacheDir}, subpaths...)...) } -// type Files = map[string]string -// type Modules = map[string]Files - -// Write modules directly into the cache directory in an acceptable format so -// that Go thinks these files are cached and doesn't try reading them from the -// network. -// -// This implementation is the minimal format needed to get `go mod tidy` to -// think the files are cached. This shouldn't be used outside of testing -// contexts. -// -// Based on: https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/fetch.go -// func WriteFS(modules Modules) (fs.FS, error) { -// mapfs := fstest.MapFS{} -// for pv, files := range modules { -// if err := writeModuleFS(mapfs, pv, files); err != nil { -// return nil, err -// } -// } -// return mapfs, nil -// } - // SplitPathVersion splits a path@version into path & version func SplitPathVersion(pathVersion string) (path, version string, err error) { parts := strings.SplitN(pathVersion, "@", 2) @@ -81,246 +47,6 @@ func SplitPathVersion(pathVersion string) (path, version string, err error) { return path, version, nil } -var zeroTime time.Time - -func writeModuleFS(mapfs fstest.MapFS, pv string, files map[string]string) error { - modulePath, moduleVersion, err := SplitPathVersion(pv) - if err != nil { - return err - } - goMod, ok := files["go.mod"] - if !ok { - goMod = `module ` + modulePath + "\n" - // Write go.mod back into module to make cached files a valid go.mod - files["go.mod"] = goMod - } - if modfile.ModulePath([]byte(goMod)) != modulePath { - return fmt.Errorf("modcache: %q does not match module path in go.mod", modulePath) - } - downloadDir, err := downloadDir(modulePath) - if err != nil { - return err - } - escapedVersion, err := module.EscapeVersion(moduleVersion) - if err != nil { - return err - } - extlessPath := filepath.Join(downloadDir, escapedVersion) - zipData := new(bytes.Buffer) - module := module.Version{Path: modulePath, Version: moduleVersion} - var zipFiles []modzip.File - for path, data := range files { - zipFiles = append(zipFiles, &zipEntry{path, data}) - } - if err := modzip.Create(zipData, module, zipFiles); err != nil { - return err - } - hash, err := hashZip(bytes.NewReader(zipData.Bytes()), int64(zipData.Len()), dirhash.DefaultHash) - if err != nil { - return err - } - // Write the zip hash - mapfs[extlessPath+".ziphash"] = &fstest.MapFile{ - Data: []byte(hash), - ModTime: zeroTime, - Mode: 0644, - } - // Write the info file - // We use zero-time because it doesn't seem to affect module download - // functionality and allows us to have consistent hashes for downloaded - // modules - infoFile := fmt.Sprintf(`{"Version":%q,"Time":%q}`, moduleVersion, zeroTime.Format(time.RFC3339)) - mapfs[extlessPath+".info"] = &fstest.MapFile{ - Data: []byte(infoFile), - ModTime: zeroTime, - Mode: 0644, - } - // Write the .mod - mapfs[extlessPath+".mod"] = &fstest.MapFile{ - Data: []byte(goMod), - ModTime: zeroTime, - Mode: 0644, - } - // Write the zip - mapfs[extlessPath+".zip"] = &fstest.MapFile{ - Data: zipData.Bytes(), - ModTime: zeroTime, - Mode: 0644, - } - // Write all the files - for path, data := range files { - mapfs[filepath.Join(pv, path)] = &fstest.MapFile{ - Data: []byte(data), - ModTime: zeroTime, - Mode: 0644, - } - } - return nil -} - -// HashZip returns the hash of the file content in the named zip file. -// Only the file names and their contents are included in the hash: -// the exact zip file format encoding, compression method, -// per-file modification times, and other metadata are ignored. -func hashZip(r io.ReaderAt, size int64, hash dirhash.Hash) (string, error) { - z, err := zip.NewReader(r, size) - if err != nil { - return "", err - } - var files []string - zfiles := make(map[string]*zip.File) - for _, file := range z.File { - files = append(files, file.Name) - zfiles[file.Name] = file - } - zipOpen := func(name string) (io.ReadCloser, error) { - f := zfiles[name] - if f == nil { - return nil, fmt.Errorf("file %q not found in zip", name) // should never happen - } - return f.Open() - } - return hash(files, zipOpen) -} - -// Write modules directly into the cache directory in an acceptable format so -// that Go thinks these files are cached and doesn't try reading them from the -// network. -// -// This implementation is the minimal format needed to get `go mod tidy` to -// think the files are cached. This shouldn't be used outside of testing -// contexts. -// -// Based on: https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/fetch.go -// func (c *Cache) Write(modules Modules) error { -// eg := new(errgroup.Group) -// for modulePathVersion, files := range modules { -// modulePathVersion, files := modulePathVersion, files -// eg.Go(func() error { return c.writeModule(modulePathVersion, files) }) -// } -// return eg.Wait() -// } - -func (c *Cache) writeModule(modulePathVersion string, files map[string]string) error { - moduleParts := strings.SplitN(modulePathVersion, "@", 2) - if len(moduleParts) != 2 { - return fmt.Errorf("modcache: invalid module key") - } - modulePath, moduleVersion := moduleParts[0], moduleParts[1] - goMod, ok := files["go.mod"] - if !ok { - goMod = `module ` + modulePath + "\n" - // Write go.mod back into module to make cached files a valid go.mod - files["go.mod"] = goMod - } - if modulePath == "" { - return fmt.Errorf("modcache: missing module path in go.mod") - } - if modfile.ModulePath([]byte(goMod)) != modulePath { - return fmt.Errorf("modcache: %q does not match module path in go.mod", modulePath) - } - downloadDir, err := c.downloadDir(modulePath) - if err != nil { - return err - } - if err := os.MkdirAll(downloadDir, 0755); err != nil { - return err - } - escapedVersion, err := module.EscapeVersion(moduleVersion) - if err != nil { - return err - } - extlessPath := filepath.Join(downloadDir, escapedVersion) - zipPath := extlessPath + ".zip" - zipFile, err := os.Create(zipPath) - if err != nil { - return err - } - defer zipFile.Close() - module := module.Version{Path: modulePath, Version: moduleVersion} - var zipFiles []modzip.File - for path, data := range files { - zipFiles = append(zipFiles, &zipEntry{path, data}) - } - if err := modzip.Create(zipFile, module, zipFiles); err != nil { - return err - } - if err := zipFile.Close(); err != nil { - return err - } - hash, err := dirhash.HashZip(zipPath, dirhash.DefaultHash) - if err != nil { - return err - } - if err := ioutil.WriteFile(extlessPath+".ziphash", []byte(hash), 0644); err != nil { - return err - } - if err := ioutil.WriteFile(extlessPath+".mod", []byte(goMod), 0644); err != nil { - return err - } - moduleDir, err := c.getModuleDirectory(modulePath, moduleVersion) - if err != nil { - return err - } - eg := new(errgroup.Group) - for path, data := range files { - path := filepath.Join(moduleDir, path) - dir := filepath.Dir(path) - data := data - eg.Go(func() error { - if err := os.MkdirAll(dir, 0755); err != nil { - return err - } - return ioutil.WriteFile(path, []byte(data), 0644) - }) - } - return eg.Wait() -} - -type zipEntry struct { - path, data string -} - -func (z *zipEntry) Path() string { - return z.path -} - -// Lstat returns information about the file. If the file is a symbolic link, -func (z *zipEntry) Lstat() (os.FileInfo, error) { - return &fileInfo{ - name: filepath.Base(z.path), - data: []byte(z.data), - size: int64(len(z.data)), - mode: fs.FileMode(0644), - modTime: zeroTime, - }, nil -} - -// A fileInfo implements fs.FileInfo and fs.DirEntry for a given map file. -type fileInfo struct { - name string - data []byte - size int64 - mode fs.FileMode - modTime time.Time - sys interface{} -} - -func (i *fileInfo) Name() string { return i.name } -func (i *fileInfo) Mode() fs.FileMode { return i.mode } -func (i *fileInfo) Type() fs.FileMode { return i.mode.Type() } -func (i *fileInfo) ModTime() time.Time { return i.modTime } -func (i *fileInfo) IsDir() bool { return i.mode&fs.ModeDir != 0 } -func (i *fileInfo) Sys() interface{} { return i.sys } -func (i *fileInfo) Info() (fs.FileInfo, error) { return i, nil } -func (i *fileInfo) Size() int64 { return i.size } - -// Open provides access to the data within a regular file. Open may return -// an error if called on a directory or symbolic link. -func (z *zipEntry) Open() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewBufferString(z.data)), nil -} - // ResolveDirectory returns the directory to which m should have been // downloaded. An error will be returned if the module path or version cannot be // escaped. An error satisfying errors.Is(err, os.ErrNotExist) will be returned @@ -341,35 +67,6 @@ func (c *Cache) ResolveDirectory(modulePath, version string) (string, error) { return dir, nil } -// InCache returns true if the module with this version is in the cache, -// otherwise it returns false. -// func (c *Cache) InCache(modulePath, version string) (bool, error) { -// if _, err := c.ResolveDirectory(modulePath, version); err != nil { -// if !errors.Is(err, fs.ErrNotExist) { -// return false, err -// } -// return false, nil -// } -// return true, nil -// } - -// Import from a directory -// func (c *Cache) Import(from string) error { -// return cp.Copy(from, c.cacheDir, cp.Options{}) -// } - -// // Export to a directory -// func (c *Cache) Export(to string) error { -// return cp.Copy(c.cacheDir, to, cp.Options{ -// Skip: func(src string) (bool, error) { -// if src == filepath.Join(c.cacheDir, "cache", "vcs") { -// return true, nil -// } -// return false, nil -// }, -// }) -// } - // Cache for faster subsequent requests var modDir string @@ -421,21 +118,3 @@ func (e *downloadDirPartialError) Error() string { return fmt.Sprintf("%s: %v", // Is fn func (e *downloadDirPartialError) Is(err error) bool { return err == os.ErrNotExist } - -// partialDownloadPath returns the partial download path -func (c *Cache) downloadDir(modulePath string) (string, error) { - dir, err := downloadDir(modulePath) - if err != nil { - return "", err - } - return filepath.Join(c.cacheDir, dir), nil -} - -// partialDownloadPath returns the partial download path -func downloadDir(modulePath string) (string, error) { - enc, err := module.EscapePath(modulePath) - if err != nil { - return "", err - } - return filepath.Join("cache/download", enc, "/@v"), nil -} diff --git a/package/remotefs/client.go b/package/remotefs/client.go index 4f34afa6..09441b43 100644 --- a/package/remotefs/client.go +++ b/package/remotefs/client.go @@ -5,7 +5,6 @@ import ( "encoding/gob" "io/fs" "strings" - "time" "github.com/keegancsmith/rpc" "github.com/livebud/bud/internal/virtual" @@ -18,9 +17,6 @@ func init() { gob.Register(&virtual.DirEntry{}) } -// client timeout defaults to 10 seconds -const clientTimeout = 10 * time.Second - func Dial(ctx context.Context, addr string) (*Client, error) { conn, err := socket.Dial(ctx, addr) if err != nil { diff --git a/package/router/lex/lex.go b/package/router/lex/lex.go index ffb31825..fcf4560f 100644 --- a/package/router/lex/lex.go +++ b/package/router/lex/lex.go @@ -81,11 +81,6 @@ func (l *lexer) backup() { l.pos -= l.width } -// ignore skips over the pending input before this point. -func (l *lexer) ignore() { - l.start = l.pos -} - func (l *lexer) errorf(format string, args ...interface{}) stateFn { value := fmt.Sprintf(format, args...) l.tokenCh <- Token{ @@ -221,16 +216,6 @@ func lexSlotRest(l *lexer) stateFn { } } -// The character after the slot can't be a character that could be in the slot -func lexAfterSlot(l *lexer) stateFn { - switch r := l.peek(); r { - case '.', '/', end: - return lexText - default: - return l.errorf(`route %q: invalid value after slot %q`, l.input, string(r)) - } -} - func lexQuestion(l *lexer) stateFn { // Expect End after switch r := l.step(); r { diff --git a/package/router/radix/tree_test.go b/package/router/radix/tree_test.go index 1cf4408a..5b220814 100644 --- a/package/router/radix/tree_test.go +++ b/package/router/radix/tree_test.go @@ -1,7 +1,7 @@ package radix_test import ( - "io/ioutil" + "io" "net/http" "net/http/httptest" "sort" @@ -67,7 +67,7 @@ func ok(t testing.TB, test *test) { rec := httptest.NewRecorder() match.Handler.ServeHTTP(rec, req) res := rec.Result() - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal(request.route, string(body)) // Test slots diff --git a/package/router/router_test.go b/package/router/router_test.go index 06f2cb27..5c8b823a 100644 --- a/package/router/router_test.go +++ b/package/router/router_test.go @@ -2,7 +2,7 @@ package router_test import ( "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "testing" @@ -82,7 +82,7 @@ func ok(t testing.TB, test *test) { fmt.Println("location", url.Path) is.Equal(request.location, url.Path) } - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal(request.body, string(body)) } @@ -205,7 +205,7 @@ func TestPut(t *testing.T) { router.ServeHTTP(rec, req) res := rec.Result() is.Equal(200, res.StatusCode) - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal("id=10", string(body)) } @@ -219,7 +219,7 @@ func TestAdd(t *testing.T) { router.ServeHTTP(rec, req) res := rec.Result() is.Equal(200, res.StatusCode) - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal("id=10", string(body)) } diff --git a/package/socket/socket_test.go b/package/socket/socket_test.go index 275f1dac..9787a640 100644 --- a/package/socket/socket_test.go +++ b/package/socket/socket_test.go @@ -4,7 +4,6 @@ import ( "context" "errors" "io" - "io/ioutil" "net/http" "os" "path/filepath" @@ -34,7 +33,7 @@ func TestLoadTCP(t *testing.T) { } res, err := client.Get("http://" + listener.Addr().String() + "/hello") is.NoErr(err) - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal(string(body), "/hello") server.Shutdown(context.Background()) @@ -58,7 +57,7 @@ func TestLoadNumberOnly(t *testing.T) { } res, err := client.Get("http://" + listener.Addr().String() + "/hello") is.NoErr(err) - body, err := ioutil.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) is.NoErr(err) is.Equal(string(body), "/hello") server.Shutdown(context.Background()) diff --git a/package/vfs/singleflight.go b/package/vfs/singleflight.go index 96e06a59..6fab742a 100644 --- a/package/vfs/singleflight.go +++ b/package/vfs/singleflight.go @@ -3,7 +3,6 @@ package vfs import ( "io" "io/fs" - "io/ioutil" "golang.org/x/sync/singleflight" ) @@ -42,7 +41,7 @@ func (s *singleFlight) Open(name string) (fs.File, error) { } return file, nil } - data, err := ioutil.ReadAll(file) + data, err := io.ReadAll(file) if err != nil { return nil, err } diff --git a/package/watcher/watcher.go b/package/watcher/watcher.go index ec4b5124..b618ab15 100644 --- a/package/watcher/watcher.go +++ b/package/watcher/watcher.go @@ -18,6 +18,9 @@ import ( "github.com/livebud/bud/internal/gitignore" ) +// Stop informs the watcher to stop. +// +//lint:ignore ST1012 stop is not an error, it's part of the control flow. var Stop = errors.New("stop watching") // Arbitrarily picked after some manual testing. OSX is pretty fast, but Ubuntu diff --git a/tools.go b/tools.go index 8487d311..8c71c0e4 100644 --- a/tools.go +++ b/tools.go @@ -9,5 +9,6 @@ import ( _ "github.com/evanw/esbuild/cmd/esbuild" _ "github.com/livebud/bud-test-plugin" _ "github.com/pointlander/peg" + _ "honnef.co/go/tools/cmd/staticcheck" _ "src.techknowlogick.com/xgo" )