mirror of
https://github.com/syncthing/syncthing.git
synced 2025-12-24 06:28:10 -05:00
Compare commits
46 Commits
v2.0.0-rc.
...
v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0945304a79 | ||
|
|
9703dd9f57 | ||
|
|
259e9ef08e | ||
|
|
6a0c6128d8 | ||
|
|
b05ece0681 | ||
|
|
e9133ef82b | ||
|
|
67ba20d777 | ||
|
|
21da0d7890 | ||
|
|
ebbe57d0ab | ||
|
|
f4abc71dcc | ||
|
|
8aa02da93a | ||
|
|
0e560486db | ||
|
|
57d413099d | ||
|
|
1fdf07933c | ||
|
|
c50678618f | ||
|
|
8094b459e4 | ||
|
|
6765867a2e | ||
|
|
4fb8ee6a6f | ||
|
|
674834ccf4 | ||
|
|
3bd2bff23b | ||
|
|
40660c5fb7 | ||
|
|
d940d094a1 | ||
|
|
9d67727989 | ||
|
|
6f51700a7f | ||
|
|
598915193a | ||
|
|
905e5ec07f | ||
|
|
4075b886d0 | ||
|
|
cade790198 | ||
|
|
98555a9a80 | ||
|
|
48b757cac1 | ||
|
|
58c85fc9db | ||
|
|
ddd98a818a | ||
|
|
64b5a1b738 | ||
|
|
1a131a56f2 | ||
|
|
beda37f28b | ||
|
|
2532ac35cf | ||
|
|
bcd30ceaec | ||
|
|
9a3493c2f4 | ||
|
|
fa404d5a0d | ||
|
|
73ad18fbfb | ||
|
|
1dd264894a | ||
|
|
8c3d2f3bc5 | ||
|
|
702ed8ecc1 | ||
|
|
b038650810 | ||
|
|
a16bf555c0 | ||
|
|
cd6ea60fa1 |
1
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
1
.github/ISSUE_TEMPLATE/01-feature.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Feature request
|
||||
description: File a new feature request
|
||||
labels: ["enhancement", "needs-triage"]
|
||||
type: Feature
|
||||
body:
|
||||
|
||||
- type: textarea
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/02-bug.yml
vendored
1
.github/ISSUE_TEMPLATE/02-bug.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: Bug report
|
||||
description: If you're actually looking for support instead, see "I need help / I have a question".
|
||||
labels: ["bug", "needs-triage"]
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
23
.github/labeler.yml
vendored
Normal file
23
.github/labeler.yml
vendored
Normal 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
17
.github/release.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
changelog:
|
||||
exclude:
|
||||
labels:
|
||||
- dependencies
|
||||
|
||||
categories:
|
||||
- title: Fixes
|
||||
labels:
|
||||
- bug
|
||||
|
||||
- title: Features
|
||||
labels:
|
||||
- enhancement
|
||||
|
||||
- title: Other
|
||||
labels:
|
||||
- '*'
|
||||
82
.github/workflows/build-syncthing.yaml
vendored
82
.github/workflows/build-syncthing.yaml
vendored
@@ -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
|
||||
@@ -734,7 +716,7 @@ jobs:
|
||||
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
|
||||
RCLONE_CONFIG_OBJSTORE_ACL: public-read
|
||||
with:
|
||||
args: sync -v packages objstore:nightly
|
||||
args: sync -v --no-update-modtime packages objstore:nightly
|
||||
|
||||
#
|
||||
# Push release artifacts to Spaces
|
||||
@@ -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
|
||||
@@ -788,7 +772,7 @@ jobs:
|
||||
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
|
||||
RCLONE_CONFIG_OBJSTORE_ACL: public-read
|
||||
with:
|
||||
args: sync -v packages objstore:release/${{ env.VERSION }}
|
||||
args: sync -v --no-update-modtime packages objstore:release/${{ env.VERSION }}
|
||||
|
||||
- name: Push to object store (latest)
|
||||
uses: docker://docker.io/rclone/rclone:latest
|
||||
@@ -801,7 +785,43 @@ jobs:
|
||||
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
|
||||
RCLONE_CONFIG_OBJSTORE_ACL: public-read
|
||||
with:
|
||||
args: sync -v objstore:release/${{ env.VERSION }} objstore:release/latest
|
||||
args: sync -v --no-update-modtime objstore:release/${{ env.VERSION }} objstore:release/latest
|
||||
|
||||
- name: Create GitHub releases 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 --clobber "$VERSION" \
|
||||
packages/*.asc packages/*.json \
|
||||
packages/syncthing-*.tar.gz \
|
||||
packages/syncthing-*.zip \
|
||||
packages/syncthing_*.deb
|
||||
|
||||
PKGS=$(pwd)/packages
|
||||
cd /tmp # gh will not release for repo x while inside repo y
|
||||
for repo in relaysrv discosrv ; do
|
||||
export GH_REPO="syncthing/$repo"
|
||||
if ! gh release view --json name "$VERSION" >/dev/null 2>&1 ; then
|
||||
gh release create "$VERSION" \
|
||||
$maybePrerelease \
|
||||
--title "$VERSION" \
|
||||
--notes "https://github.com/syncthing/syncthing/releases/tag/$VERSION"
|
||||
fi
|
||||
gh release upload --clobber "$VERSION" \
|
||||
$PKGS/*.asc \
|
||||
$PKGS/*${repo}*
|
||||
done
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
|
||||
#
|
||||
# Push Debian/APT archive
|
||||
@@ -809,7 +829,7 @@ jobs:
|
||||
|
||||
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
|
||||
@@ -879,7 +899,7 @@ jobs:
|
||||
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
|
||||
RCLONE_CONFIG_OBJSTORE_ACL: public-read
|
||||
with:
|
||||
args: sync -v dists objstore:apt/dists
|
||||
args: sync -v --no-update-modtime dists objstore:apt/dists
|
||||
|
||||
#
|
||||
# Build and push to Docker Hub
|
||||
@@ -888,7 +908,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
49
.github/workflows/pr-linters.yaml
vendored
Normal 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
|
||||
27
.github/workflows/pr-metadata.yaml
vendored
Normal file
27
.github/workflows/pr-metadata.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: PR metadata
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
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 }}"
|
||||
60
.github/workflows/release-syncthing.yaml
vendored
Normal file
60
.github/workflows/release-syncthing.yaml
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
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.ACTIONS_GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: stable
|
||||
|
||||
- name: Determine version to release
|
||||
run: |
|
||||
if [[ "$GITHUB_REF_NAME" == "release" ]] ; then
|
||||
next=$(go run ./script/next-version.go)
|
||||
else
|
||||
next=$(go run ./script/next-version.go --pre)
|
||||
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.ACTIONS_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"
|
||||
|
||||
- name: Trigger the build
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
with:
|
||||
workflow: build-syncthing.yaml
|
||||
ref: refs/tags/${{ env.NEXT }}
|
||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
||||
@@ -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$
|
||||
|
||||
5
AUTHORS
5
AUTHORS
@@ -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>
|
||||
@@ -221,6 +224,7 @@ luzpaz <luzpaz@users.noreply.github.com>
|
||||
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
||||
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
|
||||
Marc Pujol (kilburn) <kilburn@la3.org>
|
||||
Marcel Meyer <mm.marcelmeyer@gmail.com>
|
||||
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
|
||||
marco-m <marco.molteni@laposte.net>
|
||||
Marcus B Spencer <marcus@marcusspencer.xyz> <marcus@marcusspencer.us>
|
||||
@@ -293,6 +297,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>
|
||||
|
||||
6
build.go
6
build.go
@@ -330,7 +330,7 @@ func runCommand(cmd string, target target) {
|
||||
writeCompatJSON()
|
||||
|
||||
case "deb":
|
||||
buildDeb(target)
|
||||
buildDeb(target, tags)
|
||||
|
||||
case "vet":
|
||||
metalintShort()
|
||||
@@ -609,7 +609,7 @@ func buildZip(target target, tags []string) {
|
||||
fmt.Println(filename)
|
||||
}
|
||||
|
||||
func buildDeb(target target) {
|
||||
func buildDeb(target target, tags []string) {
|
||||
os.RemoveAll("deb")
|
||||
|
||||
// "goarch" here is set to whatever the Debian packages expect. We correct
|
||||
@@ -623,7 +623,7 @@ func buildDeb(target target) {
|
||||
goarch = "arm"
|
||||
}
|
||||
|
||||
build(target, []string{"noupgrade"})
|
||||
build(target, append(tags, "noupgrade"))
|
||||
|
||||
for i := range target.installationFiles {
|
||||
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -201,17 +201,21 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
// looking for a prerelease at all.
|
||||
func filterForLatest(rels []upgrade.Release) []upgrade.Release {
|
||||
var filtered []upgrade.Release
|
||||
var havePre bool
|
||||
havePre := make(map[string]bool)
|
||||
haveStable := make(map[string]bool)
|
||||
for _, rel := range rels {
|
||||
if !rel.Prerelease {
|
||||
// We found a stable version, we're good now.
|
||||
major, _, _ := strings.Cut(rel.Tag, ".")
|
||||
if !rel.Prerelease && !haveStable[major] {
|
||||
// Remember the first non-pre for each major
|
||||
filtered = append(filtered, rel)
|
||||
break
|
||||
haveStable[major] = true
|
||||
continue
|
||||
}
|
||||
if rel.Prerelease && !havePre {
|
||||
// We remember the first prerelease we find.
|
||||
if rel.Prerelease && !havePre[major] && !haveStable[major] {
|
||||
// We remember the first prerelease we find, unless we've
|
||||
// already found a non-pre of the same major.
|
||||
filtered = append(filtered, rel)
|
||||
havePre = true
|
||||
havePre[major] = true
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -576,6 +578,7 @@ func syncthingMain(options serveOptions) {
|
||||
os.Exit(svcutil.ExitError.AsInt())
|
||||
}
|
||||
earlyService.Add(cfgWrapper)
|
||||
config.RegisterInfoMetrics(cfgWrapper)
|
||||
|
||||
// Candidate builds should auto upgrade. Make sure the option is set,
|
||||
// unless we are in a build where it's disabled or the STNOUPGRADE
|
||||
|
||||
@@ -238,19 +238,18 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
|
||||
return
|
||||
}
|
||||
|
||||
if panicFd == nil {
|
||||
dst.Write([]byte(line))
|
||||
dst.Write([]byte(line))
|
||||
|
||||
if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") {
|
||||
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
||||
if err != nil {
|
||||
l.Warnln("Create panic log:", err)
|
||||
continue
|
||||
}
|
||||
if panicFd == nil && (strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:")) {
|
||||
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
|
||||
if err != nil {
|
||||
l.Warnln("Create panic log:", err)
|
||||
continue
|
||||
}
|
||||
|
||||
l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
|
||||
if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") {
|
||||
l.Warnln(`
|
||||
l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
|
||||
if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") {
|
||||
l.Warnln(`
|
||||
*********************************************************************************
|
||||
* Crash due to corrupt database. *
|
||||
* *
|
||||
@@ -263,22 +262,21 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
|
||||
* https://docs.syncthing.net/users/faq.html#my-syncthing-database-is-corrupt *
|
||||
*********************************************************************************
|
||||
`)
|
||||
} else {
|
||||
l.Warnln("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
|
||||
l.Warnln("If no issue with similar panic message exists, please create a new issue with the panic log attached")
|
||||
}
|
||||
|
||||
stdoutMut.Lock()
|
||||
for _, line := range stdoutFirstLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
panicFd.WriteString("...\n")
|
||||
for _, line := range stdoutLastLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
stdoutMut.Unlock()
|
||||
} else {
|
||||
l.Warnln("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/")
|
||||
l.Warnln("If no issue with similar panic message exists, please create a new issue with the panic log attached")
|
||||
}
|
||||
|
||||
stdoutMut.Lock()
|
||||
for _, line := range stdoutFirstLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
panicFd.WriteString("...\n")
|
||||
for _, line := range stdoutLastLines {
|
||||
panicFd.WriteString(line)
|
||||
}
|
||||
stdoutMut.Unlock()
|
||||
|
||||
panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
|
||||
}
|
||||
|
||||
|
||||
45
go.mod
45
go.mod
@@ -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,55 +28,55 @@ 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
|
||||
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // 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 +92,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
|
||||
)
|
||||
|
||||
|
||||
168
go.sum
168
go.sum
@@ -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=
|
||||
@@ -46,6 +46,8 @@ github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dg
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
|
||||
@@ -53,8 +55,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 +65,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 +79,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 +101,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 +113,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 +145,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 +175,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 +202,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 +212,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 +226,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 +238,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 +260,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 +310,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=
|
||||
|
||||
@@ -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": "إصدارات نظيفة",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"Allowed Networks": "Mga Pinapayagang Network",
|
||||
"Alphabetic": "Alpabetiko",
|
||||
"Altered by ignoring deletes.": "Binago sa pamamagitan ng hindi pagpansin sa mga pagtanggal.",
|
||||
"Always turned on when the folder type is \"{%foldertype%}\".": "Palaging nakabukas kung ang uri ng folder ay nakatakda bilang \"{{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.": "Pinapamahala ng external na command ang file versioning. Kailangan nitong tanggalin ang file mula sa binabahaging folder. Kung may mga space ang path sa application, kailangan itong i-quote.",
|
||||
"Anonymous Usage Reporting": "Anonymous na Pag-uulat ng Paggamit",
|
||||
"Anonymous usage report format has changed. Would you like to move to the new format?": "Nagbago ang pormat ng anonymous na ulat ng paggamit. Gusto mo bang lumipat sa bagong pormat?",
|
||||
@@ -52,6 +53,7 @@
|
||||
"Body:": "Body:",
|
||||
"Bugs": "Mga Bug",
|
||||
"Cancel": "Kanselahin",
|
||||
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Hindi maaaring paganahin kapag ang uri ng folder ay \"{{foldertype}}\".",
|
||||
"Changelog": "Mga Pagbabago",
|
||||
"Clean out after": "Linisin pagkatapos",
|
||||
"Cleaning Versions": "Mga Bersyon ng Paglinis",
|
||||
@@ -311,7 +313,7 @@
|
||||
"Receive Encrypted": "Makatanggap Naka-Encrypt",
|
||||
"Receive Only": "Makatanggap Lamang",
|
||||
"Received data is already encrypted": "Naka-encrypt na ang natanggap na data",
|
||||
"Recent Changes": "Mga Kamakilang Pagbabago",
|
||||
"Recent Changes": "Mga Kamakailang Pagbabago",
|
||||
"Reduced by ignore patterns": "Binabawasan ng mga ignore pattern",
|
||||
"Relay LAN": "Relay na LAN",
|
||||
"Relay WAN": "Relay na WAN",
|
||||
|
||||
@@ -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?",
|
||||
|
||||
@@ -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": "Вечно",
|
||||
|
||||
36
gui/default/assets/lang/lang-sr.json
Normal file
36
gui/default/assets/lang/lang-sr.json
Normal 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": "Примени"
|
||||
}
|
||||
@@ -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, Marcel Meyer, 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 © 2013, Julien Schmidt.</li>
|
||||
<li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright © 2014 Kevin Ballard.</li>
|
||||
<li><a href="https://github.com/klauspost/compress">klauspost/compress</a>, Copyright © 2012 The Go Authors.</li>
|
||||
<li><a href="https://github.com/miscreant/miscreant.go">miscreant/miscreant.go</a>, Copyright © 2017-2019 The Miscreant Developers.</li>
|
||||
<li><a href="https://github.com/munnerz/goautoneg">munnerz/goautoneg</a>, Copyright © 2011, Open Knowledge Foundation Ltd.</li>
|
||||
<li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright © 2015 Pierre Curto.</li>
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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] {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
62
lib/config/metrics.go
Normal file
62
lib/config/metrics.go
Normal file
@@ -0,0 +1,62 @@
|
||||
// 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/.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// RegisterInfoMetrics registers Prometheus metrics for the given config
|
||||
// wrapper.
|
||||
func RegisterInfoMetrics(cfg Wrapper) {
|
||||
prometheus.DefaultRegisterer.MustRegister(prometheus.CollectorFunc((&folderInfoMetric{cfg}).Collect))
|
||||
prometheus.DefaultRegisterer.MustRegister(prometheus.CollectorFunc((&folderDeviceMetric{cfg}).Collect))
|
||||
}
|
||||
|
||||
type folderInfoMetric struct {
|
||||
cfg Wrapper
|
||||
}
|
||||
|
||||
var folderInfoMetricDesc = prometheus.NewDesc(
|
||||
"syncthing_config_folder_info",
|
||||
"Provides additional information labels on folders",
|
||||
[]string{"folder", "label", "type", "path", "paused"},
|
||||
nil,
|
||||
)
|
||||
|
||||
func (m *folderInfoMetric) Collect(ch chan<- prometheus.Metric) {
|
||||
for _, folder := range m.cfg.FolderList() {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
folderInfoMetricDesc,
|
||||
prometheus.GaugeValue, 1,
|
||||
folder.ID, folder.Label, folder.Type.String(), folder.Path, strconv.FormatBool(folder.Paused),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type folderDeviceMetric struct {
|
||||
cfg Wrapper
|
||||
}
|
||||
|
||||
var folderDeviceMetricDesc = prometheus.NewDesc(
|
||||
"syncthing_config_device_info",
|
||||
"Provides additional information labels on devices",
|
||||
[]string{"device", "name", "introducer", "paused", "untrusted"},
|
||||
nil,
|
||||
)
|
||||
|
||||
func (m *folderDeviceMetric) Collect(ch chan<- prometheus.Metric) {
|
||||
for _, device := range m.cfg.DeviceList() {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
folderDeviceMetricDesc,
|
||||
prometheus.GaugeValue, 1,
|
||||
device.DeviceID.String(), device.Name, strconv.FormatBool(device.Introducer), strconv.FormatBool(device.Paused), strconv.FormatBool(device.Untrusted),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"net"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
stdsync "sync"
|
||||
"time"
|
||||
@@ -445,7 +444,7 @@ func (s *service) handleHellos(ctx context.Context) error {
|
||||
// connections are limited.
|
||||
rd, wr := s.limiter.getLimiters(remoteID, c, c.IsLocal())
|
||||
|
||||
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression.ToProtocol(), s.cfg.FolderPasswords(remoteID), s.keyGen)
|
||||
protoConn := protocol.NewConnection(remoteID, rd, wr, c, s.model, c, deviceCfg.Compression.ToProtocol(), s.keyGen)
|
||||
s.accountAddedConnection(protoConn, hello, s.cfg.Options().ConnectionPriorityUpgradeThreshold)
|
||||
go func() {
|
||||
<-protoConn.Closed()
|
||||
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -31,17 +31,17 @@ func TestIndexhandlerConcurrency(t *testing.T) {
|
||||
ci := &protomock.ConnectionInfo{}
|
||||
|
||||
m1 := &mocks.Model{}
|
||||
c1 := protocol.NewConnection(protocol.EmptyDeviceID, ar, bw, testutil.NoopCloser{}, m1, ci, protocol.CompressionNever, nil, nil)
|
||||
c1 := protocol.NewConnection(protocol.EmptyDeviceID, ar, bw, testutil.NoopCloser{}, m1, ci, protocol.CompressionNever, nil)
|
||||
c1.Start()
|
||||
defer c1.Close(io.EOF)
|
||||
|
||||
m2 := &mocks.Model{}
|
||||
c2 := protocol.NewConnection(protocol.EmptyDeviceID, br, aw, testutil.NoopCloser{}, m2, ci, protocol.CompressionNever, nil, nil)
|
||||
c2 := protocol.NewConnection(protocol.EmptyDeviceID, br, aw, testutil.NoopCloser{}, m2, ci, protocol.CompressionNever, nil)
|
||||
c2.Start()
|
||||
defer c2.Close(io.EOF)
|
||||
|
||||
c1.ClusterConfig(&protocol.ClusterConfig{})
|
||||
c2.ClusterConfig(&protocol.ClusterConfig{})
|
||||
c1.ClusterConfig(&protocol.ClusterConfig{}, nil)
|
||||
c2.ClusterConfig(&protocol.ClusterConfig{}, nil)
|
||||
c1.Index(ctx, &protocol.Index{Folder: "foo"})
|
||||
c2.Index(ctx, &protocol.Index{Folder: "foo"})
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
stdsync "sync"
|
||||
"sync/atomic"
|
||||
@@ -1628,8 +1629,7 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) {
|
||||
// Generating cluster-configs acquires the mutex.
|
||||
for _, conn := range ccConns {
|
||||
cm, passwords := m.generateClusterConfig(conn.DeviceID())
|
||||
conn.SetFolderPasswords(passwords)
|
||||
go conn.ClusterConfig(cm)
|
||||
go conn.ClusterConfig(cm, passwords)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,11 +1804,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 {
|
||||
@@ -2385,8 +2383,14 @@ func (m *model) scheduleConnectionPromotion() {
|
||||
// be called after adding new connections, and after closing a primary
|
||||
// device connection.
|
||||
func (m *model) promoteConnections() {
|
||||
// Slice of actions to take on connections after releasing the main
|
||||
// mutex. We do this so that we do not perform blocking network actions
|
||||
// inside the loop, and also to avoid a possible deadlock with calling
|
||||
// Start() on connections that are already executing a Close() with a
|
||||
// callback into the model...
|
||||
var postLockActions []func()
|
||||
|
||||
m.mut.Lock()
|
||||
defer m.mut.Unlock()
|
||||
|
||||
for deviceID, connIDs := range m.deviceConnIDs {
|
||||
cm, passwords := m.generateClusterConfigRLocked(deviceID)
|
||||
@@ -2399,11 +2403,12 @@ func (m *model) promoteConnections() {
|
||||
// on where we get ClusterConfigs from the peer.)
|
||||
conn := m.connections[connIDs[0]]
|
||||
l.Debugf("Promoting connection to %s at %s", deviceID.Short(), conn)
|
||||
if conn.Statistics().StartedAt.IsZero() {
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.Start()
|
||||
}
|
||||
conn.ClusterConfig(cm)
|
||||
postLockActions = append(postLockActions, func() {
|
||||
if conn.Statistics().StartedAt.IsZero() {
|
||||
conn.Start()
|
||||
}
|
||||
conn.ClusterConfig(cm, passwords)
|
||||
})
|
||||
m.promotedConnID[deviceID] = connIDs[0]
|
||||
}
|
||||
|
||||
@@ -2412,12 +2417,19 @@ func (m *model) promoteConnections() {
|
||||
for _, connID := range connIDs[1:] {
|
||||
conn := m.connections[connID]
|
||||
if conn.Statistics().StartedAt.IsZero() {
|
||||
conn.SetFolderPasswords(passwords)
|
||||
conn.Start()
|
||||
conn.ClusterConfig(&protocol.ClusterConfig{Secondary: true})
|
||||
postLockActions = append(postLockActions, func() {
|
||||
conn.Start()
|
||||
conn.ClusterConfig(&protocol.ClusterConfig{Secondary: true}, passwords)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m.mut.Unlock()
|
||||
|
||||
for _, action := range postLockActions {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) DownloadProgress(conn protocol.Connection, p *protocol.DownloadProgress) error {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
@@ -2973,7 +2969,7 @@ func TestConnCloseOnRestart(t *testing.T) {
|
||||
nw := &testutil.NoopRW{}
|
||||
ci := &protocolmocks.ConnectionInfo{}
|
||||
ci.ConnectionIDReturns(srand.String(16))
|
||||
m.AddConnection(protocol.NewConnection(device1, br, nw, testutil.NoopCloser{}, m, ci, protocol.CompressionNever, nil, m.keyGen), protocol.Hello{})
|
||||
m.AddConnection(protocol.NewConnection(device1, br, nw, testutil.NoopCloser{}, m, ci, protocol.CompressionNever, m.keyGen), protocol.Hello{})
|
||||
m.mut.RLock()
|
||||
if len(m.closed) != 1 {
|
||||
t.Fatalf("Expected just one conn (len(m.closed) == %v)", len(m.closed))
|
||||
@@ -3632,11 +3628,11 @@ func testConfigChangeTriggersClusterConfigs(t *testing.T, expectFirst, expectSec
|
||||
cc1 := make(chan struct{}, 1)
|
||||
cc2 := make(chan struct{}, 1)
|
||||
fc1 := newFakeConnection(device1, m)
|
||||
fc1.ClusterConfigCalls(func(_ *protocol.ClusterConfig) {
|
||||
fc1.ClusterConfigCalls(func(_ *protocol.ClusterConfig, _ map[string]string) {
|
||||
cc1 <- struct{}{}
|
||||
})
|
||||
fc2 := newFakeConnection(device2, m)
|
||||
fc2.ClusterConfigCalls(func(_ *protocol.ClusterConfig) {
|
||||
fc2.ClusterConfigCalls(func(_ *protocol.ClusterConfig, _ map[string]string) {
|
||||
cc2 <- struct{}{}
|
||||
})
|
||||
m.AddConnection(fc1, protocol.Hello{})
|
||||
@@ -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
|
||||
|
||||
@@ -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] }
|
||||
|
||||
@@ -1240,7 +1240,7 @@ func TestRequestIndexSenderClusterConfigBeforeStart(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
fc.ClusterConfigCalls(func(cc *protocol.ClusterConfig) {
|
||||
fc.ClusterConfigCalls(func(cc *protocol.ClusterConfig, _ map[string]string) {
|
||||
select {
|
||||
case ccChan <- cc:
|
||||
case <-done:
|
||||
|
||||
@@ -64,14 +64,14 @@ func benchmarkRequestsTLS(b *testing.B, conn0, conn1 net.Conn) {
|
||||
|
||||
func benchmarkRequestsConnPair(b *testing.B, conn0, conn1 net.Conn) {
|
||||
// Start up Connections on them
|
||||
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
|
||||
c0 := NewConnection(LocalDeviceID, conn0, conn0, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, testKeyGen)
|
||||
c0.Start()
|
||||
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, nil, testKeyGen)
|
||||
c1 := NewConnection(LocalDeviceID, conn1, conn1, testutil.NoopCloser{}, new(fakeModel), new(mockedConnectionInfo), CompressionMetadata, testKeyGen)
|
||||
c1.Start()
|
||||
|
||||
// Satisfy the assertions in the protocol by sending an initial cluster config
|
||||
c0.ClusterConfig(&ClusterConfig{})
|
||||
c1.ClusterConfig(&ClusterConfig{})
|
||||
c0.ClusterConfig(&ClusterConfig{}, nil)
|
||||
c1.ClusterConfig(&ClusterConfig{}, nil)
|
||||
|
||||
// Report some useful stats and reset the timer for the actual test
|
||||
b.ReportAllocs()
|
||||
|
||||
@@ -849,9 +849,13 @@ func unixOwnershipEqual(a, b *UnixData) bool {
|
||||
if a == nil || b == nil {
|
||||
return false
|
||||
}
|
||||
ownerEqual := a.OwnerName == "" || b.OwnerName == "" || a.OwnerName == b.OwnerName
|
||||
groupEqual := a.GroupName == "" || b.GroupName == "" || a.GroupName == b.GroupName
|
||||
return a.UID == b.UID && a.GID == b.GID && ownerEqual && groupEqual
|
||||
if a.UID == b.UID && a.GID == b.GID {
|
||||
return true
|
||||
}
|
||||
if a.OwnerName == b.OwnerName && a.GroupName == b.GroupName {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func windowsOwnershipEqual(a, b *WindowsData) bool {
|
||||
|
||||
@@ -196,6 +196,42 @@ func TestIsEquivalent(t *testing.T) {
|
||||
b: FileInfo{Type: FileInfoTypeFile, SymlinkTarget: []byte("b")},
|
||||
eq: true,
|
||||
},
|
||||
// Unix Ownership should be the same
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
eq: true,
|
||||
},
|
||||
// ... but matching ID is enough
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1000, GID: 1000}}},
|
||||
eq: true,
|
||||
},
|
||||
// ... or matching name
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1001, GID: 1001}}},
|
||||
eq: true,
|
||||
},
|
||||
// ... or empty name
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "", GroupName: "", UID: 1000, GID: 1000}}},
|
||||
eq: true,
|
||||
},
|
||||
// ... but not different ownership
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "B", GroupName: "B", UID: 1001, GID: 1001}}},
|
||||
eq: false,
|
||||
},
|
||||
// or missing ownership
|
||||
{
|
||||
a: FileInfo{Platform: PlatformData{Unix: &UnixData{OwnerName: "A", GroupName: "A", UID: 1000, GID: 1000}}},
|
||||
b: FileInfo{Platform: PlatformData{}},
|
||||
eq: false,
|
||||
},
|
||||
}
|
||||
|
||||
if build.IsWindows {
|
||||
|
||||
@@ -50,10 +50,10 @@ type encryptedModel struct {
|
||||
keyGen *KeyGenerator
|
||||
}
|
||||
|
||||
func newEncryptedModel(model rawModel, folderKeys *folderKeyRegistry, keyGen *KeyGenerator) encryptedModel {
|
||||
func newEncryptedModel(model rawModel, keyGen *KeyGenerator) encryptedModel {
|
||||
return encryptedModel{
|
||||
model: model,
|
||||
folderKeys: folderKeys,
|
||||
folderKeys: newFolderKeyRegistry(),
|
||||
keyGen: keyGen,
|
||||
}
|
||||
}
|
||||
@@ -187,10 +187,6 @@ func (e encryptedConnection) Start() {
|
||||
e.conn.Start()
|
||||
}
|
||||
|
||||
func (e encryptedConnection) SetFolderPasswords(passwords map[string]string) {
|
||||
e.folderKeys.setPasswords(passwords)
|
||||
}
|
||||
|
||||
func (e encryptedConnection) DeviceID() DeviceID {
|
||||
return e.conn.DeviceID()
|
||||
}
|
||||
@@ -262,8 +258,9 @@ func (e encryptedConnection) DownloadProgress(ctx context.Context, dp *DownloadP
|
||||
// No need to send these
|
||||
}
|
||||
|
||||
func (e encryptedConnection) ClusterConfig(config *ClusterConfig) {
|
||||
e.conn.ClusterConfig(config)
|
||||
func (e encryptedConnection) ClusterConfig(config *ClusterConfig, passwords map[string]string) {
|
||||
e.folderKeys.setPasswords(e.keyGen, passwords)
|
||||
e.conn.ClusterConfig(config, passwords)
|
||||
}
|
||||
|
||||
func (e encryptedConnection) Close(err error) {
|
||||
@@ -680,15 +677,13 @@ func IsEncryptedParent(pathComponents []string) bool {
|
||||
}
|
||||
|
||||
type folderKeyRegistry struct {
|
||||
keyGen *KeyGenerator
|
||||
keys map[string]*[keySize]byte // folder ID -> key
|
||||
mut sync.RWMutex
|
||||
keys map[string]*[keySize]byte // folder ID -> key
|
||||
mut sync.RWMutex
|
||||
}
|
||||
|
||||
func newFolderKeyRegistry(keyGen *KeyGenerator, passwords map[string]string) *folderKeyRegistry {
|
||||
func newFolderKeyRegistry() *folderKeyRegistry {
|
||||
return &folderKeyRegistry{
|
||||
keyGen: keyGen,
|
||||
keys: keysFromPasswords(keyGen, passwords),
|
||||
keys: make(map[string]*[keySize]byte),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,8 +694,8 @@ func (r *folderKeyRegistry) get(folder string) (*[keySize]byte, bool) {
|
||||
return key, ok
|
||||
}
|
||||
|
||||
func (r *folderKeyRegistry) setPasswords(passwords map[string]string) {
|
||||
func (r *folderKeyRegistry) setPasswords(keyGen *KeyGenerator, passwords map[string]string) {
|
||||
r.mut.Lock()
|
||||
r.keys = keysFromPasswords(r.keyGen, passwords)
|
||||
r.keys = keysFromPasswords(keyGen, passwords)
|
||||
r.mut.Unlock()
|
||||
}
|
||||
|
||||
@@ -26,10 +26,11 @@ type Connection struct {
|
||||
closedReturnsOnCall map[int]struct {
|
||||
result1 <-chan struct{}
|
||||
}
|
||||
ClusterConfigStub func(*protocol.ClusterConfig)
|
||||
ClusterConfigStub func(*protocol.ClusterConfig, map[string]string)
|
||||
clusterConfigMutex sync.RWMutex
|
||||
clusterConfigArgsForCall []struct {
|
||||
arg1 *protocol.ClusterConfig
|
||||
arg2 map[string]string
|
||||
}
|
||||
ConnectionIDStub func() string
|
||||
connectionIDMutex sync.RWMutex
|
||||
@@ -145,11 +146,6 @@ type Connection struct {
|
||||
result1 []byte
|
||||
result2 error
|
||||
}
|
||||
SetFolderPasswordsStub func(map[string]string)
|
||||
setFolderPasswordsMutex sync.RWMutex
|
||||
setFolderPasswordsArgsForCall []struct {
|
||||
arg1 map[string]string
|
||||
}
|
||||
StartStub func()
|
||||
startMutex sync.RWMutex
|
||||
startArgsForCall []struct {
|
||||
@@ -283,16 +279,17 @@ func (fake *Connection) ClosedReturnsOnCall(i int, result1 <-chan struct{}) {
|
||||
}{result1}
|
||||
}
|
||||
|
||||
func (fake *Connection) ClusterConfig(arg1 *protocol.ClusterConfig) {
|
||||
func (fake *Connection) ClusterConfig(arg1 *protocol.ClusterConfig, arg2 map[string]string) {
|
||||
fake.clusterConfigMutex.Lock()
|
||||
fake.clusterConfigArgsForCall = append(fake.clusterConfigArgsForCall, struct {
|
||||
arg1 *protocol.ClusterConfig
|
||||
}{arg1})
|
||||
arg2 map[string]string
|
||||
}{arg1, arg2})
|
||||
stub := fake.ClusterConfigStub
|
||||
fake.recordInvocation("ClusterConfig", []interface{}{arg1})
|
||||
fake.recordInvocation("ClusterConfig", []interface{}{arg1, arg2})
|
||||
fake.clusterConfigMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.ClusterConfigStub(arg1)
|
||||
fake.ClusterConfigStub(arg1, arg2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,17 +299,17 @@ func (fake *Connection) ClusterConfigCallCount() int {
|
||||
return len(fake.clusterConfigArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Connection) ClusterConfigCalls(stub func(*protocol.ClusterConfig)) {
|
||||
func (fake *Connection) ClusterConfigCalls(stub func(*protocol.ClusterConfig, map[string]string)) {
|
||||
fake.clusterConfigMutex.Lock()
|
||||
defer fake.clusterConfigMutex.Unlock()
|
||||
fake.ClusterConfigStub = stub
|
||||
}
|
||||
|
||||
func (fake *Connection) ClusterConfigArgsForCall(i int) *protocol.ClusterConfig {
|
||||
func (fake *Connection) ClusterConfigArgsForCall(i int) (*protocol.ClusterConfig, map[string]string) {
|
||||
fake.clusterConfigMutex.RLock()
|
||||
defer fake.clusterConfigMutex.RUnlock()
|
||||
argsForCall := fake.clusterConfigArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
return argsForCall.arg1, argsForCall.arg2
|
||||
}
|
||||
|
||||
func (fake *Connection) ConnectionID() string {
|
||||
@@ -908,38 +905,6 @@ func (fake *Connection) RequestReturnsOnCall(i int, result1 []byte, result2 erro
|
||||
}{result1, result2}
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswords(arg1 map[string]string) {
|
||||
fake.setFolderPasswordsMutex.Lock()
|
||||
fake.setFolderPasswordsArgsForCall = append(fake.setFolderPasswordsArgsForCall, struct {
|
||||
arg1 map[string]string
|
||||
}{arg1})
|
||||
stub := fake.SetFolderPasswordsStub
|
||||
fake.recordInvocation("SetFolderPasswords", []interface{}{arg1})
|
||||
fake.setFolderPasswordsMutex.Unlock()
|
||||
if stub != nil {
|
||||
fake.SetFolderPasswordsStub(arg1)
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsCallCount() int {
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
return len(fake.setFolderPasswordsArgsForCall)
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsCalls(stub func(map[string]string)) {
|
||||
fake.setFolderPasswordsMutex.Lock()
|
||||
defer fake.setFolderPasswordsMutex.Unlock()
|
||||
fake.SetFolderPasswordsStub = stub
|
||||
}
|
||||
|
||||
func (fake *Connection) SetFolderPasswordsArgsForCall(i int) map[string]string {
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
argsForCall := fake.setFolderPasswordsArgsForCall[i]
|
||||
return argsForCall.arg1
|
||||
}
|
||||
|
||||
func (fake *Connection) Start() {
|
||||
fake.startMutex.Lock()
|
||||
fake.startArgsForCall = append(fake.startArgsForCall, struct {
|
||||
@@ -1207,8 +1172,6 @@ func (fake *Connection) Invocations() map[string][][]interface{} {
|
||||
defer fake.remoteAddrMutex.RUnlock()
|
||||
fake.requestMutex.RLock()
|
||||
defer fake.requestMutex.RUnlock()
|
||||
fake.setFolderPasswordsMutex.RLock()
|
||||
defer fake.setFolderPasswordsMutex.RUnlock()
|
||||
fake.startMutex.RLock()
|
||||
defer fake.startMutex.RUnlock()
|
||||
fake.statisticsMutex.RLock()
|
||||
|
||||
@@ -129,7 +129,9 @@ type Connection interface {
|
||||
// Send a Cluster Configuration message to the peer device. The message
|
||||
// in the parameter may be altered by the connection and should not be
|
||||
// used further by the caller.
|
||||
ClusterConfig(config *ClusterConfig)
|
||||
// For any folder that must be encrypted for the connected device, the
|
||||
// password must be provided.
|
||||
ClusterConfig(config *ClusterConfig, passwords map[string]string)
|
||||
|
||||
// Send a Download Progress message to the peer device. The message in
|
||||
// the parameter may be altered by the connection and should not be used
|
||||
@@ -137,7 +139,6 @@ type Connection interface {
|
||||
DownloadProgress(ctx context.Context, dp *DownloadProgress)
|
||||
|
||||
Start()
|
||||
SetFolderPasswords(passwords map[string]string)
|
||||
Close(err error)
|
||||
DeviceID() DeviceID
|
||||
Statistics() Statistics
|
||||
@@ -215,7 +216,7 @@ const (
|
||||
// Should not be modified in production code, just for testing.
|
||||
var CloseTimeout = 10 * time.Second
|
||||
|
||||
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, model Model, connInfo ConnectionInfo, compress Compression, passwords map[string]string, keyGen *KeyGenerator) Connection {
|
||||
func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer io.Closer, model Model, connInfo ConnectionInfo, compress Compression, keyGen *KeyGenerator) Connection {
|
||||
// We create the wrapper for the model first, as it needs to be passed
|
||||
// in at the lowest level in the stack. At the end of construction,
|
||||
// before returning, we add the connection to cwm so that it can be used
|
||||
@@ -225,7 +226,7 @@ func NewConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, closer
|
||||
// Encryption / decryption is first (outermost) before conversion to
|
||||
// native path formats.
|
||||
nm := makeNative(cwm)
|
||||
em := newEncryptedModel(nm, newFolderKeyRegistry(keyGen, passwords), keyGen)
|
||||
em := newEncryptedModel(nm, keyGen)
|
||||
|
||||
// We do the wire format conversion first (outermost) so that the
|
||||
// metadata is in wire format when it reaches the encryption step.
|
||||
@@ -265,10 +266,22 @@ func newRawConnection(deviceID DeviceID, reader io.Reader, writer io.Writer, clo
|
||||
}
|
||||
|
||||
// Start creates the goroutines for sending and receiving of messages. It must
|
||||
// be called exactly once after creating a connection.
|
||||
// be called once after creating a connection. It should only be called once,
|
||||
// subsequent calls will have no effect.
|
||||
func (c *rawConnection) Start() {
|
||||
c.startStopMut.Lock()
|
||||
defer c.startStopMut.Unlock()
|
||||
|
||||
select {
|
||||
case <-c.started:
|
||||
return
|
||||
case <-c.closed:
|
||||
// we have already closed the connection before starting processing
|
||||
// on it.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
c.loopWG.Add(5)
|
||||
go func() {
|
||||
c.readerLoop()
|
||||
@@ -291,6 +304,7 @@ func (c *rawConnection) Start() {
|
||||
c.pingReceiver()
|
||||
c.loopWG.Done()
|
||||
}()
|
||||
|
||||
c.startTime = time.Now().Truncate(time.Second)
|
||||
close(c.started)
|
||||
}
|
||||
@@ -369,7 +383,7 @@ func (c *rawConnection) Request(ctx context.Context, req *Request) ([]byte, erro
|
||||
}
|
||||
|
||||
// ClusterConfig sends the cluster configuration message to the peer.
|
||||
func (c *rawConnection) ClusterConfig(config *ClusterConfig) {
|
||||
func (c *rawConnection) ClusterConfig(config *ClusterConfig, _ map[string]string) {
|
||||
select {
|
||||
case c.clusterConfigBox <- config:
|
||||
case <-c.closed:
|
||||
@@ -950,9 +964,9 @@ func (c *rawConnection) Close(err error) {
|
||||
|
||||
// internalClose is called if there is an unexpected error during normal operation.
|
||||
func (c *rawConnection) internalClose(err error) {
|
||||
c.startStopMut.Lock()
|
||||
defer c.startStopMut.Unlock()
|
||||
c.closeOnce.Do(func() {
|
||||
c.startStopMut.Lock()
|
||||
|
||||
l.Debugf("close connection to %s at %s due to %v", c.deviceID.Short(), c.ConnectionInfo, err)
|
||||
if cerr := c.closer.Close(); cerr != nil {
|
||||
l.Debugf("failed to close underlying conn %s at %s %v:", c.deviceID.Short(), c.ConnectionInfo, cerr)
|
||||
@@ -974,6 +988,10 @@ func (c *rawConnection) internalClose(err error) {
|
||||
<-c.dispatcherLoopStopped
|
||||
}
|
||||
|
||||
c.startStopMut.Unlock()
|
||||
|
||||
// We don't want to call into the model while holding the
|
||||
// startStopMut.
|
||||
c.model.Closed(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,14 +35,14 @@ func TestPing(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := getRawConnection(NewConnection(c1ID, br, aw, testutil.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c1 := getRawConnection(NewConnection(c1ID, br, aw, testutil.NoopCloser{}, newTestModel(), new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(&ClusterConfig{})
|
||||
c1.ClusterConfig(&ClusterConfig{})
|
||||
c0.ClusterConfig(&ClusterConfig{}, nil)
|
||||
c1.ClusterConfig(&ClusterConfig{}, nil)
|
||||
|
||||
if ok := c0.ping(); !ok {
|
||||
t.Error("c0 ping failed")
|
||||
@@ -61,14 +61,14 @@ func TestClose(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := NewConnection(c1ID, br, aw, testutil.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen)
|
||||
c1 := NewConnection(c1ID, br, aw, testutil.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionAlways, testKeyGen)
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(&ClusterConfig{})
|
||||
c1.ClusterConfig(&ClusterConfig{})
|
||||
c0.ClusterConfig(&ClusterConfig{}, nil)
|
||||
c1.ClusterConfig(&ClusterConfig{}, nil)
|
||||
|
||||
c0.internalClose(errManual)
|
||||
|
||||
@@ -106,7 +106,7 @@ func TestCloseOnBlockingSend(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutil.NewBlockingRW()
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -114,7 +114,7 @@ func TestCloseOnBlockingSend(t *testing.T) {
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
c.ClusterConfig(&ClusterConfig{})
|
||||
c.ClusterConfig(&ClusterConfig{}, nil)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
@@ -157,14 +157,14 @@ func TestCloseRace(t *testing.T) {
|
||||
ar, aw := io.Pipe()
|
||||
br, bw := io.Pipe()
|
||||
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever, nil, testKeyGen))
|
||||
c0 := getRawConnection(NewConnection(c0ID, ar, bw, testutil.NoopCloser{}, m0, new(mockedConnectionInfo), CompressionNever, testKeyGen))
|
||||
c0.Start()
|
||||
defer closeAndWait(c0, ar, bw)
|
||||
c1 := NewConnection(c1ID, br, aw, testutil.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever, nil, testKeyGen)
|
||||
c1 := NewConnection(c1ID, br, aw, testutil.NoopCloser{}, m1, new(mockedConnectionInfo), CompressionNever, testKeyGen)
|
||||
c1.Start()
|
||||
defer closeAndWait(c1, ar, bw)
|
||||
c0.ClusterConfig(&ClusterConfig{})
|
||||
c1.ClusterConfig(&ClusterConfig{})
|
||||
c0.ClusterConfig(&ClusterConfig{}, nil)
|
||||
c1.ClusterConfig(&ClusterConfig{}, nil)
|
||||
|
||||
c1.Index(context.Background(), &Index{Folder: "default"})
|
||||
select {
|
||||
@@ -197,7 +197,7 @@ func TestClusterConfigFirst(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutil.NewBlockingRW()
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutil.NoopRW{}, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutil.NoopRW{}, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -208,7 +208,7 @@ func TestClusterConfigFirst(t *testing.T) {
|
||||
// Allow some time for c.writerLoop to set up after c.Start
|
||||
}
|
||||
|
||||
c.ClusterConfig(&ClusterConfig{})
|
||||
c.ClusterConfig(&ClusterConfig{}, nil)
|
||||
|
||||
done := make(chan struct{})
|
||||
if ok := c.send(context.Background(), &bep.Ping{}, done); !ok {
|
||||
@@ -249,7 +249,7 @@ func TestCloseTimeout(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutil.NewBlockingRW()
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -531,7 +531,7 @@ func TestClusterConfigAfterClose(t *testing.T) {
|
||||
m := newTestModel()
|
||||
|
||||
rw := testutil.NewBlockingRW()
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c := getRawConnection(NewConnection(c0ID, rw, rw, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
c.Start()
|
||||
defer closeAndWait(c, rw)
|
||||
|
||||
@@ -539,7 +539,7 @@ func TestClusterConfigAfterClose(t *testing.T) {
|
||||
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
c.ClusterConfig(&ClusterConfig{})
|
||||
c.ClusterConfig(&ClusterConfig{}, nil)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
@@ -555,7 +555,7 @@ func TestDispatcherToCloseDeadlock(t *testing.T) {
|
||||
// the model callbacks (ClusterConfig).
|
||||
m := newTestModel()
|
||||
rw := testutil.NewBlockingRW()
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutil.NoopRW{}, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, nil, testKeyGen))
|
||||
c := getRawConnection(NewConnection(c0ID, rw, &testutil.NoopRW{}, testutil.NoopCloser{}, m, new(mockedConnectionInfo), CompressionAlways, testKeyGen))
|
||||
m.ccFn = func(*ClusterConfig) {
|
||||
c.Close(errManual)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ func (a *aggregator) updateConfig(folderCfg config.FolderConfiguration) {
|
||||
if maxDelay := folderCfg.FSWatcherTimeoutS; maxDelay > 0 {
|
||||
// FSWatcherTimeoutS is set explicitly so use that, but it also
|
||||
// can't be lower than FSWatcherDelayS
|
||||
a.notifyTimeout = time.Duration(max(maxDelay, folderCfg.FSWatcherDelayS)) * time.Second
|
||||
a.notifyTimeout = time.Duration(max(maxDelay, folderCfg.FSWatcherDelayS) * float64(time.Second))
|
||||
} else {
|
||||
// Use the default FSWatcherTimeoutS calculation
|
||||
a.notifyTimeout = notifyTimeout(folderCfg.FSWatcherDelayS)
|
||||
@@ -471,10 +471,10 @@ func notifyTimeout(eventDelayS float64) time.Duration {
|
||||
longDelayTimeout = time.Minute
|
||||
)
|
||||
if eventDelayS < shortDelayS {
|
||||
return time.Duration(eventDelayS*shortDelayMultiplicator) * time.Second
|
||||
return time.Duration(eventDelayS * shortDelayMultiplicator * float64(time.Second))
|
||||
}
|
||||
if eventDelayS < longDelayS {
|
||||
return longDelayTimeout
|
||||
}
|
||||
return time.Duration(eventDelayS) * time.Second
|
||||
return time.Duration(eventDelayS * float64(time.Second))
|
||||
}
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Jun 01, 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
|
||||
@@ -259,8 +254,9 @@ protocol buffer message just as in the uncompressed case.
|
||||
This informational message provides information about the cluster
|
||||
configuration as it pertains to the current connection. A Cluster Config
|
||||
message MUST be the first post authentication message sent on a BEP
|
||||
connection. Additional Cluster Config messages MUST NOT be sent after the
|
||||
initial exchange.
|
||||
connection. Additional Cluster Config messages MAY be sent after the initial
|
||||
exchange to change the connection in place with the update configuration. If an
|
||||
implementation does not support that, it SHOULD close the connection.
|
||||
.SS Protocol Buffer Schema
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
@@ -1054,82 +1050,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
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.SH SYNOPSIS
|
||||
@@ -567,8 +567,8 @@ change this unless you know what you’re 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
|
||||
@@ -1536,6 +1537,23 @@ no limit. Affects incoming connections and prevents attempting outgoing
|
||||
connections. The mechanism is described in detail in a \fI\%separate
|
||||
chapter\fP\&.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B auditEnabled
|
||||
When \fBtrue\fP, analogous to \fI\%\-\-audit\fP being set.
|
||||
Defaults to \fBfalse\fP\&.
|
||||
.sp
|
||||
When either this option, or \fI\%\-\-audit\fP (or both) are enabled,
|
||||
auditing is enabled.
|
||||
.UNINDENT
|
||||
.INDENT 0.0
|
||||
.TP
|
||||
.B auditFile
|
||||
Analogous to \fI\%\-\-auditfile\fP\&. Defaults to being unset.
|
||||
.sp
|
||||
For compatibility reasons, if both this option and \fI\%\-\-auditfile\fP
|
||||
are set, \fI\%\-\-auditfile\fP takes priority.
|
||||
.UNINDENT
|
||||
.SH DEFAULTS ELEMENT
|
||||
.INDENT 0.0
|
||||
.INDENT 3.5
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 01, 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
|
||||
},
|
||||
|
||||
@@ -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" "Jun 01, 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
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.SH ANNOUNCEMENTS
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.SH MODE OF OPERATION
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.SH ROUTER SETUP
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.SH WHAT IS A RELAY?
|
||||
@@ -246,8 +246,11 @@ After the successful response, all the bytes written and received will be
|
||||
relayed between the two devices in the session directly.
|
||||
.SS Example Exchange
|
||||
.sp
|
||||
Client A \- Permanent protocol mode
|
||||
Client B \- Temporary protocol mode
|
||||
Client A is the first to join the session.
|
||||
.sp
|
||||
Client B is the second to join the session.
|
||||
.sp
|
||||
Both are in session mode.
|
||||
.TS
|
||||
box center;
|
||||
l|l|l|l.
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.sp
|
||||
|
||||
@@ -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" "Jun 01, 2025" "v1.29.6" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.SH SYNOPSIS
|
||||
|
||||
@@ -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
21
relnotes/README.md
Normal 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.
|
||||
15
relnotes/v1.md
Normal file
15
relnotes/v1.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## 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.
|
||||
|
||||
This release is also available as:
|
||||
|
||||
* APT repository: https://apt.syncthing.net/
|
||||
|
||||
* Docker image: `docker.io/syncthing/syncthing:{{.version}}` or `ghcr.io/syncthing/syncthing:{{.version}}`
|
||||
(`{docker,ghcr}.io/syncthing/syncthing:1` to follow just the major version)
|
||||
@@ -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{}
|
||||
|
||||
124
script/next-version.go
Normal file
124
script/next-version.go
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coreos/go-semver/semver"
|
||||
)
|
||||
|
||||
const suffix = "rc"
|
||||
|
||||
func main() {
|
||||
pre := flag.Bool("pre", false, "Create a prerelease")
|
||||
flag.Parse()
|
||||
|
||||
// Get the latest "v1.22.3" or "v1.22.3-rc.1" style tag.
|
||||
latestTag, err := cmd("git", "describe", "--abbrev=0", "--match", "v[0-9].*")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
latest, err := semver.NewVersion(latestTag[1:])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the latest "v1.22.3" style tag, excludeing prereleases.
|
||||
latestStableTag, err := cmd("git", "describe", "--abbrev=0", "--match", "v[0-9].*", "--exclude", "*-*")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
latestStable, err := semver.NewVersion(latestStableTag[1:])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Get the commit logs since the latest stable tag.
|
||||
logsSinceLatest, err := cmd("git", "log", "--pretty=format:%s", latestStableTag+"..HEAD")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Check if the next version should be a feature or a patch release
|
||||
nextIsFeature := false
|
||||
for _, line := range strings.Split(logsSinceLatest, "\n") {
|
||||
if strings.HasPrefix(line, "feat") {
|
||||
nextIsFeature = true
|
||||
break
|
||||
}
|
||||
}
|
||||
next := *latestStable
|
||||
if nextIsFeature {
|
||||
next.BumpMinor()
|
||||
} else {
|
||||
next.BumpPatch()
|
||||
}
|
||||
|
||||
if latest.PreRelease != "" {
|
||||
if !*pre {
|
||||
// We want a stable release. Simply remove the prerelease
|
||||
// suffix.
|
||||
latest.PreRelease = ""
|
||||
fmt.Println("v" + latest.String())
|
||||
return
|
||||
}
|
||||
|
||||
// We want the next prerelease. We are already on a prerelease. If
|
||||
// it's the correct prerelease compared to the logs we just got, we
|
||||
// should just bump the prerelease counter.
|
||||
if next.Major == latest.Major && next.Minor == latest.Minor && next.Patch == latest.Patch {
|
||||
parts := latest.PreRelease.Slice()
|
||||
for i, p := range parts {
|
||||
if v, err := strconv.Atoi(p); err == nil {
|
||||
parts[i] = strconv.Itoa(v + 1)
|
||||
latest.PreRelease = semver.PreRelease(strings.Join(parts, "."))
|
||||
fmt.Println("v" + latest.String())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we generate a new rc.1 for the correct next version.
|
||||
next.PreRelease = suffix + ".1"
|
||||
fmt.Println("v" + next.String())
|
||||
return
|
||||
}
|
||||
|
||||
if nextIsFeature {
|
||||
latest.BumpMinor()
|
||||
} else {
|
||||
latest.BumpPatch()
|
||||
}
|
||||
if *pre {
|
||||
latest.PreRelease = suffix + ".1"
|
||||
}
|
||||
|
||||
fmt.Println("v" + latest.String())
|
||||
}
|
||||
|
||||
func cmd(name string, args ...string) (string, error) {
|
||||
cmd := exec.Command(name, args...)
|
||||
bs, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(bs)), nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user