240718.1 · d6ebdd1cdd8e059d8c5ebfe409a7f36345a86e44
fix go 1.20 build and retract v1.0.7
This commit fixes the build for Go 1.20 by removing use of the slices package and retracts the v1.0.7 release.
It also changes the CI to test go1.20 (our minimum supported version) so that this will not happen again in the future.
reading declarations
@@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix:- go: [1.21, 1.22]+ go: ['1.20', '1.21', '1.22'] steps: - uses: actions/checkout@v4 with:
@@ -47,7 +47,9 @@ test_build_solaris_amd64: .PHONY: test_build_wasip1_wasm test_build_wasip1_wasm:- GOOS=wasip1 GOARCH=wasm go test -c -o /dev/null+ @# Ignore versions before 1.21+ go version | grep -qE 'go1\.(20|1[0-9])' || \+ GOOS=wasip1 GOARCH=wasm go test -c -o /dev/null .PHONY: test_build_aix_ppc64 test_build_aix_ppc64:
@@ -8,9 +8,10 @@ package fastwalk import ( "io/fs" "os"- "slices"- "strings"+ "sort" "sync"++ "github.com/charlievieth/fastwalk/internal/fmtdirent" ) var _ DirEntry = (*portableDirent)(nil)@@ -22,7 +23,7 @@ type portableDirent struct { } func (d *portableDirent) String() string {- return fs.FormatDirEntry(d)+ return fmtdirent.FormatDirEntry(d) } func (d *portableDirent) Stat() (fs.FileInfo, error) {@@ -72,52 +73,56 @@ func sortDirents(mode SortMode, dents []DirEntry) { } switch mode { case SortLexical:- slices.SortFunc(dents, func(d1, d2 DirEntry) int {- return strings.Compare(d1.Name(), d2.Name())+ sort.Slice(dents, func(i, j int) bool {+ return dents[i].Name() < dents[j].Name() }) case SortFilesFirst:- slices.SortFunc(dents, func(d1, d2 DirEntry) int {+ sort.Slice(dents, func(i, j int) bool {+ d1 := dents[i]+ d2 := dents[j] r1 := d1.Type().IsRegular() r2 := d2.Type().IsRegular() switch { case r1 && !r2:- return -1+ return true case !r1 && r2:- return 1+ return false case !r1 && !r2: // Both are not regular files: sort directories last dd1 := d1.Type().IsDir() dd2 := d2.Type().IsDir() switch { case !dd1 && dd2:- return -1+ return true case dd1 && !dd2:- return 1+ return false } }- return strings.Compare(d1.Name(), d2.Name())+ return d1.Name() < d2.Name() }) case SortDirsFirst:- slices.SortFunc(dents, func(d1, d2 DirEntry) int {+ sort.Slice(dents, func(i, j int) bool {+ d1 := dents[i]+ d2 := dents[j] dd1 := d1.Type().IsDir() dd2 := d2.Type().IsDir() switch { case dd1 && !dd2:- return -1+ return true case !dd1 && dd2:- return 1+ return false case !dd1 && !dd2: // Both are not directories: sort regular files first r1 := d1.Type().IsRegular() r2 := d2.Type().IsRegular() switch { case r1 && !r2:- return -1+ return true case !r1 && r2:- return 1+ return false } }- return strings.Compare(d1.Name(), d2.Name())+ return d1.Name() < d2.Name() }) } }
@@ -8,6 +8,8 @@ import ( "reflect" "testing" "time"++ "github.com/charlievieth/fastwalk/internal/fmtdirent" ) var _ DirEntry = dirEntry{}@@ -25,7 +27,7 @@ func (de dirEntry) Info() (fs.FileInfo, error) { panic("not implemented") } func (de dirEntry) Stat() (fs.FileInfo, error) { panic("not implemented") } func (de dirEntry) String() string {- return fs.FormatDirEntry(de)+ return fmtdirent.FormatDirEntry(de) } // NB: this must be kept in sync with the
@@ -5,9 +5,10 @@ package fastwalk import ( "io/fs" "os"- "slices"- "strings"+ "sort" "sync"++ "github.com/charlievieth/fastwalk/internal/fmtdirent" ) type unixDirent struct {@@ -21,7 +22,7 @@ type unixDirent struct { func (d *unixDirent) Name() string { return d.name } func (d *unixDirent) IsDir() bool { return d.typ.IsDir() } func (d *unixDirent) Type() fs.FileMode { return d.typ }-func (d *unixDirent) String() string { return fs.FormatDirEntry(d) }+func (d *unixDirent) String() string { return fmtdirent.FormatDirEntry(d) } func (d *unixDirent) Info() (fs.FileInfo, error) { info := loadFileInfo(&d.info)@@ -87,52 +88,56 @@ func sortDirents(mode SortMode, dents []*unixDirent) { } switch mode { case SortLexical:- slices.SortFunc(dents, func(d1, d2 *unixDirent) int {- return strings.Compare(d1.name, d2.name)+ sort.Slice(dents, func(i, j int) bool {+ return dents[i].name < dents[j].name }) case SortFilesFirst:- slices.SortFunc(dents, func(d1, d2 *unixDirent) int {+ sort.Slice(dents, func(i, j int) bool {+ d1 := dents[i]+ d2 := dents[j] r1 := d1.typ.IsRegular() r2 := d2.typ.IsRegular() switch { case r1 && !r2:- return -1+ return true case !r1 && r2:- return 1+ return false case !r1 && !r2: // Both are not regular files: sort directories last dd1 := d1.typ.IsDir() dd2 := d2.typ.IsDir() switch { case !dd1 && dd2:- return -1+ return true case dd1 && !dd2:- return 1+ return false } }- return strings.Compare(d1.name, d2.name)+ return d1.name < d2.name }) case SortDirsFirst:- slices.SortFunc(dents, func(d1, d2 *unixDirent) int {+ sort.Slice(dents, func(i, j int) bool {+ d1 := dents[i]+ d2 := dents[j] dd1 := d1.typ.IsDir() dd2 := d2.typ.IsDir() switch { case dd1 && !dd2:- return -1+ return true case !dd1 && dd2:- return 1+ return false case !dd1 && !dd2: // Both are not directories: sort regular files first r1 := d1.typ.IsRegular() r2 := d2.typ.IsRegular() switch { case r1 && !r2:- return -1+ return true case !r1 && r2:- return 1+ return false } }- return strings.Compare(d1.name, d2.name)+ return d1.name < d2.name }) } }
@@ -1,3 +1,5 @@ module github.com/charlievieth/fastwalk go 1.20++retract v1.0.7 // Build broken on Go 1.20
internal/fmtdirent/fmtdirent_go120.go
@@ -0,0 +1,31 @@+//go:build !go1.21++package fmtdirent++import "io/fs"++// Backport fs.FormatDirEntry from go1.21++// FormatDirEntry returns a formatted version of dir for human readability.+// Implementations of [DirEntry] can call this from a String method.+// The outputs for a directory named subdir and a file named hello.go are:+//+// d subdir/+// - hello.go+func FormatDirEntry(dir fs.DirEntry) string {+ name := dir.Name()+ b := make([]byte, 0, 5+len(name))++ // The Type method does not return any permission bits,+ // so strip them from the string.+ mode := dir.Type().String()+ mode = mode[:len(mode)-9]++ b = append(b, mode...)+ b = append(b, ' ')+ b = append(b, name...)+ if dir.IsDir() {+ b = append(b, '/')+ }+ return string(b)+}
internal/fmtdirent/fmtdirent_go120_test.go
@@ -0,0 +1,118 @@+//go:build !go1.21++// Copyright 2023 The Go Authors. All rights reserved.+// Use of this source code is governed by a BSD-style+// license that can be found in the LICENSE file.++// Backport fs.FormatDirEntry tests from go1.21. We don't test+// the go1.21+ FormatDirEntry function since it just calls the+// stdlib and we don't want changes in its output to break our+// tests.++package fmtdirent_test++import (+ . "io/fs"+ "testing"+ "time"++ "github.com/charlievieth/fastwalk/internal/fmtdirent"+)++// formatTest implements FileInfo to test FormatFileInfo,+// and implements DirEntry to test FormatDirEntry.+type formatTest struct {+ name string+ size int64+ mode FileMode+ modTime time.Time+ isDir bool+}++func (fs *formatTest) Name() string {+ return fs.name+}++func (fs *formatTest) Size() int64 {+ return fs.size+}++func (fs *formatTest) Mode() FileMode {+ return fs.mode+}++func (fs *formatTest) ModTime() time.Time {+ return fs.modTime+}++func (fs *formatTest) IsDir() bool {+ return fs.isDir+}++func (fs *formatTest) Sys() any {+ return nil+}++func (fs *formatTest) Type() FileMode {+ return fs.mode.Type()+}++func (fs *formatTest) Info() (FileInfo, error) {+ return fs, nil+}++var formatTests = []struct {+ input formatTest+ wantDirEntry string+}{+ {+ formatTest{+ name: "hello.go",+ size: 100,+ mode: 0o644,+ modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),+ isDir: false,+ },+ "- hello.go",+ },+ {+ formatTest{+ name: "home/gopher",+ size: 0,+ mode: ModeDir | 0o755,+ modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),+ isDir: true,+ },+ "d home/gopher/",+ },+ {+ formatTest{+ name: "big",+ size: 0x7fffffffffffffff,+ mode: ModeIrregular | 0o644,+ modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),+ isDir: false,+ },+ "? big",+ },+ {+ formatTest{+ name: "small",+ size: -0x8000000000000000,+ mode: ModeSocket | ModeSetuid | 0o644,+ modTime: time.Date(1970, time.January, 1, 12, 0, 0, 0, time.UTC),+ isDir: false,+ },+ "S small",+ },+}++func TestFormatDirEntry(t *testing.T) {+ for i, test := range formatTests {+ got := fmtdirent.FormatDirEntry(&test.input)+ if got != test.wantDirEntry {+ t.Errorf("%d: FormatDirEntry(%#v) = %q, want %q", i, test.input, got, test.wantDirEntry)+ }+ }++}
internal/fmtdirent/fmtdirent_go121.go
@@ -0,0 +1,15 @@+//go:build go1.21++package fmtdirent++import "io/fs"++// FormatDirEntry returns a formatted version of dir for human readability.+// Implementations of [DirEntry] can call this from a String method.+// The outputs for a directory named subdir and a file named hello.go are:+//+// d subdir/+// - hello.go+func FormatDirEntry(dir fs.DirEntry) string {+ return fs.FormatDirEntry(dir)+}