Compare commits

...

26 Commits

Author SHA1 Message Date
Jakob Borg
40660c5fb7 build: add labeler workflow for PRs (#10143)
Use labels to categorise release notes
2025-05-29 10:04:08 +02:00
Jakob Borg
d940d094a1 build(deps): update our notify package from upstream (#10142) 2025-05-28 15:04:24 +00:00
Jakob Borg
9d67727989 build(deps): update dependencies (#10141) 2025-05-28 13:52:08 +00:00
Jakob Borg
6f51700a7f docs: general notes about v2 coming (#10135)
This adds a file that will be prepended to release notes (tag messages,
GitHub releases, forum posts) for v1 releases. I'd like there to be
something there to flag that things are going to change.
2025-05-27 10:01:04 +02:00
Marcel Meyer
598915193a refactor: use slices package for sorting (#10136)
Few more complicated usages of the sort packages are left.

### Purpose

Make progress towards replacing the sort package with slices package.
2025-05-26 20:37:49 +02:00
Jakob Borg
905e5ec07f build: handle multiple general release notes 2025-05-26 16:27:23 +02:00
Jakob Borg
4075b886d0 build: no need to build on the branches that just trigger tags 2025-05-26 15:21:21 +02:00
Jakob Borg
cade790198 build: use specific token for pushing release tags 2025-05-26 14:13:02 +02:00
Luke Hamburg
98555a9a80 fix(gui): update uncamel() to handle strings like 'IDs' (fixes #10128) (#10131)
> ⚠️ resubmission targeting `main` instead of `v2`

### Purpose

Updates `uncamel()` function in
[uncamelFilter.js](https://github.com/syncthing/syncthing/blob/v2/gui/default/syncthing/core/uncamelFilter.js)
to fix camelCase conversion edge cases, see #10128

This adds an array called `reservedStrings` which will be printed as-is,
e.g. `IDs`, `LAN` etc. I pre-populated this with what I believe makes
sense, but of course this is easily updated.

### Testing

I compiled all the config variables I could find in
`syncthing/lib/config/*configuration.go` and tested this new function
against them. Everything seemed to pass.

### Screenshot


![Image](https://github.com/user-attachments/assets/af8c9821-58b3-4a6a-8462-bead8a6d845a)
2025-05-26 11:43:38 +00:00
Marcel Meyer
48b757cac1 refactor: use slices package for sort (#10132)
The sort package is still used in places that were not trivial to
change. Since Go 1.21 slices package can be uswed for sort. See
https://go.dev/doc/go1.21#slices

### Purpose

Make some progress with the migration to a more up-to-date syntax.
2025-05-26 13:37:26 +02:00
Jakob Borg
58c85fc9db build: process for automatic release tags (#10133)
Make the release tagging consistent. Push to release branch to create a
stable release; push to release-rc to release a new candidate.
2025-05-26 13:33:53 +02:00
Syncthing Release Automation
ddd98a818a chore(gui, man, authors): update docs, translations, and contributors 2025-05-26 03:55:20 +00:00
Jakob Borg
64b5a1b738 fix(syncthing): ensure both config and data dirs exist at startup (fixes #10126) (#10127)
Previously we'd only ensure the config dir, which is often but not
always the same as the data dir.

Fixes #10126
2025-05-25 08:10:17 +02:00
Ashish Bhate
1a131a56f2 fix(versioner): fix perms of created folders (fixes #9626) (#10105)
As suggested in the linked issue, I've updated the versioner code to use
the permissions of the corresponding directory in the synced folder,
when creating the folder in the versions directory

### Testing
- Some tests are included with the PR. Happy to add more if you think
there are some edge-cases that we're missing.
- I've tested manually on linux to confirm the permissions of the
created directories.
- I haven't tested on Windows or OSX (I don't have access to these OS)
2025-05-24 07:35:32 +02:00
pullmerge
beda37f28b refactor: use slices.Contains to simplify code (#10121)
There is a [new function](https://pkg.go.dev/slices@go1.21.0#Contains)
added in the go1.21 standard library, which can make the code more
concise and easy to read.
2025-05-23 10:36:06 +00:00
Jakob Borg
2532ac35cf build(deps): update dependency due to build breakage (#10120) 2025-05-21 06:52:29 +00:00
Jakob Borg
bcd30ceaec chore: move golangci-lint & meta to separate PR-only workflow (#10119)
For now. Existing code is not golangci-lint clean, but new PRs should
be, ideally.
2025-05-21 08:32:49 +02:00
Jakob Borg
9a3493c2f4 build: reactivate golangci-lint (#10118)
With DeepSource becoming (imho) less and less useful, let's get this one
back on track. It will likely require adjusting over time.
2025-05-20 14:03:43 +02:00
André Colomb
fa404d5a0d chore(gui): add Serbian (sr) translation template (#10116)
Based on user request from Weblate, user `@vlazic`.
2025-05-19 21:06:38 +00:00
Syncthing Release Automation
73ad18fbfb chore(gui, man, authors): update docs, translations, and contributors 2025-05-19 03:56:31 +00:00
Syncthing Release Automation
1dd264894a chore(gui, man, authors): update docs, translations, and contributors 2025-05-12 03:54:02 +00:00
Marcus B Spencer
8c3d2f3bc5 fix(config): mark audit log options as needing restart (fixes #10099) (#10100)
### Testing

Change the `auditEnabled` option and you should get a prompt in the Web
GUI.
Restart and change the `auditFile` option, and you should get that same
prompt.

The prompt you should get is shown in the screenshots below.

### Screenshots


![Screenshot_20250507_122546](https://github.com/user-attachments/assets/23ce7c42-5e60-4f88-ac58-f312a9a1f5cc)

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-05-09 10:49:11 +00:00
Hazem Krimi
702ed8ecc1 fix(config): deep copy configuration defaults (fixes #9916) (#10101)
### Purpose

Setting default configuration was not working properly since the
defaults struct is not deeply copied.

### Testing

Try running commands to change default configuration and either inspect
`config.xml` or `/rest/config` result to see the applied changed.
Example:
```
./syncthing cli config defaults folder versioning params set keep 5
```
2025-05-09 07:40:32 +02:00
Syncthing Release Automation
b038650810 chore(gui, man, authors): update docs, translations, and contributors 2025-05-05 03:52:42 +00:00
Hazem Krimi
a16bf555c0 feat(gui): close a modal when pressing ESC after switching modal tabs (fixes #9489) (#10092)
### Purpose

As stated in #9489 after clicking on a tab link to switch tabs in a
modal you can no longer close the modal through clicking the ESC key
unless you click anywhere on the modal to focus on it again.

### Testing

- Click on a modal that has tabs like "Settings" or "Add Folder" and
switch tabs then click on ESC.
- Check if clicking outside of a modal in the backdrop should still
close the modal.

### Demo


https://github.com/user-attachments/assets/a010db9a-72f7-4160-a7db-ddfebffb4834
2025-05-02 14:53:54 +00:00
Jakob Borg
cd6ea60fa1 build(deps): update dependencies (#10091)
Without bumping Go version
2025-05-01 18:15:37 +00:00
86 changed files with 988 additions and 689 deletions

23
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
version: 1
labels:
- label: enhancement
title: ^feat\b
- label: bug
title: ^fix\b
- label: documentation
title: ^docs\b
- label: chore
title: ^chore\b
- label: chore
title: ^refactor\b
- label: build
title: ^build\b
- label: dependencies
title: ^build\(deps\)\b

17
.github/release.yml vendored Normal file
View File

@@ -0,0 +1,17 @@
changelog:
exclude:
labels:
- dependencies
categories:
- title: Fixes
labels:
- bug
- title: Features
labels:
- enhancement
- title: Other
labels:
- '*'

View File

@@ -3,6 +3,9 @@ name: Build Syncthing
on:
pull_request:
push:
branches-ignore:
- release
- release-rc*
workflow_call:
workflow_dispatch:
@@ -86,26 +89,6 @@ jobs:
LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }}
LOKI_LABELS: "go=${{ matrix.go }},runner=${{ matrix.runner }},repo=${{ github.repository }},ref=${{ github.ref }}"
#
# Meta checks for formatting, copyright, etc
#
correctness:
name: Check correctness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
cache: false
check-latest: true
- name: Check correctness
run: |
go test -v ./meta
#
# The basic checks job is a virtual one that depends on the matrix tests,
# the correctness checks, and various builds that we always do. This makes
@@ -120,7 +103,6 @@ jobs:
runs-on: ubuntu-latest
needs:
- build-test
- correctness
- package-linux
- package-cross
- package-source
@@ -194,7 +176,7 @@ jobs:
codesign-windows:
name: Codesign for Windows
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
runs-on: windows-latest
needs:
@@ -301,7 +283,7 @@ jobs:
package-macos:
name: Package for macOS
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
runs-on: macos-latest
steps:
@@ -401,7 +383,7 @@ jobs:
notarize-macos:
name: Notarize for macOS
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
needs:
- package-macos
@@ -545,7 +527,7 @@ jobs:
sign-for-upgrade:
name: Sign for upgrade
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
needs:
- codesign-windows
@@ -744,6 +726,8 @@ jobs:
name: Publish release files
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/tags/v'))
environment: release
permissions:
contents: write
needs:
- sign-for-upgrade
- package-debian
@@ -803,13 +787,35 @@ jobs:
with:
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
- name: Create GitHub release and push binaries
run: |
maybePrerelease=""
if [[ $VERSION == *-* ]]; then
maybePrerelease="--prerelease"
fi
export GH_PROMPT_DISABLED=1
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
gh release create \
"$VERSION" \
$maybePrerelease \
--title "$VERSION" \
--notes-from-tag
fi
gh release upload "$VERSION" \
packages/*.asc packages/*.json \
packages/syncthing-*.tar.gz \
packages/syncthing-*.zip \
packages/syncthing*.deb
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
#
# Push Debian/APT archive
#
publish-apt:
name: Publish APT
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release
needs:
- package-debian
@@ -888,7 +894,7 @@ jobs:
docker-syncthing:
name: Build and push Docker images
runs-on: ubuntu-latest
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/heads/release-') || startsWith(github.ref, 'refs/tags/v'))
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release-nightly' || github.ref == 'refs/heads/infrastructure' || startsWith(github.ref, 'refs/tags/v'))
environment: docker
permissions:
contents: read

49
.github/workflows/pr-linters.yaml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Run PR linters
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
pull-requests: read
jobs:
#
# golangci-lint runs a suite of static analysis checks on the code
#
golangci:
runs-on: ubuntu-latest
name: Golangci-lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- name: ensure asset generation
run: go run build.go assets
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
only-new-issues: true
#
# Meta checks for formatting, copyright, etc
#
meta:
name: Meta checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- run: |
go run build.go assets
go test -v ./meta

30
.github/workflows/pr-metadata.yaml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: PR metadata
on:
pull_request_target:
types:
- opened
- reopened
- edited
- synchronize
schedule:
- cron: "42 7 * * *"
permissions:
contents: read
issues: write
pull-requests: write
jobs:
#
# Set labels on PRs, which are then used to categorise release notes
#
labels:
name: Set labels
runs-on: ubuntu-latest
steps:
- uses: srvaroa/labeler@v1
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -0,0 +1,57 @@
name: Release Syncthing
on:
push:
branches:
- release
- release-rc*
permissions:
contents: write
jobs:
create-release-tag:
name: Create release tag
runs-on: ubuntu-latest
environment: release
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.ref }} # https://github.com/actions/checkout/issues/882
token: ${{ secrets.STRELEASE_GITHUB_TOKEN }}
- uses: actions/setup-go@v5
with:
go-version: stable
- name: Get svu
run: |
go install github.com/caarlos0/svu@latest
- name: Determine version to release
run: |
if [[ "$GITHUB_REF_NAME" == "release" ]] ; then
next=$(svu next)
else
next=$(svu prerelease --pre-release rc)
fi
echo "NEXT=$next" >> $GITHUB_ENV
echo "Next version is $next"
prev=$(git describe --exclude "*-*" --abbrev=0)
echo "PREV=$prev" >> $GITHUB_ENV
echo "Previous version is $prev"
- name: Determine release notes
run: |
go run ./script/relnotes.go --new-ver "$NEXT" --branch "$GITHUB_REF_NAME" --prev-ver "$PREV" > notes.md
env:
GITHUB_TOKEN: ${{ secrets.STRELEASE_GITHUB_TOKEN }}
- name: Create and push tag
run: |
git config --global user.name 'Syncthing Release Automation'
git config --global user.email 'release@syncthing.net'
git tag -a -F notes.md --cleanup=whitespace "$NEXT"
git push origin "$NEXT"

View File

@@ -1,39 +1,67 @@
version: "2"
linters:
enable-all: true
default: all
disable:
- cyclop
- depguard
- exhaustive
- exhaustruct
- forbidigo
- funlen
- gci
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocyclo
- godox
- gofmt
- goimports
- gomoddirectives
- inamedparam
- interfacebloat
- ireturn
- lll
- maintidx
- mnd
- musttag
- nestif
- nlreturn
- nonamedreturns
- paralleltest
- prealloc
- predeclared
- protogetter
- scopelint
- recvcheck
- revive
- tagalign
- tagliatelle
- testpackage
- usetesting # go 1.24
- varnamelen
- whitespace
- wrapcheck
- wsl
issues:
exclude-dirs:
- internal/gen
- cmd/dev
- repos
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- internal/gen
- cmd/dev
- repos
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofumpt
exclusions:
generated: lax
paths:
- internal/gen
- cmd/dev
- repos
- third_party$
- builtin$
- examples$

View File

@@ -48,6 +48,7 @@ Arkadiusz Tymiński <gevleeog@gmail.com>
Aroun <login@b-vo.fr>
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
Artur Zubilewicz <AkaZecik@users.noreply.github.com>
Ashish Bhate <bhate.ashish@gmail.com>
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks>
Aurélien Rainone <476650+arl@users.noreply.github.com>
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
@@ -113,6 +114,7 @@ diemade <spamkill@posteo.ch>
digital <didev@dinid.net>
Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com>
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
domain <32405309+szu17dmy@users.noreply.github.com>
Domenic Horner <domenic@tgxn.net>
Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
@@ -147,6 +149,7 @@ Gusted <postmaster@gusted.xyz> <williamzijl7@hotmail.com>
Han Boetes <han@boetes.org>
HansK-p <42314815+HansK-p@users.noreply.github.com>
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
Hazem Krimi <me@hazemkrimi.tech>
Heiko Zuerker (Smiley73) <heiko@zuerker.org>
Hireworks <129852174+hireworksltd@users.noreply.github.com>
Hugo Locurcio <hugo.locurcio@hugo.pro>
@@ -293,6 +296,7 @@ Pier Paolo Ramon <ramonpierre@gmail.com>
Piotr Bejda (piobpl) <piotrb10@gmail.com>
polyfloyd <polyfloyd@users.noreply.github.com>
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@users.noreply.github.com>
pullmerge <166967364+pullmerge@users.noreply.github.com>
Quentin Hibon <qh.public@yahoo.com>
Rahmi Pruitt <rjpruitt16@gmail.com>
red_led <red-led@users.noreply.github.com>

View File

@@ -8,6 +8,7 @@ package main
import (
"bytes"
"cmp"
"compress/gzip"
"context"
"io"
@@ -15,7 +16,7 @@ import (
"math"
"os"
"path/filepath"
"sort"
"slices"
"time"
)
@@ -177,8 +178,8 @@ func (d *diskStore) inventory() error {
})
return nil
})
sort.Slice(d.currentFiles, func(i, j int) bool {
return d.currentFiles[i].mtime < d.currentFiles[j].mtime
slices.SortFunc(d.currentFiles, func(a, b currentFile) int {
return cmp.Compare(a.mtime, b.mtime)
})
var oldest time.Duration
if len(d.currentFiles) > 0 {

View File

@@ -7,9 +7,10 @@
package cli
import (
"cmp"
"encoding/binary"
"fmt"
"sort"
"slices"
"github.com/syncthing/syncthing/lib/db"
)
@@ -77,8 +78,8 @@ func indexDumpSize() error {
elems = append(elems, ele)
}
sort.Slice(elems, func(i, j int) bool {
return elems[i].size > elems[j].size
slices.SortFunc(elems, func(a, b sizedElement) int {
return cmp.Compare(b.size, a.size)
})
for _, ele := range elems {
fmt.Println(ele.key, ele.size)

View File

@@ -8,10 +8,11 @@ package cli
import (
"bytes"
"cmp"
"encoding/binary"
"errors"
"fmt"
"sort"
"slices"
"google.golang.org/protobuf/proto"
@@ -207,11 +208,11 @@ func indexCheck() (err error) {
// Aggregate the ranges of missing sequence entries, print them
sort.Slice(missingSeq, func(a, b int) bool {
if missingSeq[a].folder != missingSeq[b].folder {
return missingSeq[a].folder < missingSeq[b].folder
slices.SortFunc(missingSeq, func(a, b sequenceKey) int {
if a.folder != b.folder {
return cmp.Compare(a.folder, b.folder)
}
return missingSeq[a].sequence < missingSeq[b].sequence
return cmp.Compare(a.sequence, b.sequence)
})
var folder uint32

View File

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

View File

@@ -23,7 +23,7 @@ import (
"path/filepath"
"regexp"
"runtime/pprof"
"sort"
"slices"
"strconv"
"strings"
"syscall"
@@ -349,10 +349,12 @@ func (options serveOptions) Run() error {
return nil
}
// Ensure that our home directory exists.
if err := syncthing.EnsureDir(locations.GetBaseDir(locations.ConfigBaseDir), 0o700); err != nil {
l.Warnln("Failure on home directory:", err)
os.Exit(svcutil.ExitError.AsInt())
// Ensure that our config and data directories exist.
for _, loc := range []locations.BaseDirEnum{locations.ConfigBaseDir, locations.DataBaseDir} {
if err := syncthing.EnsureDir(locations.GetBaseDir(loc), 0o700); err != nil {
l.Warnln("Failed to ensure directory exists:", err)
os.Exit(svcutil.ExitError.AsInt())
}
}
if options.UpgradeTo != "" {
@@ -441,7 +443,7 @@ func debugFacilities() string {
maxLen = len(name)
}
}
sort.Strings(names)
slices.Sort(names)
// Format the choices
b := new(bytes.Buffer)

44
go.mod
View File

@@ -4,16 +4,16 @@ go 1.23.0
require (
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0
github.com/alecthomas/kong v1.10.0
github.com/aws/aws-sdk-go v1.55.6
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
github.com/alecthomas/kong v1.11.0
github.com/aws/aws-sdk-go v1.55.7
github.com/calmh/incontainer v1.0.0
github.com/calmh/xdr v1.2.0
github.com/ccding/go-stun v0.1.5
github.com/chmduquesne/rollinghash v4.0.0+incompatible
github.com/d4l3k/messagediff v1.2.1
github.com/getsentry/raven-go v0.2.0
github.com/go-ldap/ldap/v3 v3.4.10
github.com/go-ldap/ldap/v3 v3.4.11
github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.12.1
github.com/greatroar/blobloom v0.8.0
@@ -28,32 +28,32 @@ require (
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
github.com/oschwald/geoip2-golang v1.11.0
github.com/pierrec/lz4/v4 v4.1.22
github.com/prometheus/client_golang v1.21.1
github.com/prometheus/client_golang v1.22.0
github.com/puzpuzpuz/xsync/v3 v3.5.1
github.com/quic-go/quic-go v0.50.1
github.com/quic-go/quic-go v0.52.0
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
github.com/shirou/gopsutil/v4 v4.25.3
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc
github.com/shirou/gopsutil/v4 v4.25.4
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.6
github.com/urfave/cli v1.22.16
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
github.com/willabides/kongplete v0.4.0
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.36.0
golang.org/x/net v0.38.0
golang.org/x/sys v0.31.0
golang.org/x/text v0.23.0
golang.org/x/crypto v0.38.0
golang.org/x/net v0.40.0
golang.org/x/sys v0.33.0
golang.org/x/text v0.25.0
golang.org/x/time v0.11.0
golang.org/x/tools v0.31.0
golang.org/x/tools v0.33.0
google.golang.org/protobuf v1.36.6
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
@@ -61,22 +61,21 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/ebitengine/purego v0.8.3 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/pprof v0.0.0-20241009165004-a3522334989c // indirect
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.11 // indirect
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -92,10 +91,9 @@ require (
github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
go.uber.org/mock v0.5.2 // indirect
golang.org/x/mod v0.24.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sync v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

166
go.sum
View File

@@ -1,30 +1,30 @@
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v1.10.0 h1:8K4rGDpT7Iu+jEXCIJUeKqvpwZHbsFRoebLbnzlmrpw=
github.com/alecthomas/kong v1.10.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/kong v1.11.0 h1:y++1gI7jf8O7G7l4LZo5ASFhrhJvzc+WgF/arranEmM=
github.com/alecthomas/kong v1.11.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
@@ -53,8 +53,8 @@ github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkE
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/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -63,10 +63,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU=
github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
@@ -77,8 +77,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -99,12 +99,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA=
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4=
github.com/greatroar/blobloom v0.8.0/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -113,7 +111,6 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@@ -146,8 +143,8 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4d
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -176,14 +173,14 @@ github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vv
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
@@ -203,8 +200,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -213,8 +210,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/quic-go v0.50.1 h1:unsgjFIUqW8a2oopkY7YNONpV1gYND6Nt9hnt1PN94Q=
github.com/quic-go/quic-go v0.50.1/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
@@ -227,8 +224,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE=
github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
github.com/shirou/gopsutil/v4 v4.25.4/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -239,13 +236,12 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc h1:xc3UfSFlH/X5hRw3h21RF6WXnRUYKmGRx06FEaVxfkM=
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
@@ -262,67 +258,37 @@ github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0/go.mod h1:TTbGUfE+cXXc
github.com/willabides/kongplete v0.4.0 h1:eivXxkp5ud5+4+NVN9e4goxC5mSh3n1RHov+gsblM2g=
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -342,49 +308,25 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -2,7 +2,7 @@
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
"API Key": "مفتاح API",
"API Key": "مفتاح واجهة برمجة التطبيقات \"API\"",
"About": "حول",
"Action": "إجراء",
"Actions": "الإجراءات",
@@ -27,6 +27,7 @@
"Allowed Networks": "الشبكات المسموح بها",
"Alphabetic": "أبجدية",
"Altered by ignoring deletes.": "تغير بتجاهل عمليات الحذف.",
"Always turned on when the folder type is \"{%foldertype%}\".": "مفعل دائمًا عندما يكون نوع المجلد هو \"{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "الإصدار يعالج بواسطة أمر خارجي. يجب إزالة الملف من المجلدات المشتركة. إذا كان المسار للتطبيق يحتوي على مسافات، يجب وضعها بين علامتي تنصيص دلالة على الاقتباس.",
"Anonymous Usage Reporting": "تقارير الإستخدام المجهولة",
"Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
@@ -52,6 +53,7 @@
"Body:": "جسم:",
"Bugs": "أخطاء برمجية",
"Cancel": "إلغاء",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "لا يمكن تفعيله عندما يكون نوع المجلد هو \"{{foldertype}}\".",
"Changelog": "سجل التغيير",
"Clean out after": "نظف بعد",
"Cleaning Versions": "إصدارات نظيفة",

View File

@@ -26,6 +26,7 @@
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
"Allowed Networks": "Rețele permise",
"Alphabetic": "Alfabetic",
"Altered by ignoring deletes.": "Modificat prin ignorarea ștergerilor.",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "O comandă externă gestionează versiunea. Trebuie să elimine fișierul din mapa partajat. Dacă calea către aplicație conține spații, ar trebui să fie pusă între ghilimele.",
"Anonymous Usage Reporting": "Raport Anonim despre Folosirea Aplicației",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Formatul raportului de utilizare anonim s-a schimbat. Doriți să vă mutați în noul format?",

View File

@@ -27,6 +27,7 @@
"Allowed Networks": "Разрешённые сети",
"Alphabetic": "По алфавиту",
"Altered by ignoring deletes.": "Изменено, игнорируя удаления.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Всегда включено для папок с типом «{{foldertype}}».",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Для версионирования используется внешняя программа. Ей нужно удалить файл из общей папки. Если путь к приложению содержит пробелы, его нужно взять в кавычки.",
"Anonymous Usage Reporting": "Анонимный отчет об использовании",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимных отчётов изменился. Хотите переключиться на новый формат?",
@@ -52,6 +53,7 @@
"Body:": "Тело:",
"Bugs": "Ошибки",
"Cancel": "Отмена",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Не может быть включено для папок с типом «{{foldertype}}».",
"Changelog": "Журнал изменений",
"Clean out after": "Очистить после",
"Cleaning Versions": "Очистка версий",
@@ -171,8 +173,8 @@
"Folder Path": "Путь к папке",
"Folder Status": "Статус папки",
"Folder Type": "Тип папки",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Тип папки «{{receiveEncrypted}}» может быть указан только при создании новой папки.",
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Тип папки «{{receiveEncrypted}}» не может быть изменён после добавления. Вам необходимо убрать папку, удалить или дешифровать данные на диске, а затем снова добавить папку.",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Тип папки «{{receiveEncrypted}}» может быть выбран только при добавлении новой папки.",
"Folder type \"{%receiveEncrypted%}\" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.": "Тип папки «{{receiveEncrypted}}» не может быть изменён после добавления. Вам необходимо убрать папку, удалить или дешифровать данные на диске и затем снова её добавить.",
"Folders": "Папки",
"For the following folders an error occurred while starting to watch for changes. It will be retried every minute, so the errors might go away soon. If they persist, try to fix the underlying issue and ask for help if you can't.": "Для следующих папок произошла ошибка при запуске отслеживания изменений. Попытки будут повторяться раз в минуту, и ошибки скоро могут быть устранены. Если этого не произойдёт, попробуйте разобраться в причинах и попросите поддержки, если у вас не получится.",
"Forever": "Вечно",

View File

@@ -0,0 +1,36 @@
{
"A device with that ID is already added.": "Уређај са тим идентификатором је већ додат.",
"A negative number of days doesn't make sense.": "Негативан број дана нема смисла.",
"A new major version may not be compatible with previous versions.": "Нова верзија можда неће радити са претходним верзијама.",
"API Key": "АПИ кључ",
"About": "Информације",
"Action": "Радња",
"Actions": "Радње",
"Active filter rules": "Активна правила филтера",
"Add": "Додај",
"Add Device": "Додај уређај",
"Add Folder": "Додај фасциклу",
"Add Remote Device": "Додаај удаљени уређај",
"Add devices from the introducer to our device list, for mutually shared folders.": "Додај уређаје од иницијатора на нашу листу уређаја, за међусобно дељене фасцикле.",
"Add filter entry": "Додај ставку филтера",
"Add ignore patterns": "Додај правила за игнорисање",
"Add new folder?": "Додај нову фасциклу?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Додатно, интервал потпуног поновног скенирања ће бити повећан (60 пута, тј. нови подразумевани интервал од 1 сат). Такође можете ручно да га подесите за сваку фасциклу касније након што изаберете Не.",
"Address": "Адреса",
"Addresses": "Адресе",
"Advanced": "Напредно",
"Advanced Configuration": "Напредна конфигурација",
"All Data": "Сви подаци",
"All Time": "Све време",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Све фасцикле које се деле са овим уређајем морају бити заштићене лозинком, тако да сви послати подаци не могу бити прочитани без дате лозинке.",
"Allow Anonymous Usage Reporting?": "Дозволити анонимно слање података о коришћењу?",
"Allowed Networks": "Дозвољене мреже",
"Alphabetic": "Абецедним редом",
"Altered by ignoring deletes.": "Промењено због игнорисања брисања.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Увек укључено када је тип фасцикле „{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Екстерна команда управља верзионирањем. Она мора да уклони фајл из дељене фасцикле. Ако путања до апликације садржи размаке, треба да буде под наводницима.",
"Anonymous Usage Reporting": "Анонимно слање података о употреби",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимног слања података о коришћењу је промењен. Желите ли да пређете на нови формат?",
"Applied to LAN": "Важи за локалну мрежу",
"Apply": "Примени"
}

View File

@@ -30,7 +30,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row">
<div class="col-md-12" id="contributor-list">
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ross Smith II, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Gusted, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Luke Hamburg, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tim Nordenfur, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, WangXi, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, dashangcun, derekriemer, desbma, diemade, digital, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, kylosus, luchenhan, luzpaz, marco-m, mathias4833, maxice8, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, polyfloyd, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Emil Lundberg, Eric P, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ross Smith II, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Wulf Weich, bt90, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Aleksey Vasenev, Alessandro G., Alex Ionescu, Alex Lindeman, Alex Xu, Alexander Seiler, Alexandre Alves, Aman Gupta, Anatoli Babenia, Andreas Sommer, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Anthony Goeckner, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Ashish Bhate, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Beat Reichenbach, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Catfriend1, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Kujau, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Daniel Padrta, Darshil Chanpura, David Rimmer, DeflateAwning, Denis A., Dennis Wilson, DerRockWolf, Devon G. Redekopp, Dimitri Papadopoulos Orfanos, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Gusted, Han Boetes, HansK-p, Harrison Jones, Hazem Krimi, Heiko Zuerker, Hireworks, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James O'Beirne, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaspitta, Jauder Ho, Jaya Chithra, Jaya Kumar, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Julian Lehrhuber, Jörg Thalheim, Jędrzej Kula, K.B.Dharun Krishna, Kalle Laine, Kapil Sareen, Karol Różycki, Kebin Liu, Keith Harrison, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Luke Hamburg, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus B Spencer, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Martin Polehla, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, Maximilian, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Migelo, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nick Busey, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Paul Donald, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Sertonix, Severin von Wnuck-Lipinski, Shaarad Dalvi, Simon Mwepu, Simon Pickup, Sly_tom_cat, Sonu Kumar Saw, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Sven Bachmann, Sébastien WENSKE, Taylor Khan, Terrance, TheCreeper, Thomas, Thomas Hipp, Tim Abell, Tim Howes, Tim Nordenfur, Tobias Frölich, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tommy van der Vorst, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vik, Vil Brekin, Vladimir Rusinov, WangXi, Will Rouesnel, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, chenrui, chucic, cjc7373, cui fliter, d-volution, dashangcun, derekriemer, desbma, diemade, digital, domain, entity0xfe, georgespatton, ghjklw, guangwu, gudvinr, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, kylosus, luchenhan, luzpaz, marco-m, mathias4833, maxice8, mclang, mv1005, nf, orangekame3, otbutz, overkill, perewa, polyfloyd, pullmerge, red_led, rubenbe, sec65, vapatel2, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
</div>
</div>
</div>
@@ -78,7 +78,6 @@ Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Al
<li><a href="https://github.com/jackpal/go-nat-pmp">jackpal/go-nat-pmp</a>, Copyright 2013 John Howard Palevich.</li>
<li><a href="https://github.com/julienschmidt/httprouter">julienschmidt/httprouter</a>, Copyright &copy; 2013, Julien Schmidt.</li>
<li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li>
<li><a href="https://github.com/klauspost/compress">klauspost/compress</a>, Copyright &copy; 2012 The Go Authors.</li>
<li><a href="https://github.com/miscreant/miscreant.go">miscreant/miscreant.go</a>, Copyright &copy; 2017-2019 The Miscreant Developers.</li>
<li><a href="https://github.com/munnerz/goautoneg">munnerz/goautoneg</a>, Copyright &copy; 2011, Open Knowledge Foundation Ltd.</li>
<li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li>

View File

@@ -16,6 +16,14 @@ angular.module('syncthing.core')
},
link: function (scope, element, attrs) {
$(element).on('click', function (event) {
const closestTabAnchor = event.target.closest('a[data-toggle="tab"]');
if (closestTabAnchor && closestTabAnchor.href.includes('#')) {
event.preventDefault();
}
});
// before modal show animation
$(element).on('show.bs.modal', function () {

View File

@@ -1,27 +1,39 @@
angular.module('syncthing.core')
.filter('uncamel', function () {
const reservedStrings = [
'IDs', 'ID', // substrings must come AFTER longer keywords containing them
'URL', 'UR',
'API', 'QUIC', 'TCP', 'UDP', 'NAT', 'LAN', 'WAN',
'KiB', 'MiB', 'GiB', 'TiB'
];
return function (input) {
input = input.replace(/(.)([A-Z][a-z]+)/g, '$1 $2').replace(/([a-z0-9])([A-Z])/g, '$1 $2');
var parts = input.split(' ');
var lastPart = parts.splice(-1)[0];
if (!input || typeof input !== 'string') return '';
const placeholders = {};
let counter = 0;
reservedStrings.forEach(word => {
const placeholder = `__RSV${counter}__`;
const re = new RegExp(word, 'g');
input = input.replace(re, placeholder);
placeholders[placeholder] = word;
counter++;
});
input = input.replace(/([a-z0-9])([A-Z])/g, '$1 $2');
Object.entries(placeholders).forEach(([ph, word]) => {
input = input.replace(new RegExp(ph, 'g'), ` ${word} `);
});
let parts = input.split(' ');
const lastPart = parts.pop();
switch (lastPart) {
case "S":
parts.push('(seconds)');
break;
case "M":
parts.push('(minutes)');
break;
case "H":
parts.push('(hours)');
break;
case "Ms":
parts.push('(milliseconds)');
break;
default:
parts.push(lastPart);
break;
case 'S': parts.push('(seconds)'); break;
case 'M': parts.push('(minutes)'); break;
case 'H': parts.push('(hours)'); break;
case 'Ms': parts.push('(milliseconds)'); break;
default: parts.push(lastPart); break;
}
input = parts.join(' ');
return input.charAt(0).toUpperCase() + input.slice(1);
parts = parts.map(part => {
const match = reservedStrings.find(w => w.toUpperCase() === part.toUpperCase());
return match || part.charAt(0).toUpperCase() + part.slice(1);
});
return parts.join(' ').replace(/\s+/g, ' ').trim();
};
});

View File

@@ -8,6 +8,7 @@ package api
import (
"bytes"
"cmp"
"context"
"crypto/tls"
"crypto/x509"
@@ -24,7 +25,7 @@ import (
"reflect"
"runtime"
"runtime/pprof"
"sort"
"slices"
"strconv"
"strings"
"time"
@@ -750,7 +751,7 @@ func (*service) getSystemVersion(w http.ResponseWriter, _ *http.Request) {
func (*service) getSystemDebug(w http.ResponseWriter, _ *http.Request) {
names := l.Facilities()
enabled := l.FacilityDebugging()
sort.Strings(enabled)
slices.Sort(enabled)
sendJSON(w, map[string]interface{}{
"facilities": names,
"enabled": enabled,
@@ -1535,8 +1536,8 @@ func (*service) getLang(w http.ResponseWriter, r *http.Request) {
langs = append(langs, code)
}
// Reorder by descending q value
sort.SliceStable(langs, func(i, j int) bool {
return weights[langs[i]] > weights[langs[j]]
slices.SortStableFunc(langs, func(i, j string) int {
return cmp.Compare(weights[j], weights[i])
})
sendJSON(w, langs)
}
@@ -1822,8 +1823,8 @@ func browseFiles(ffs fs.Filesystem, search string) []string {
}
// sort to return matches in deterministic order (don't depend on file system order)
sort.Strings(exactMatches)
sort.Strings(caseInsMatches)
slices.Sort(exactMatches)
slices.Sort(caseInsMatches)
return append(exactMatches, caseInsMatches...)
}
@@ -1920,7 +1921,7 @@ func dirNames(dir string) []string {
}
}
sort.Strings(dirs)
slices.Sort(dirs)
return dirs
}

View File

@@ -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] {

View File

@@ -12,7 +12,7 @@ import (
"os"
"regexp"
"runtime"
"sort"
"slices"
"strconv"
"strings"
"time"
@@ -109,7 +109,7 @@ func TagsList() []string {
tags = append(tags, Extra)
}
sort.Strings(tags)
slices.Sort(tags)
return tags
}

View File

@@ -17,7 +17,7 @@ import (
"net/url"
"os"
"reflect"
"sort"
"slices"
"strconv"
"strings"
@@ -235,6 +235,10 @@ func ReadJSON(r io.Reader, myID protocol.DeviceID) (Configuration, error) {
func (cfg Configuration) Copy() Configuration {
newCfg := cfg
// Deep copy Defaults
newCfg.Defaults.Folder = cfg.Defaults.Folder.Copy()
newCfg.Defaults.Device = cfg.Defaults.Device.Copy()
// Deep copy FolderConfigurations
newCfg.Folders = make([]FolderConfiguration, len(cfg.Folders))
for i := range newCfg.Folders {
@@ -333,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
@@ -376,8 +380,8 @@ func (cfg *Configuration) prepareFolders(myID protocol.DeviceID, existingDevices
}
}
// Ensure that the folder list is sorted by ID
sort.Slice(cfg.Folders, func(a, b int) bool {
return cfg.Folders[a].ID < cfg.Folders[b].ID
slices.SortFunc(cfg.Folders, func(a, b FolderConfiguration) int {
return strings.Compare(a.ID, b.ID)
})
return sharedFolders, nil
}

View File

@@ -17,7 +17,7 @@ import (
"path/filepath"
"reflect"
"runtime"
"sort"
"slices"
"strings"
"testing"
@@ -911,7 +911,7 @@ func TestV14ListenAddressesMigration(t *testing.T) {
t.Error("Configuration was not converted")
}
sort.Strings(tc[2])
slices.Sort(tc[2])
if !reflect.DeepEqual(cfg.Options.RawListenAddresses, tc[2]) {
t.Errorf("Migration error; actual %#v != expected %#v", cfg.Options.RawListenAddresses, tc[2])
}

View File

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

View File

@@ -15,7 +15,7 @@ import (
"fmt"
"path"
"path/filepath"
"sort"
"slices"
"strings"
"time"
@@ -291,8 +291,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 {

View File

@@ -7,11 +7,12 @@
package config
import (
"cmp"
"net/url"
"os"
"path"
"path/filepath"
"sort"
"slices"
"strings"
"sync"
@@ -65,8 +66,8 @@ type migrationSet []migration
func (ms migrationSet) apply(cfg *Configuration) {
// Make sure we apply the migrations in target version order regardless
// of how it was defined.
sort.Slice(ms, func(a, b int) bool {
return ms[a].targetVersion < ms[b].targetVersion
slices.SortFunc(ms, func(a, b migration) int {
return cmp.Compare(a.targetVersion, b.targetVersion)
})
// Apply all migrations.
@@ -349,7 +350,7 @@ func migrateToConfigV14(cfg *Configuration) {
cfg.Options.DeprecatedRelayServers = nil
// For consistency
sort.Strings(cfg.Options.RawListenAddresses)
slices.Sort(cfg.Options.RawListenAddresses)
var newAddrs []string
for _, addr := range cfg.Options.RawGlobalAnnServers {

View File

@@ -68,8 +68,8 @@ type OptionsConfiguration struct {
AnnounceLANAddresses bool `json:"announceLANAddresses" xml:"announceLANAddresses" default:"true"`
SendFullIndexOnUpgrade bool `json:"sendFullIndexOnUpgrade" xml:"sendFullIndexOnUpgrade"`
FeatureFlags []string `json:"featureFlags" xml:"featureFlag"`
AuditEnabled bool `json:"auditEnabled" xml:"auditEnabled" default:"false"`
AuditFile string `json:"auditFile" xml:"auditFile"`
AuditEnabled bool `json:"auditEnabled" xml:"auditEnabled" default:"false" restart:"true"`
AuditFile string `json:"auditFile" xml:"auditFile" restart:"true"`
// The number of connections at which we stop trying to connect to more
// devices, zero meaning no limit. Does not affect incoming connections.
ConnectionLimitEnough int `json:"connectionLimitEnough" xml:"connectionLimitEnough"`

View File

@@ -9,7 +9,8 @@ package config
import (
"encoding/json"
"encoding/xml"
"sort"
"slices"
"strings"
"github.com/syncthing/syncthing/lib/structutil"
)
@@ -84,8 +85,8 @@ func (c *VersioningConfiguration) toInternal() internalVersioningConfiguration {
for k, v := range c.Params {
tmp.Params = append(tmp.Params, internalParam{k, v})
}
sort.Slice(tmp.Params, func(a, b int) bool {
return tmp.Params[a].Key < tmp.Params[b].Key
slices.SortFunc(tmp.Params, func(a, b internalParam) int {
return strings.Compare(a.Key, b.Key)
})
return tmp
}

View File

@@ -23,7 +23,6 @@ import (
"net"
"net/url"
"slices"
"sort"
"strings"
stdsync "sync"
"time"
@@ -1151,7 +1150,7 @@ func (s *service) dialParallel(ctx context.Context, deviceID protocol.DeviceID,
}
// Sort the priorities so that we dial lowest first (which means highest...)
sort.Ints(priorities)
slices.Sort(priorities)
sema := semaphore.MultiSemaphore{semaphore.New(dialMaxParallelPerDevice), parentSema}
for _, prio := range priorities {

View File

@@ -8,7 +8,7 @@ package db
import (
"math/bits"
"sort"
"slices"
"testing"
"github.com/syncthing/syncthing/lib/events"
@@ -71,8 +71,8 @@ func TestMetaDevices(t *testing.T) {
}
// Check that we got the two devices we expect
sort.Slice(devs, func(a, b int) bool {
return devs[a].Compare(devs[b]) == -1
slices.SortFunc(devs, func(a, b protocol.DeviceID) int {
return a.Compare(b)
})
if devs[0] != d1 {
t.Error("first device should be d1")

View File

@@ -11,7 +11,8 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"slices"
"strings"
"testing"
"time"
@@ -102,16 +103,8 @@ func needList(t testing.TB, s *db.FileSet, n protocol.DeviceID) []protocol.FileI
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) String() string {
@@ -218,7 +211,7 @@ func TestGlobalSet(t *testing.T) {
t.Helper()
g := fileList(globalList(t, m))
sort.Sort(g)
slices.SortFunc(g, compareByName)
if fmt.Sprint(g) != fmt.Sprint(expectedGlobal) {
t.Errorf("Global incorrect;\n A: %v !=\n E: %v", g, expectedGlobal)
@@ -255,7 +248,7 @@ func TestGlobalSet(t *testing.T) {
}
h := fileList(haveList(t, m, protocol.LocalDeviceID))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(localTot) {
t.Errorf("Have incorrect (local);\n A: %v !=\n E: %v", h, localTot)
@@ -292,14 +285,14 @@ func TestGlobalSet(t *testing.T) {
}
h = fileList(haveList(t, m, remoteDevice0))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(remoteTot) {
t.Errorf("Have incorrect (remote);\n A: %v !=\n E: %v", h, remoteTot)
}
n := fileList(needList(t, m, protocol.LocalDeviceID))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedLocalNeed) {
t.Errorf("Need incorrect (local);\n A: %v !=\n E: %v", n, expectedLocalNeed)
@@ -308,7 +301,7 @@ func TestGlobalSet(t *testing.T) {
checkNeed(t, m, protocol.LocalDeviceID, expectedLocalNeed)
n = fileList(needList(t, m, remoteDevice0))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedRemoteNeed) {
t.Errorf("Need incorrect (remote);\n A: %v !=\n E: %v", n, expectedRemoteNeed)
@@ -428,14 +421,14 @@ func TestGlobalSet(t *testing.T) {
check()
h := fileList(haveList(t, m, remoteDevice1))
sort.Sort(h)
slices.SortFunc(h, compareByName)
if fmt.Sprint(h) != fmt.Sprint(secRemote) {
t.Errorf("Have incorrect (secRemote);\n A: %v !=\n E: %v", h, secRemote)
}
n := fileList(needList(t, m, remoteDevice1))
sort.Sort(n)
slices.SortFunc(n, compareByName)
if fmt.Sprint(n) != fmt.Sprint(expectedSecRemoteNeed) {
t.Errorf("Need incorrect (secRemote);\n A: %v !=\n E: %v", n, expectedSecRemoteNeed)
@@ -475,7 +468,7 @@ func TestNeedWithInvalid(t *testing.T) {
replace(s, remoteDevice1, remote1Have)
need := fileList(needList(t, s, protocol.LocalDeviceID))
sort.Sort(need)
slices.SortFunc(need, compareByName)
if fmt.Sprint(need) != fmt.Sprint(expectedNeed) {
t.Errorf("Need incorrect;\n A: %v !=\n E: %v", need, expectedNeed)
@@ -503,7 +496,7 @@ func TestUpdateToInvalid(t *testing.T) {
replace(s, protocol.LocalDeviceID, localHave)
have := fileList(haveList(t, s, protocol.LocalDeviceID))
sort.Sort(have)
slices.SortFunc(have, compareByName)
if fmt.Sprint(have) != fmt.Sprint(localHave) {
t.Errorf("Have incorrect before invalidation;\n A: %v !=\n E: %v", have, localHave)
@@ -519,8 +512,8 @@ func TestUpdateToInvalid(t *testing.T) {
s.Update(protocol.LocalDeviceID, append(fileList{}, localHave[1], localHave[4]))
have = fileList(haveList(t, s, protocol.LocalDeviceID))
sort.Sort(have)
have = haveList(t, s, protocol.LocalDeviceID)
slices.SortFunc(have, compareByName)
if fmt.Sprint(have) != fmt.Sprint(localHave) {
t.Errorf("Have incorrect after invalidation;\n A: %v !=\n E: %v", have, localHave)
@@ -605,7 +598,7 @@ func TestGlobalReset(t *testing.T) {
replace(m, protocol.LocalDeviceID, local)
g := globalList(t, m)
sort.Sort(fileList(g))
slices.SortFunc(g, compareByName)
if diff, equal := messagediff.PrettyDiff(local, g); !equal {
t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
@@ -615,7 +608,7 @@ func TestGlobalReset(t *testing.T) {
replace(m, remoteDevice0, nil)
g = globalList(t, m)
sort.Sort(fileList(g))
slices.SortFunc(g, compareByName)
if diff, equal := messagediff.PrettyDiff(local, g); !equal {
t.Errorf("Global incorrect;\nglobal: %v\n!=\nlocal: %v\ndiff:\n%s", g, local, diff)
@@ -653,8 +646,8 @@ func TestNeed(t *testing.T) {
need := needList(t, m, protocol.LocalDeviceID)
sort.Sort(fileList(need))
sort.Sort(fileList(shouldNeed))
slices.SortFunc(need, compareByName)
slices.SortFunc(shouldNeed, compareByName)
if fmt.Sprint(need) != fmt.Sprint(shouldNeed) {
t.Errorf("Need incorrect;\n%v !=\n%v", need, shouldNeed)

View File

@@ -8,7 +8,7 @@ package db
import (
"encoding/binary"
"sort"
"slices"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/sync"
@@ -147,6 +147,6 @@ func (i *smallIndex) Values() []string {
}
i.mut.Unlock()
sort.Strings(vals)
slices.Sort(vals)
return vals
}

View File

@@ -13,7 +13,7 @@ import (
"context"
"crypto/tls"
"fmt"
"sort"
"slices"
"time"
"github.com/thejerf/suture/v4"
@@ -159,7 +159,7 @@ func (m *manager) Lookup(ctx context.Context, deviceID protocol.DeviceID) (addre
m.mut.RUnlock()
addresses = stringutil.UniqueTrimmedStrings(addresses)
sort.Strings(addresses)
slices.Sort(addresses)
l.Debugln("lookup results for", deviceID)
l.Debugln(" addresses: ", addresses)

View File

@@ -12,7 +12,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"slices"
"strconv"
"strings"
"syscall"
@@ -218,7 +218,7 @@ func TestDirNames(t *testing.T) {
"a",
"bC",
}
sort.Strings(testCases)
slices.Sort(testCases)
for _, sub := range testCases {
if err := os.Mkdir(filepath.Join(dir, sub), 0o777); err != nil {
@@ -229,7 +229,7 @@ func TestDirNames(t *testing.T) {
if dirs, err := fs.DirNames("."); err != nil || len(dirs) != len(testCases) {
t.Errorf("%s %s %s", err, dirs, testCases)
} else {
sort.Strings(dirs)
slices.Sort(dirs)
for i := range dirs {
if dirs[i] != testCases[i] {
t.Errorf("%s != %s", dirs[i], testCases[i])
@@ -321,8 +321,8 @@ func TestGlob(t *testing.T) {
for _, testCase := range testCases {
results, err := fs.Glob(testCase.pattern)
sort.Strings(results)
sort.Strings(testCase.matches)
slices.Sort(results)
slices.Sort(testCase.matches)
if err != nil {
t.Error(err)
}
@@ -628,8 +628,7 @@ func TestXattr(t *testing.T) {
Value: value,
})
}
sort.Slice(attrs, func(i, j int) bool { return attrs[i].Name < attrs[j].Name })
slices.SortFunc(attrs, func(a, b protocol.Xattr) int { return strings.Compare(a.Name, b.Name) })
// Set the xattrs, read them back and compare
if err := tfs.SetXattr("/test", attrs, testXattrFilter{}); err != nil {
t.Fatal(err)

View File

@@ -12,7 +12,7 @@ package fs
import (
"errors"
"fmt"
"sort"
"slices"
"unsafe"
"golang.org/x/sys/unix"
@@ -69,7 +69,7 @@ func listXattr(path string) ([]string, error) {
}
}
sort.Strings(attrs)
slices.Sort(attrs)
return attrs, nil
}

View File

@@ -12,7 +12,7 @@ package fs
import (
"errors"
"fmt"
"sort"
"slices"
"strings"
"golang.org/x/sys/unix"
@@ -38,6 +38,6 @@ func listXattr(path string) ([]string, error) {
buf = buf[:size]
attrs := compact(strings.Split(string(buf), "\x00"))
sort.Strings(attrs)
slices.Sort(attrs)
return attrs, nil
}

View File

@@ -11,7 +11,7 @@ import (
"fmt"
"path/filepath"
"runtime"
"sort"
"slices"
"strings"
"testing"
"time"
@@ -344,7 +344,7 @@ func fakefsForBenchmark(nfiles int, latency time.Duration) (Filesystem, []string
return nil, nil, errors.New("didn't find enough stuff")
}
sort.Strings(paths)
slices.Sort(paths)
return fsys, paths, nil
}

View File

@@ -14,7 +14,7 @@ import (
"path"
"path/filepath"
"runtime"
"sort"
"slices"
"testing"
"time"
@@ -369,8 +369,8 @@ func assertDir(t *testing.T, fs Filesystem, directory string, filenames []string
if path.Clean(directory) == "/" {
filenames = append(filenames, ".stfolder")
}
sort.Strings(filenames)
sort.Strings(got)
slices.Sort(filenames)
slices.Sort(got)
if len(filenames) != len(got) {
t.Errorf("want %s, got %s", filenames, got)

View File

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

View File

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

View File

@@ -12,7 +12,8 @@ import (
"fmt"
"math/rand"
"path/filepath"
"sort"
"slices"
"strings"
"time"
"github.com/syncthing/syncthing/lib/config"
@@ -1200,7 +1201,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
}
@@ -1341,7 +1344,7 @@ func unifySubs(dirs []string, exists func(dir string) bool) []string {
if len(dirs) == 0 {
return nil
}
sort.Strings(dirs)
slices.Sort(dirs)
if dirs[0] == "" || dirs[0] == "." || dirs[0] == string(fs.PathSeparator) {
return nil
}

View File

@@ -8,7 +8,8 @@ package model
import (
"fmt"
"sort"
"slices"
"strings"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
@@ -112,7 +113,9 @@ func (f *receiveEncryptedFolder) revertHandleDirs(dirs []string, snap *db.Snapsh
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, snap, scanChan); err != nil {
f.newScanError(dir, fmt.Errorf("deleting unexpected dir: %w", err))

View File

@@ -7,7 +7,8 @@
package model
import (
"sort"
"slices"
"strings"
"time"
"github.com/syncthing/syncthing/lib/config"
@@ -207,7 +208,9 @@ func (q *deleteQueue) handle(fi protocol.FileInfo, snap *db.Snapshot) (bool, err
func (q *deleteQueue) flush(snap *db.Snapshot) ([]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

View File

@@ -14,7 +14,7 @@ import (
"fmt"
"io"
"path/filepath"
"sort"
"slices"
"strconv"
"strings"
"time"
@@ -1867,7 +1867,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)
@@ -2206,20 +2208,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

View File

@@ -8,6 +8,7 @@ package model
import (
"path/filepath"
"slices"
"testing"
"github.com/d4l3k/messagediff"
@@ -117,20 +118,11 @@ func unifySubsCases() []unifySubsCase {
return cases
}
func unifyExists(f string, tc unifySubsCase) bool {
for _, e := range tc.exists {
if f == e {
return true
}
}
return false
}
func TestUnifySubs(t *testing.T) {
cases := unifySubsCases()
for i, tc := range cases {
exists := func(f string) bool {
return unifyExists(f, tc)
return slices.Contains(tc.exists, f)
}
out := unifySubs(tc.in, exists)
if diff, equal := messagediff.PrettyDiff(tc.out, out); !equal {
@@ -146,7 +138,7 @@ func BenchmarkUnifySubs(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, tc := range cases {
exists := func(f string) bool {
return unifyExists(f, tc)
return slices.Contains(tc.exists, f)
}
unifySubs(tc.in, exists)
}

View File

@@ -21,6 +21,7 @@ import (
"path/filepath"
"reflect"
"runtime"
"slices"
"strings"
stdsync "sync"
"sync/atomic"
@@ -1804,11 +1805,9 @@ func (m *model) handleAutoAccepts(deviceID protocol.DeviceID, folder protocol.Fo
l.Infof("Failed to auto-accept folder %s from %s due to path conflict", folder.Description(), deviceID)
return config.FolderConfiguration{}, false
} else {
for _, device := range cfg.DeviceIDs() {
if device == deviceID {
// Already shared nothing todo.
return config.FolderConfiguration{}, false
}
if slices.Contains(cfg.DeviceIDs(), deviceID) {
// Already shared nothing todo.
return config.FolderConfiguration{}, false
}
if cfg.Type == config.FolderTypeReceiveEncrypted {
if len(ccDeviceInfos.remote.EncryptionPasswordToken) == 0 && len(ccDeviceInfos.local.EncryptionPasswordToken) == 0 {

View File

@@ -17,7 +17,7 @@ import (
"os"
"path/filepath"
"runtime/pprof"
"sort"
"slices"
"strconv"
"strings"
"sync"
@@ -1253,10 +1253,8 @@ func TestAutoAcceptPausedWhenFolderConfigChanged(t *testing.T) {
} else if fcfg.Path != idOther {
t.Error("folder path changed")
} else {
for _, dev := range fcfg.DeviceIDs() {
if dev == device1 {
return
}
if slices.Contains(fcfg.DeviceIDs(), device1) {
return
}
t.Error("device missing")
}
@@ -1302,10 +1300,8 @@ func TestAutoAcceptPausedWhenFolderConfigNotChanged(t *testing.T) {
} else if fcfg.Path != idOther {
t.Error("folder path changed")
} else {
for _, dev := range fcfg.DeviceIDs() {
if dev == device1 {
return
}
if slices.Contains(fcfg.DeviceIDs(), device1) {
return
}
t.Error("device missing")
}
@@ -4095,8 +4091,8 @@ func equalStringsInAnyOrder(a, b []string) bool {
if len(a) != len(b) {
return false
}
sort.Strings(a)
sort.Strings(b)
slices.Sort(a)
slices.Sort(b)
for i := range a {
if a[i] != b[i] {
return false

View File

@@ -7,7 +7,8 @@
package model
import (
"sort"
"cmp"
"slices"
"time"
"github.com/syncthing/syncthing/lib/rand"
@@ -157,40 +158,34 @@ func (q *jobQueue) SortSmallestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(smallestFirst(q.queued))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(a.size, b.size)
})
}
func (q *jobQueue) SortLargestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(sort.Reverse(smallestFirst(q.queued)))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(b.size, a.size)
})
}
func (q *jobQueue) SortOldestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(oldestFirst(q.queued))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(a.modified, b.modified)
})
}
func (q *jobQueue) SortNewestFirst() {
q.mut.Lock()
defer q.mut.Unlock()
sort.Sort(sort.Reverse(oldestFirst(q.queued)))
slices.SortFunc(q.queued, func(a, b jobQueueEntry) int {
return cmp.Compare(b.modified, a.modified)
})
}
// The usual sort.Interface boilerplate
type smallestFirst []jobQueueEntry
func (q smallestFirst) Len() int { return len(q) }
func (q smallestFirst) Less(a, b int) bool { return q[a].size < q[b].size }
func (q smallestFirst) Swap(a, b int) { q[a], q[b] = q[b], q[a] }
type oldestFirst []jobQueueEntry
func (q oldestFirst) Len() int { return len(q) }
func (q oldestFirst) Less(a, b int) bool { return q[a].modified < q[b].modified }
func (q oldestFirst) Swap(a, b int) { q[a], q[b] = q[b], q[a] }

View File

@@ -10,7 +10,7 @@ import (
"fmt"
"net/http"
"net/url"
"sort"
"slices"
"sync"
"time"
@@ -166,7 +166,7 @@ func relayAddressesOrder(ctx context.Context, input []string) []string {
ids = append(ids, id)
}
sort.Ints(ids)
slices.Sort(ids)
addresses := make([]string, 0, len(input))
for _, id := range ids {

View File

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

View File

@@ -15,7 +15,7 @@ import (
"net/http"
"os"
"runtime"
"sort"
"slices"
"strings"
"sync"
"time"
@@ -437,8 +437,8 @@ func printServiceTree(w io.Writer, sup supervisor, level int) {
printService(w, sup, level)
svcs := sup.Services()
sort.Slice(svcs, func(a, b int) bool {
return fmt.Sprint(svcs[a]) < fmt.Sprint(svcs[b])
slices.SortFunc(svcs, func(a, b suture.Service) int {
return strings.Compare(fmt.Sprint(a), fmt.Sprint(b))
})
for _, svc := range svcs {

View File

@@ -16,7 +16,7 @@ import (
"net/http"
"os"
"runtime"
"sort"
"slices"
"strings"
"sync"
"time"
@@ -162,7 +162,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
l.Warnf("Unhandled versioning type for usage reports: %s", cfg.Versioning.Type)
}
}
sort.Ints(report.RescanIntvs)
slices.Sort(report.RescanIntvs)
for _, cfg := range s.cfg.Devices() {
if cfg.Introducer {
@@ -295,7 +295,7 @@ func (s *Service) reportData(ctx context.Context, urVersion int, preview bool) (
report.FolderUsesV3.SyncOwnership++
}
}
sort.Ints(report.FolderUsesV3.FsWatcherDelays)
slices.Sort(report.FolderUsesV3.FsWatcherDelays)
for _, cfg := range s.cfg.Devices() {
if cfg.Untrusted {

View File

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

View File

@@ -8,7 +8,7 @@ package versioner
import (
"context"
"sort"
"slices"
"strconv"
"time"
@@ -79,7 +79,7 @@ func (v simple) toRemove(versions []string, now time.Time) []string {
var remove []string
// The list of versions may or may not be properly sorted.
sort.Strings(versions)
slices.Sort(versions)
// If the amount of elements exceeds the limit: the oldest elements are to be removed.
if len(versions) > v.keep {

View File

@@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config"
)
@@ -156,3 +157,102 @@ func TestPathTildes(t *testing.T) {
t.Fatalf("found versioned file %q, want one that begins with %q", got, testPath)
}
}
func TestArchiveFoldersCreationPermission(t *testing.T) {
if build.IsWindows {
t.Skip("Skipping on Windows")
return
}
dir := t.TempDir()
versionsDir := t.TempDir()
cfg := config.FolderConfiguration{
FilesystemType: config.FilesystemTypeBasic,
Path: dir,
Versioning: config.VersioningConfiguration{
FSPath: versionsDir,
FSType: config.FilesystemTypeBasic,
Params: map[string]string{
"keep": "2",
},
},
}
vfs := cfg.Filesystem(nil)
v := newSimple(cfg)
// Create two folders and set their permissions
folder1Path := filepath.Join(dir, "folder1")
folder1Perms := os.FileMode(0o777)
folder1VersionsPath := filepath.Join(versionsDir, "folder1")
err := os.Mkdir(folder1Path, folder1Perms)
if err != nil {
t.Fatal(err)
}
// chmod incase umask changes the create permissions
err = os.Chmod(folder1Path, folder1Perms)
if err != nil {
t.Fatal(err)
}
folder2Path := filepath.Join(folder1Path, "földer2")
folder2VersionsPath := filepath.Join(folder1VersionsPath, "földer2")
folder2Perms := os.FileMode(0o744)
err = os.Mkdir(folder2Path, folder2Perms)
if err != nil {
t.Fatal(err)
}
// chmod incase umask changes the create permissions
err = os.Chmod(folder2Path, folder2Perms)
if err != nil {
t.Fatal(err)
}
// create a file
filePath := filepath.Join("folder1", "földer2", "testFile")
f, err := vfs.Create(filePath)
if err != nil {
t.Fatal(err)
}
f.Close()
if err := v.Archive(filePath); err != nil {
t.Error(err)
}
// check permissions of the created version folders
folder1VersionsInfo, err := os.Stat(folder1VersionsPath)
if err != nil {
t.Fatal(err)
}
if folder1VersionsInfo.Mode().Perm() != folder1Perms {
t.Errorf("folder1 permissions %v, want %v", folder1VersionsInfo.Mode(), folder1Perms)
}
folder2VersionsInfo, err := os.Stat(folder2VersionsPath)
if err != nil {
t.Fatal(err)
}
if folder2VersionsInfo.Mode().Perm() != folder2Perms {
t.Errorf("földer2 permissions %v, want %v", folder2VersionsInfo.Mode(), folder2Perms)
}
// Archive again to test that archiving doesn't fail if the versioned folders already exist
if err := v.Archive(filePath); err != nil {
t.Error(err)
}
folder1VersionsInfo, err = os.Stat(folder1VersionsPath)
if err != nil {
t.Fatal(err)
}
if folder1VersionsInfo.Mode().Perm() != folder1Perms {
t.Errorf("folder1 permissions %v, want %v", folder1VersionsInfo.Mode(), folder1Perms)
}
folder2VersionsInfo, err = os.Stat(folder2VersionsPath)
if err != nil {
t.Fatal(err)
}
if folder2VersionsInfo.Mode().Perm() != folder2Perms {
t.Errorf("földer2 permissions %v, want %v", folder2VersionsInfo.Mode(), folder2Perms)
}
}

View File

@@ -9,7 +9,7 @@ package versioner
import (
"context"
"fmt"
"sort"
"slices"
"strconv"
"time"
@@ -69,7 +69,7 @@ func (v *staggered) toRemove(versions []string, now time.Time) []string {
var remove []string
// The list of versions may or may not be properly sorted.
sort.Strings(versions)
slices.Sort(versions)
for _, version := range versions {
versionTime, err := time.ParseInLocation(TimeFormat, extractTag(version), time.Local)

View File

@@ -9,7 +9,7 @@ package versioner
import (
"os"
"path/filepath"
"sort"
"slices"
"strconv"
"testing"
"time"
@@ -97,7 +97,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) {
"test~20150416-135958", // 365 days 2 seconds ago
"test~20150414-140000", // 367 days ago
}
sort.Strings(delete)
slices.Sort(delete)
cfg := config.FolderConfiguration{
FilesystemType: config.FilesystemTypeBasic,
@@ -111,7 +111,7 @@ func TestStaggeredVersioningVersionCount(t *testing.T) {
v := newStaggered(cfg).(*staggered)
rem := v.toRemove(versionsWithMtime, now)
sort.Strings(rem)
slices.Sort(rem)
if diff, equal := messagediff.PrettyDiff(delete, rem); !equal {
t.Errorf("Incorrect deleted files; got %v, expected %v\n%v", rem, delete, diff)

View File

@@ -10,9 +10,10 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"regexp"
"sort"
"slices"
"strings"
"time"
@@ -164,9 +165,8 @@ func archiveFile(method fs.CopyRangeMethod, srcFs, dstFs fs.Filesystem, filePath
file := filepath.Base(filePath)
inFolderPath := filepath.Dir(filePath)
err = dstFs.MkdirAll(inFolderPath, 0o755)
if err != nil && !fs.IsExist(err) {
err = dupDirTree(srcFs, dstFs, inFolderPath)
if err != nil {
l.Debugln("archiving", filePath, err)
return err
}
@@ -190,6 +190,50 @@ func archiveFile(method fs.CopyRangeMethod, srcFs, dstFs fs.Filesystem, filePath
return err
}
func dupDirTree(srcFs, dstFs fs.Filesystem, folderPath string) error {
// Return early if the folder already exists.
_, err := dstFs.Stat(folderPath)
if err == nil || !fs.IsNotExist(err) {
return err
}
hadParent := true
for i := range folderPath {
if os.IsPathSeparator(folderPath[i]) {
// If the parent folder didn't exist, then this folder doesn't exist
// so we can skip the check
if hadParent {
_, err := dstFs.Stat(folderPath[:i])
if err == nil {
continue
}
if !fs.IsNotExist(err) {
return err
}
}
hadParent = false
err := dupDirWithPerms(srcFs, dstFs, folderPath[:i])
if err != nil {
return err
}
}
}
return dupDirWithPerms(srcFs, dstFs, folderPath)
}
func dupDirWithPerms(srcFs, dstFs fs.Filesystem, folderPath string) error {
srcStat, err := srcFs.Stat(folderPath)
if err != nil {
return err
}
// If we call Mkdir with srcStat.Mode(), we won't get the expected perms because of umask
// So, we create the folder with 0700, and then change the perms to the srcStat.Mode()
err = dstFs.Mkdir(folderPath, 0o700)
if err != nil {
return err
}
return dstFs.Chmod(folderPath, srcStat.Mode())
}
func restoreFile(method fs.CopyRangeMethod, src, dst fs.Filesystem, filePath string, versionTime time.Time, tagger fileTagger) error {
tag := versionTime.In(time.Local).Truncate(time.Second).Format(TimeFormat)
taggedFilePath := tagger(filePath, tag)
@@ -295,7 +339,7 @@ func findAllVersions(fs fs.Filesystem, filePath string) []string {
return nil
}
versions = stringutil.UniqueTrimmedStrings(versions)
sort.Strings(versions)
slices.Sort(versions)
return versions
}

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STDISCOSRV" "1" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "STDISCOSRV" "1" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STRELAYSRV" "1" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "STRELAYSRV" "1" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-BEP" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-BEP" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS
@@ -61,7 +61,7 @@ level protocols providing encryption and authentication.
+\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-+
| Block Exchange Protocol |
|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|
| Encryption & Auth (TLS 1.2) |
| Encryption & Auth (TLS 1.3) |
|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|
| Reliable Transport |
|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-|
@@ -70,13 +70,8 @@ v ... v
.UNINDENT
.UNINDENT
.sp
The encryption and authentication layer SHALL use TLS 1.2 or a higher
revision. A strong cipher suite SHALL be used, with “strong cipher
suite” being defined as being without known weaknesses and providing
Perfect Forward Secrecy (PFS). Examples of strong cipher suites are
given at the end of this document. This is not to be taken as an
exhaustive list of allowed cipher suites but represents best practices
at the time of writing.
The encryption and authentication layer SHALL use TLS 1.3 or a higher
revision.
.sp
The exact nature of the authentication is up to the application, however
it SHALL be based on the TLS certificate presented at the start of the
@@ -1054,82 +1049,6 @@ retrieves the data from the folder and transmits Response records (9 through
message (13). Both peers enter idle state after 13. At some later time 14,
the ping timer on device B expires and a Ping message is sent. The same
process occurs for device A at 15.
.SH EXAMPLES OF STRONG CIPHER SUITES
.TS
box center;
l|l|l.
T{
ID
T} T{
Name
T} T{
Description
T}
_
T{
0x009F
T} T{
DHE\-RSA\-AES256\-GCM\-SHA384
T} T{
TLSv1.2 DH RSA AESGCM(256) AEAD
T}
_
T{
0x006B
T} T{
DHE\-RSA\-AES256\-SHA256
T} T{
TLSv1.2 DH RSA AES(256) SHA256
T}
_
T{
0xC030
T} T{
ECDHE\-RSA\-AES256\-GCM\-SHA384
T} T{
TLSv1.2 ECDH RSA AESGCM(256) AEAD
T}
_
T{
0xC028
T} T{
ECDHE\-RSA\-AES256\-SHA384
T} T{
TLSv1.2 ECDH RSA AES(256) SHA384
T}
_
T{
0x009E
T} T{
DHE\-RSA\-AES128\-GCM\-SHA256
T} T{
TLSv1.2 DH RSA AESGCM(128) AEAD
T}
_
T{
0x0067
T} T{
DHE\-RSA\-AES128\-SHA256
T} T{
TLSv1.2 DH RSA AES(128) SHA256
T}
_
T{
0xC02F
T} T{
ECDHE\-RSA\-AES128\-GCM\-SHA256
T} T{
TLSv1.2 ECDH RSA AESGCM(128) AEAD
T}
_
T{
0xC027
T} T{
ECDHE\-RSA\-AES128\-SHA256
T} T{
TLSv1.2 ECDH RSA AES(128) SHA256
T}
.TE
.SH AUTHOR
The Syncthing Authors
.SH COPYRIGHT

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-CONFIG" "5" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH SYNOPSIS
@@ -567,8 +567,8 @@ change this unless you know what youre doing.
.TP
.B maxConflicts
The maximum number of conflict copies to keep around for any given file.
The default, \fB\-1\fP, means an unlimited number. Setting this to \fB0\fP disables
conflict copies altogether.
The default is \fB10\fP\&. \fB\-1\fP, means an unlimited number.
Setting this to \fB0\fP disables conflict copies altogether.
.UNINDENT
.INDENT 0.0
.TP
@@ -1432,8 +1432,9 @@ ECN bits into account.
.INDENT 0.0
.TP
.B stunServer
Server to be used for STUN, given as ip:port. The keyword \fBdefault\fP gets
expanded to a set of public STUN servers.
Server to use for STUN, given as ip:port. The keyword \fBdefault\fP gets
expanded to a set of public STUN servers, with preference given to those
hosted by the Syncthing project.
.sp
To configure multiple servers, you can either: repeat \fB<stunServer>\fP tags
in the configuration file or enter several servers separated by commas in

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-DEVICE-IDS" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-EVENT-API" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION
@@ -845,9 +845,7 @@ Deprecated since version v1.10.0: The \fBversion\fP field is a legacy name kept
.sp
When authentication is enabled for the GUI, this event is emitted on every
login attempt. If either the username or password are incorrect, \fBsuccess\fP
is false and in any case the given username is returned. The included
remote address concerns the immediate connecting host, which may not be the
origin of the request, but e.g. a reverse proxy.
is false and in any case the given username is returned.
.INDENT 0.0
.INDENT 3.5
.sp
@@ -856,7 +854,32 @@ origin of the request, but e.g. a reverse proxy.
\(dqid\(dq : 187,
\(dqtime\(dq : \(dq2017\-03\-07T00:19:24.420386143+01:00\(dq,
\(dqdata\(dq : {
\(dqremoteAddress\(dq : \(dq127.0.0.1:55530\(dq,
\(dqremoteAddress\(dq : \(dq127.0.0.1\(dq,
\(dqusername\(dq : \(dqsomename\(dq,
\(dqsuccess\(dq : false
},
\(dqtype\(dq : \(dqLoginAttempt\(dq,
\(dqglobalID\(dq : 195
}
.EE
.UNINDENT
.UNINDENT
.sp
If the \fBX\-Forwared\-For\fP header is present and the connecting host is
either on \fBlocalhost\fP or on the same LAN, it will be treated as a reverse
proxy. In this case, the \fBremoteAddress\fP field is filled with the leftmost
IP address from the header, and the additional \fBproxy\fP field retains the
original IP of the connecting host.
.INDENT 0.0
.INDENT 3.5
.sp
.EX
{
\(dqid\(dq : 187,
\(dqtime\(dq : \(dq2017\-03\-07T00:19:24.420386143+01:00\(dq,
\(dqdata\(dq : {
\(dqproxy\(dq : \(dq127.0.0.1\(dq,
\(dqremoteAddress\(dq : \(dq192.168.178.10\(dq,
\(dqusername\(dq : \(dqsomename\(dq,
\(dqsuccess\(dq : false
},

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-FAQ" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0
@@ -158,6 +158,16 @@ Extended attributes (when enabled)
POSIX or NFS ACLs (as part of extended attributes)
.UNINDENT
.sp
\fBNOTE:\fP
.INDENT 0.0
.INDENT 3.5
Note that what can be synced often depends on the operating system, file
system, and possibly other factors. For example, even though Syncthing may
try to synchronise symbolic links on Android, this will not succeed, as the
OS does not support them on the user storage.
.UNINDENT
.UNINDENT
.sp
The following are \fInot\fP synchronized;
.INDENT 0.0
.IP \(bu 2

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-GLOBALDISCO" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-LOCALDISCO" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-NETWORKING" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP

View File

@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-RELAY" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-REST-API" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-SECURITY" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-STIGNORE" "5" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-VERSIONING" "7" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp

View File

@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING" "1" "Apr 26, 2025" "v1.29.3" "Syncthing"
.TH "SYNCTHING" "1" "May 25, 2025" "v1.29.6" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS

View File

@@ -1,118 +0,0 @@
// Copyright (C) 2017 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
package meta
import (
"bytes"
"log"
"os/exec"
"strings"
"testing"
)
var (
// fast linters complete in a fraction of a second and might as well be
// run always as part of the build
fastLinters = []string{
"deadcode",
"golint",
"ineffassign",
"vet",
}
// slow linters take several seconds and are run only as part of the
// "metalint" command.
slowLinters = []string{
"gosimple",
"staticcheck",
"structcheck",
"unused",
"varcheck",
}
// Which parts of the tree to lint
lintDirs = []string{
".",
"../cmd/...",
"../lib/...",
"../script/...",
}
// Messages to ignore
lintExcludes = []string{
".pb.go",
"should have comment",
"protocol.Vector composite literal uses unkeyed fields",
"cli.Requires composite literal uses unkeyed fields",
"Use DialContext instead", // Go 1.7
"os.SEEK_SET is deprecated", // Go 1.7
"SA4017", // staticcheck "is a pure function but its return value is ignored"
}
)
func TestCheckMetalint(t *testing.T) {
if !isGometalinterInstalled() {
return
}
gometalinter(t, lintDirs, lintExcludes...)
}
func isGometalinterInstalled() bool {
if _, err := runError("gometalinter", "--disable-all"); err != nil {
log.Println("gometalinter is not installed")
return false
}
return true
}
func gometalinter(_ *testing.T, dirs []string, excludes ...string) bool {
params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
for _, linter := range fastLinters {
params = append(params, "--enable="+linter)
}
if !testing.Short() {
for _, linter := range slowLinters {
params = append(params, "--enable="+linter)
}
}
for _, exclude := range excludes {
params = append(params, "--exclude="+exclude)
}
params = append(params, dirs...)
bs, _ := runError("gometalinter", params...)
nerr := 0
lines := make(map[string]struct{})
for _, line := range strings.Split(string(bs), "\n") {
if line == "" {
continue
}
if _, ok := lines[line]; ok {
continue
}
log.Println(line)
if strings.Contains(line, "executable file not found") {
log.Println(` - Try "go run build.go setup" to install missing tools`)
}
lines[line] = struct{}{}
nerr++
}
return nerr == 0
}
func runError(cmd string, args ...string) ([]byte, error) {
ecmd := exec.Command(cmd, args...)
bs, err := ecmd.CombinedOutput()
return bytes.TrimSpace(bs), err
}

21
relnotes/README.md Normal file
View File

@@ -0,0 +1,21 @@
# Release Notes
Files in this directory constitute manual release notes for a given release.
When relevant, they should be created prior to that release so that they can
be included in the corresponding tag message, etc.
To add release notes for a release 1.2.3, create a file named `v1.2.3.md`
consisting of an initial H2-level header and further notes as desired. For
example:
```
## Major changes in v1.2.3
- Files are now synchronized twice as fast on Tuesdays
```
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.

8
relnotes/v1.md Normal file
View File

@@ -0,0 +1,8 @@
## Syncthing 2 is coming
Syncthing version 1.x will soon be replaced by Syncthing version 2.x.
Version 2 brings a new database format and various cleanups, but remains
protocol compatible with Syncthing 1.
More detailed information about Syncthing 2 can be found in the release
notes at https://github.com/syncthing/syncthing/releases.

View File

@@ -14,6 +14,7 @@ package main
import (
"bytes"
"cmp"
"fmt"
"io"
"log"
@@ -21,7 +22,7 @@ import (
"os"
"os/exec"
"regexp"
"sort"
"slices"
"strings"
)
@@ -102,7 +103,17 @@ func main() {
// Write author names in GUI about modal
getContributions(authors)
sort.Sort(byContributions(authors))
// Sort by contributions
slices.SortFunc(authors, func(a, b author) int {
// Sort first by log10(commits), then by name. This means that we first get
// an alphabetic list of people with >= 1000 commits, then a list of people
// with >= 100 commits, and so on.
if a.log10commits != b.log10commits {
return cmp.Compare(b.log10commits, a.log10commits)
}
return strings.Compare(a.name, b.name)
})
var lines []string
for _, author := range authors {
@@ -125,7 +136,10 @@ func main() {
// Write AUTHORS file
sort.Sort(byName(authors))
// 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 {
@@ -298,34 +312,6 @@ func allAuthors() map[string]string {
return names
}
type byContributions []author
func (l byContributions) Len() int { return len(l) }
// Sort first by log10(commits), then by name. This means that we first get
// an alphabetic list of people with >= 1000 commits, then a list of people
// with >= 100 commits, and so on.
func (l byContributions) Less(a, b int) bool {
if l[a].log10commits != l[b].log10commits {
return l[a].log10commits > l[b].log10commits
}
return l[a].name < l[b].name
}
func (l byContributions) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
type byName []author
func (l byName) Len() int { return len(l) }
func (l byName) Less(a, b int) bool {
aname := strings.ToLower(l[a].name)
bname := strings.ToLower(l[b].name)
return aname < bname
}
func (l byName) Swap(a, b int) { l[a], l[b] = l[b], l[a] }
// A simple string set type
type stringSet map[string]struct{}

123
script/relnotes.go Normal file
View File

@@ -0,0 +1,123 @@
// Copyright (C) 2025 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
//go:build ignore
// +build ignore
package main
import (
"bytes"
"cmp"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"regexp"
"strings"
)
var (
githubToken = os.Getenv("GITHUB_TOKEN")
githubRepo = cmp.Or(os.Getenv("GITHUB_REPOSITORY"), "syncthing/syncthing")
)
func main() {
ver := flag.String("new-ver", "", "New version tag")
prevVer := flag.String("prev-ver", "", "Previous version tag")
branch := flag.String("branch", "HEAD", "Branch to release from")
flag.Parse()
log.SetOutput(os.Stderr)
if *ver == "" {
log.Fatalln("Must set --new-ver")
}
if githubToken == "" {
log.Fatalln("Must set $GITHUB_TOKEN")
}
notes, err := additionalNotes(*ver)
if err != nil {
log.Fatalln("Gathering additional notes:", err)
}
gh, err := generatedNotes(*ver, *branch, *prevVer)
if err != nil {
log.Fatalln("Gathering github notes:", err)
}
notes = append(notes, gh)
fmt.Println(strings.Join(notes, "\n\n"))
}
// Load potential additional release notes from within the repo
func additionalNotes(newVer string) ([]string, error) {
var notes []string
ver, _, _ := strings.Cut(newVer, "-")
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 notes, nil
}
// Load generated release notes (list of pull requests and contributors)
// from GitHub.
func generatedNotes(newVer, targetCommit, prevVer string) (string, error) {
fields := map[string]string{
"tag_name": newVer,
"target_commitish": targetCommit,
"previous_tag_name": prevVer,
}
bs, err := json.Marshal(fields)
if err != nil {
return "", err
}
req, err := http.NewRequest(http.MethodPost, "https://api.github.com/repos/"+githubRepo+"/releases/generate-notes", bytes.NewReader(bs)) //nolint:noctx
if err != nil {
return "", err
}
req.Header.Set("Accept", "application/vnd.github+json")
req.Header.Set("Authorization", "Bearer "+githubToken)
req.Header.Set("X-Github-Api-Version", "2022-11-28")
res, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
if res.StatusCode != http.StatusOK {
bs, _ := io.ReadAll(res.Body)
log.Print(string(bs))
return "", errors.New(res.Status) //nolint:err113
}
defer res.Body.Close()
var resJSON struct {
Body string
}
if err := json.NewDecoder(res.Body).Decode(&resJSON); err != nil {
return "", err
}
return strings.TrimSpace(removeHTMLComments(resJSON.Body)), nil
}
func removeHTMLComments(s string) string {
return regexp.MustCompile(`<!--.*?-->`).ReplaceAllString(s, "")
}

View File

@@ -17,7 +17,7 @@ import (
"net/http"
"os"
"regexp"
"sort"
"slices"
"strings"
)
@@ -93,7 +93,7 @@ func main() {
}
func saveValidLangs(langs []string) {
sort.Strings(langs)
slices.Sort(langs)
fd, err := os.Create("valid-langs.js")
if err != nil {
log.Fatal(err)

View File

@@ -17,7 +17,7 @@ import (
"net/http"
"os"
"regexp"
"sort"
"slices"
"strings"
)
@@ -116,7 +116,7 @@ func reformatLanguageCode(origCode string) string {
}
func saveValidLangs(langs []string) {
sort.Strings(langs)
slices.Sort(langs)
fd, err := os.Create("valid-langs.js")
if err != nil {
log.Fatal(err)

View File

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