diff --git a/.github/workflows/build-syncthing.yaml b/.github/workflows/build-syncthing.yaml index fa12b1572..3a4a21640 100644 --- a/.github/workflows/build-syncthing.yaml +++ b/.github/workflows/build-syncthing.yaml @@ -3,6 +3,9 @@ name: Build Syncthing on: pull_request: push: + branches-ignore: + - release + - release-rc* workflow_call: workflow_dispatch: diff --git a/cmd/syncthing/crash_reporting.go b/cmd/syncthing/crash_reporting.go index c71dc007c..279ae11f7 100644 --- a/cmd/syncthing/crash_reporting.go +++ b/cmd/syncthing/crash_reporting.go @@ -14,7 +14,7 @@ import ( "net/http" "os" "path/filepath" - "sort" + "slices" "strings" "time" ) @@ -37,7 +37,9 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) { return } - sort.Sort(sort.Reverse(sort.StringSlice(files))) + slices.SortFunc(files, func(a, b string) int { + return strings.Compare(b, a) + }) for _, file := range files { if strings.Contains(file, ".reported.") { // We've already sent this file. It'll be cleaned out at some diff --git a/lib/api/tokenmanager.go b/lib/api/tokenmanager.go index fdf9f01c9..ddf6b8d28 100644 --- a/lib/api/tokenmanager.go +++ b/lib/api/tokenmanager.go @@ -117,8 +117,8 @@ func (m *tokenManager) saveLocked() { for token, expiry := range m.tokens.Tokens { tokens = append(tokens, tokenExpiry{token, expiry}) } - slices.SortFunc(tokens, func(i, j tokenExpiry) int { - return int(i.expiry - j.expiry) + slices.SortFunc(tokens, func(a, b tokenExpiry) int { + return int(a.expiry - b.expiry) }) // Remove the oldest tokens. for _, token := range tokens[:len(tokens)-m.maxItems] { diff --git a/lib/config/config.go b/lib/config/config.go index faa454f17..08feb4aa1 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -18,7 +18,6 @@ import ( "os" "reflect" "slices" - "sort" "strconv" "strings" @@ -338,8 +337,8 @@ func (cfg *Configuration) prepareDeviceList() map[protocol.DeviceID]*DeviceConfi // - sorted by ID // Happen before preparting folders as that needs a correct device list. cfg.Devices = ensureNoDuplicateOrEmptyIDDevices(cfg.Devices) - sort.Slice(cfg.Devices, func(a, b int) bool { - return cfg.Devices[a].DeviceID.Compare(cfg.Devices[b].DeviceID) == -1 + slices.SortFunc(cfg.Devices, func(a, b DeviceConfiguration) int { + return a.DeviceID.Compare(b.DeviceID) }) // Build a list of available devices diff --git a/lib/config/deviceconfiguration.go b/lib/config/deviceconfiguration.go index d91722764..b12514175 100644 --- a/lib/config/deviceconfiguration.go +++ b/lib/config/deviceconfiguration.go @@ -8,7 +8,7 @@ package config import ( "fmt" - "sort" + "slices" "github.com/syncthing/syncthing/lib/protocol" ) @@ -100,8 +100,8 @@ func sortedObservedFolderSlice(input map[string]ObservedFolder) []ObservedFolder for _, folder := range input { output = append(output, folder) } - sort.Slice(output, func(i, j int) bool { - return output[i].Time.Before(output[j].Time) + slices.SortFunc(output, func(a, b ObservedFolder) int { + return a.Time.Compare(b.Time) }) return output } diff --git a/lib/config/folderconfiguration.go b/lib/config/folderconfiguration.go index 9d4876dde..1dee2085e 100644 --- a/lib/config/folderconfiguration.go +++ b/lib/config/folderconfiguration.go @@ -15,7 +15,7 @@ import ( "fmt" "path" "path/filepath" - "sort" + "slices" "strings" "time" @@ -288,8 +288,8 @@ func (f *FolderConfiguration) prepare(myID protocol.DeviceID, existingDevices ma f.Devices = ensureDevicePresent(f.Devices, myID) f.Devices = ensureNoUntrustedTrustingSharing(f, f.Devices, existingDevices) - sort.Slice(f.Devices, func(a, b int) bool { - return f.Devices[a].DeviceID.Compare(f.Devices[b].DeviceID) == -1 + slices.SortFunc(f.Devices, func(a, b FolderDeviceConfiguration) int { + return a.DeviceID.Compare(b.DeviceID) }) if f.RescanIntervalS > MaxRescanIntervalS { diff --git a/lib/model/blockpullreorderer.go b/lib/model/blockpullreorderer.go index a98a4ce7a..15d8fabc6 100644 --- a/lib/model/blockpullreorderer.go +++ b/lib/model/blockpullreorderer.go @@ -7,7 +7,7 @@ package model import ( - "sort" + "slices" "github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/protocol" @@ -52,8 +52,8 @@ type standardBlockPullReorderer struct { func newStandardBlockPullReorderer(id protocol.DeviceID, otherDevices []protocol.DeviceID) *standardBlockPullReorderer { allDevices := append(otherDevices, id) - sort.Slice(allDevices, func(i, j int) bool { - return allDevices[i].Compare(allDevices[j]) == -1 + slices.SortFunc(allDevices, func(a, b protocol.DeviceID) int { + return a.Compare(b) }) // Find our index myIndex := -1 diff --git a/lib/model/blockpullreorderer_test.go b/lib/model/blockpullreorderer_test.go index 049c16640..ac24fdeb0 100644 --- a/lib/model/blockpullreorderer_test.go +++ b/lib/model/blockpullreorderer_test.go @@ -8,7 +8,7 @@ package model import ( "reflect" - "sort" + "slices" "testing" "github.com/syncthing/syncthing/lib/protocol" @@ -65,8 +65,8 @@ func Test_inOrderBlockPullReorderer_Reorder(t *testing.T) { func Test_standardBlockPullReorderer_Reorder(t *testing.T) { // Order the devices, so we know their ordering ahead of time. devices := []protocol.DeviceID{myID, device1, device2} - sort.Slice(devices, func(i, j int) bool { - return devices[i].Compare(devices[j]) == -1 + slices.SortFunc(devices, func(a, b protocol.DeviceID) int { + return a.Compare(b) }) blocks := func(i ...int) []protocol.BlockInfo { diff --git a/lib/model/folder.go b/lib/model/folder.go index 0986dedba..d01795df3 100644 --- a/lib/model/folder.go +++ b/lib/model/folder.go @@ -13,7 +13,7 @@ import ( "math/rand" "path/filepath" "slices" - "sort" + "strings" "time" "github.com/syncthing/syncthing/internal/db" @@ -1226,7 +1226,9 @@ func (f *folder) Errors() []FileError { errors := make([]FileError, scanLen+len(f.pullErrors)) copy(errors[:scanLen], f.scanErrors) copy(errors[scanLen:], f.pullErrors) - sort.Sort(fileErrorList(errors)) + slices.SortFunc(errors, func(a, b FileError) int { + return strings.Compare(a.Path, b.Path) + }) return errors } diff --git a/lib/model/folder_recvenc.go b/lib/model/folder_recvenc.go index 69b1c5771..bc5e5b701 100644 --- a/lib/model/folder_recvenc.go +++ b/lib/model/folder_recvenc.go @@ -8,7 +8,8 @@ package model import ( "fmt" - "sort" + "slices" + "strings" "github.com/syncthing/syncthing/internal/itererr" "github.com/syncthing/syncthing/lib/config" @@ -104,7 +105,9 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string) { go f.pullScannerRoutine(scanChan) defer close(scanChan) - sort.Sort(sort.Reverse(sort.StringSlice(dirs))) + slices.SortFunc(dirs, func(a, b string) int { + return strings.Compare(b, a) + }) for _, dir := range dirs { if err := f.deleteDirOnDisk(dir, scanChan); err != nil { f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err)) diff --git a/lib/model/folder_recvonly.go b/lib/model/folder_recvonly.go index 535483c55..1ad611b6d 100644 --- a/lib/model/folder_recvonly.go +++ b/lib/model/folder_recvonly.go @@ -7,7 +7,8 @@ package model import ( - "sort" + "slices" + "strings" "time" "github.com/syncthing/syncthing/internal/itererr" @@ -208,7 +209,9 @@ func (q *deleteQueue) handle(fi protocol.FileInfo) (bool, error) { func (q *deleteQueue) flush() ([]string, error) { // Process directories from the leaves inward. - sort.Sort(sort.Reverse(sort.StringSlice(q.dirs))) + slices.SortFunc(q.dirs, func(a, b string) int { + return strings.Compare(b, a) + }) var firstError error var deleted []string diff --git a/lib/model/folder_sendrecv.go b/lib/model/folder_sendrecv.go index 70cd37838..d65b45096 100644 --- a/lib/model/folder_sendrecv.go +++ b/lib/model/folder_sendrecv.go @@ -14,7 +14,7 @@ import ( "fmt" "io" "path/filepath" - "sort" + "slices" "strconv" "strings" "time" @@ -1854,7 +1854,9 @@ func (f *sendReceiveFolder) moveForConflict(name, lastModBy string, scanChan cha if f.MaxConflicts > -1 { matches := existingConflicts(name, f.mtimefs) if len(matches) > f.MaxConflicts { - sort.Sort(sort.Reverse(sort.StringSlice(matches))) + slices.SortFunc(matches, func(a, b string) int { + return strings.Compare(b, a) + }) for _, match := range matches[f.MaxConflicts:] { if gerr := f.mtimefs.Remove(match); gerr != nil { l.Debugln(f, "removing extra conflict", gerr) @@ -2196,20 +2198,6 @@ type FileError struct { Err string `json:"error"` } -type fileErrorList []FileError - -func (l fileErrorList) Len() int { - return len(l) -} - -func (l fileErrorList) Less(a, b int) bool { - return l[a].Path < l[b].Path -} - -func (l fileErrorList) Swap(a, b int) { - l[a], l[b] = l[b], l[a] -} - func conflictName(name, lastModBy string) string { ext := filepath.Ext(name) return name[:len(name)-len(ext)] + time.Now().Format(".sync-conflict-20060102-150405-") + lastModBy + ext diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index 01485a34d..c644edaaa 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -16,7 +16,8 @@ import ( "os" "path/filepath" rdebug "runtime/debug" - "sort" + "slices" + "strings" "sync" "testing" @@ -145,7 +146,7 @@ func TestWalk(t *testing.T) { } tmp = append(tmp, f.File) } - sort.Sort(fileList(tmp)) + slices.SortFunc(fileList(tmp), compareByName) files := fileList(tmp).testfiles() if diff, equal := messagediff.PrettyDiff(testdata, files); !equal { @@ -584,23 +585,15 @@ func walkDir(fs fs.Filesystem, dir string, cfiler CurrentFiler, matcher *ignore. tmp = append(tmp, f.File) } } - sort.Sort(fileList(tmp)) + slices.SortFunc(fileList(tmp), compareByName) return tmp } type fileList []protocol.FileInfo -func (l fileList) Len() int { - return len(l) -} - -func (l fileList) Less(a, b int) bool { - return l[a].Name < l[b].Name -} - -func (l fileList) Swap(a, b int) { - l[a], l[b] = l[b], l[a] +func compareByName(a, b protocol.FileInfo) int { + return strings.Compare(a.Name, b.Name) } func (l fileList) testfiles() testfileList { @@ -825,7 +818,7 @@ func TestIssue4841(t *testing.T) { } files = append(files, f.File) } - sort.Sort(fileList(files)) + slices.SortFunc(fileList(files), compareByName) if len(files) != 1 { t.Fatalf("Expected 1 file, got %d: %v", len(files), files) diff --git a/lib/versioner/empty_dir_tracker.go b/lib/versioner/empty_dir_tracker.go index fc1603df2..963e5f0fd 100644 --- a/lib/versioner/empty_dir_tracker.go +++ b/lib/versioner/empty_dir_tracker.go @@ -8,7 +8,8 @@ package versioner import ( "path/filepath" - "sort" + "slices" + "strings" "github.com/syncthing/syncthing/lib/fs" ) @@ -37,7 +38,9 @@ func (t emptyDirTracker) emptyDirs() []string { for dir := range t { empty = append(empty, dir) } - sort.Sort(sort.Reverse(sort.StringSlice(empty))) + slices.SortFunc(empty, func(a, b string) int { + return strings.Compare(b, a) + }) return empty } diff --git a/relnotes/README.md b/relnotes/README.md index 66a4991c6..99d75634d 100644 --- a/relnotes/README.md +++ b/relnotes/README.md @@ -16,3 +16,6 @@ example: The release notes will also be included in candidate releases (e.g. v1.2.3-rc.1). + +Additional notes will also be loaded from `v1.2.md` and `v1.md`, if they +exist. diff --git a/script/authors.go b/script/authors.go index febe22606..cc2861c0f 100644 --- a/script/authors.go +++ b/script/authors.go @@ -99,6 +99,11 @@ func main() { // Write AUTHORS file + // Sort by author name + slices.SortFunc(authors, func(a, b author) int { + return strings.Compare(strings.ToLower(a.name), strings.ToLower(b.name)) + }) + out, err := os.Create("AUTHORS") if err != nil { log.Fatal(err) diff --git a/script/relnotes.go b/script/relnotes.go index ffc27c22f..b88c2f7fc 100644 --- a/script/relnotes.go +++ b/script/relnotes.go @@ -43,29 +43,38 @@ func main() { log.Fatalln("Must set $GITHUB_TOKEN") } - addl, err := additionalNotes(*ver) + notes, err := additionalNotes(*ver) if err != nil { log.Fatalln("Gathering additional notes:", err) } - notes, err := generatedNotes(*ver, *branch, *prevVer) + gh, err := generatedNotes(*ver, *branch, *prevVer) if err != nil { log.Fatalln("Gathering github notes:", err) } + notes = append(notes, gh) - if addl != "" { - fmt.Println(addl) - } - fmt.Println(notes) + fmt.Println(strings.Join(notes, "\n\n")) } // Load potential additional release notes from within the repo -func additionalNotes(newVer string) (string, error) { +func additionalNotes(newVer string) ([]string, error) { + var notes []string ver, _, _ := strings.Cut(newVer, "-") - bs, err := os.ReadFile(fmt.Sprintf("relnotes/%s.md", ver)) - if os.IsNotExist(err) { - return "", nil + for { + file := fmt.Sprintf("relnotes/%s.md", ver) + if bs, err := os.ReadFile(file); err == nil { + notes = append(notes, strings.TrimSpace(string(bs))) + } else if !os.IsNotExist(err) { + return nil, err + } + + if idx := strings.LastIndex(ver, "."); idx > 0 { + ver = ver[:idx] + } else { + break + } } - return string(bs), err + return notes, nil } // Load generated release notes (list of pull requests and contributors) @@ -105,5 +114,5 @@ func generatedNotes(newVer, targetCommit, prevVer string) (string, error) { if err := json.NewDecoder(res.Body).Decode(&resJSON); err != nil { return "", err } - return resJSON.Body, nil + return strings.TrimSpace(resJSON.Body), nil } diff --git a/test/util.go b/test/util.go index c0aab73fd..81548bdb1 100644 --- a/test/util.go +++ b/test/util.go @@ -20,7 +20,7 @@ import ( "os" "path/filepath" "runtime" - "sort" + "slices" "strings" "testing" "time" @@ -375,7 +375,9 @@ func mergeDirectoryContents(c ...[]fileInfo) []fileInfo { i++ } - sort.Sort(fileInfoList(res)) + slices.SortFunc(res, func(a, b fileInfo) int { + return strings.Compare(a.name, b.name) + }) return res } @@ -404,20 +406,6 @@ func (f fileInfo) String() string { return fmt.Sprintf("%s %04o %d %x", f.name, f.mode, f.mod, f.hash) } -type fileInfoList []fileInfo - -func (l fileInfoList) Len() int { - return len(l) -} - -func (l fileInfoList) Less(a, b int) bool { - return l[a].name < l[b].name -} - -func (l fileInfoList) Swap(a, b int) { - l[a], l[b] = l[b], l[a] -} - func startWalker(dir string, res chan<- fileInfo, abort <-chan struct{}) chan error { walker := func(path string, info os.FileInfo, err error) error { if err != nil {