Compare commits

..
Author SHA1 Message Date
Jakob Borg 0945304a79 build: fix detection of next rc version 2025-06-20 11:17:23 +02:00
Jakob Borg 9703dd9f57 build: import release workflow changes from main 2025-06-20 11:12:05 +02:00
yparitcherandJakob Borg 259e9ef08e fix(protocol): slightly loosen/correct ownership comparison criteria (fixes #9879) (#10176)
Only Require either matching UID & GID OR matching Names.

If the 2 devices have a different Name => UID mapping, they can never be
totaly equal. Therefore when syncing we try matching the Name and fall
back to the UID. However when scanning for changes we currently require
both the Name & UID to match. This leads to forever having out of sync
files back and forth, or local additions when receive only.

This patch does not change the sending behavoir. It only change what we
decide is equal for exisiting files with mismapped Name => UID,

The added testcases show the change: Test 1,5,6 are the same as current.
Test 2,3 Are what change with this patch (from false to true). Test 4 is
a subset of test 2 they is currently special cased as true, which does
not chnage.

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-20 09:55:42 +02:00
Simon Frei 6a0c6128d8 fix(watchaggregator): properly handle sub-second watch durations (fixes #9927) (#10179)
I'll let Audrius words from the ticket explain this :)

> I'm a bit lost, time.Duration is an int64, yet watcher delay is float,
> anything sub 1s gets rounded down to 0, so you just end up going into
an
> infinite loop.


https://github.com/syncthing/syncthing/issues/9927#issuecomment-2967736106
2025-06-15 10:29:33 +02:00
Jakob Borg b05ece0681 build: more resilient pushes to releases 2025-06-07 13:18:58 +02:00
Jakob Borg e9133ef82b docs: link to Docker image, APT, in release notes 2025-06-05 19:19:05 +02:00
Jakob Borg 67ba20d777 build: also create relaysrv and discosrv releases 2025-06-05 19:19:05 +02:00
Jakob Borg 21da0d7890 fix(stupgrades): return latest stable & pre for each major 2025-06-05 19:19:05 +02:00
ardevdandJakob Borg ebbe57d0ab fix(syncthing): avoid writing panic log to nil fd (#10154)
### Purpose

This change fixes a logical bug in the panic log writing where we could
end up writing to a uninitialized file descriptor.

On the very first iteration, `panicFd` is nil. We enter the if `panicFd
== nil { … }` block, check for “panic:” or “fatal error:”, and if
neither matches, we skip instantiating `panicFd` altogether. However,
immediately after, still within `if panicFd == nil { … }`, we call
`panicFd.WriteString("Panic at ...")`. But `panicFd` would in this case
be `nil`, which will cause a run‐time panic.

It's not clear to me why panicFd is only initialized if the lines start
with "panic:" or "fatal error:" so I've left that logic untouched. With
this change we at least avoid the risk of writing to a nil
filedescriptor.
## Authorship

Your name and email will be added automatically to the AUTHORS file
based on the commit metadata.

---------

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-06-03 07:39:21 +02:00
Jakob Borg f4abc71dcc chore: copyright in next-version script 2025-06-02 20:41:42 +02:00
Jakob Borg 8aa02da93a build: include "v" prefix in version tags... 2025-06-02 19:59:45 +02:00
Jakob Borg 0e560486db build: use own script instead of svu
We use a slightly different handling of features between prereleases.
2025-06-02 19:49:23 +02:00
Syncthing Release Automation 57d413099d chore(gui, man, authors): update docs, translations, and contributors 2025-06-02 05:24:58 +00:00
Jakob Borg 1fdf07933c feat(config): expose folder and device info as metrics (fixes #9519) (#10148)
Tihs makes it easier to use metrics based on device and folder labels,
names, and other attributes. Other metrics which are based on folder or
device ID can be joined with these info metrics to enrich their label
sets.

```
# HELP syncthing_config_device_info Provides additional information labels on devices
# TYPE syncthing_config_device_info gauge
syncthing_config_device_info{device="I6KAH76-66SLLLB-5PFXSOA-UFJCDZC-YAOMLEK-CP2GB32-BV5RQST-3PSROAU",introducer="false",name="s1",paused="false",untrusted="false"} 1

# HELP syncthing_config_folder_info Provides additional information labels on folders
# TYPE syncthing_config_folder_info gauge
syncthing_config_folder_info{folder="default",label="The default folder",path="s2",paused="false",type="sendreceive"} 1
```

With this you can e.g. query for

```
syncthing_connections_active * on(device) group_left syncthing_config_device_info
```

Fixes #9519 
Closes #10074 
Closes #10147
2025-05-31 17:09:23 +02:00
Jakob Borg c50678618f chore: add issue types to GitHub issue templates 2025-05-30 11:57:27 +02:00
Jakob Borg 8094b459e4 build: remove schedule from PR metadata job
It shouldn't have touched non-PR issues, but it did
2025-05-30 11:57:27 +02:00
Simon FreiandJakob Borg 6765867a2e chore(protocol): only allow enc. password changes on cluster config (#10145)
In practice we already always call SetPassword and ClusterConfig
together. However it's not just "sensible" to do that, it's required: If
the passwords change, the remote device needs to know about that to
check that the enc. setup is valid/consistent (e.g. tokens match,
folder-type is appropriate, ...).
And with the passwords set later, there's no point in adding them as
part of creating a new connection.

This is a "followup" (if one can call it that 4 years later :) ) to
resp. fix for the following commit:
924b96856f

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-05-30 09:52:47 +02:00
Simon Frei 4fb8ee6a6f chore(protocol): don't start connection routines a second time (#10146) 2025-05-30 06:28:42 +00:00
Jakob Borg 674834ccf4 build: properly propagate build tags to Debian build (#10144)
Previously all were ignored except noupgrade which was hard coded...
2025-05-29 15:06:57 +00:00
Jakob Borg 3bd2bff23b fix(protocol): avoid deadlock with concurrent connection start and close (#10140) 2025-05-29 14:56:58 +00:00
Jakob Borg 40660c5fb7 build: add labeler workflow for PRs (#10143)
Use labels to categorise release notes
2025-05-29 10:04:08 +02:00
Jakob Borg d940d094a1 build(deps): update our notify package from upstream (#10142) 2025-05-28 15:04:24 +00:00
Jakob Borg 9d67727989 build(deps): update dependencies (#10141) 2025-05-28 13:52:08 +00:00
Jakob Borg 6f51700a7f docs: general notes about v2 coming (#10135)
This adds a file that will be prepended to release notes (tag messages,
GitHub releases, forum posts) for v1 releases. I'd like there to be
something there to flag that things are going to change.
2025-05-27 10:01:04 +02:00
Marcel Meyer 598915193a refactor: use slices package for sorting (#10136)
Few more complicated usages of the sort packages are left.

### Purpose

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

### Purpose

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

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

### Testing

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

### Screenshot


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

### Purpose

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

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

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

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

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

### Screenshots


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

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

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

### Testing

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

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

### Testing

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

### Demo


https://github.com/user-attachments/assets/a010db9a-72f7-4160-a7db-ddfebffb4834
2025-05-02 14:53:54 +00:00
Jakob Borg cd6ea60fa1 build(deps): update dependencies (#10091)
Without bumping Go version
2025-05-01 18:15:37 +00:00
domain 0bf21d9db2 fix(strelaysrv): make the session limiter session-dependent (fixes #10072) (#10073)
### Purpose

Make the session limiter only apply to current session.

### Testing

Relay 2 or more sessions and check if the sum of the connection speed
can exceed the specified per-session rate.

2 sessions (-global-rate=50000000 and -per-session-rate=6250000):


![图片](https://github.com/user-attachments/assets/133e531a-ed49-4890-aef7-821c628bcfc8)

1 session (-global-rate=50000000 and -per-session-rate=6250000):


![图片](https://github.com/user-attachments/assets/ac89ea53-2d8e-4347-9bbc-4780d85e38d7)
2025-04-30 14:25:01 +00:00
Jakob Borg f61843ef2e build: artifact uploads destination OCI 2025-04-29 14:01:25 -05:00
Syncthing Release Automation 23e8366f8d chore(gui, man, authors): update docs, translations, and contributors 2025-04-28 03:52:12 +00:00
Ross Smith II 93e72cc83f chore(gui): use go list --deps for dependency list (#10071) 2025-04-26 02:24:31 +00:00
Marcus B Spencer 190dff142c feat(config): add option for audit file (fixes #9481) (#10066) 2025-04-23 22:32:23 +07:00
bt90andJakob Borg c667ada63a chore(api): log X-Forwarded-For (#10035)
### Purpose

Fix https://github.com/syncthing/syncthing/issues/9336

The `emitLoginAttempt` function now checks for the presence of an
`X-Forwarded-For` header. The IP from this header is only used if the
connecting host is either on loopback or on the same LAN.

In the case of a host pretending to be a proxy, we'd still have both IPs
in the logs, which should make this much less critical from a security
standpoint.

### Testing

1. directly via localhost
2. via proxy an localhost

#### Logs

```
[3JPXJ] 2025/04/11 15:00:40 INFO: Wrong credentials supplied during API authorization from 127.0.0.1
[3JPXJ] 2025/04/11 15:03:04 INFO: Wrong credentials supplied during API authorization from 192.168.178.5 proxied by 127.0.0.1
```

#### Event API

```
  {
    "id": 23,
    "globalID": 23,
    "time": "2025-04-11T15:00:40.578577402+02:00",
    "type": "LoginAttempt",
    "data": {
      "remoteAddress": "127.0.0.1",
      "success": false,
      "username": "sdfsd"
    }
  },
  {
    "id": 24,
    "globalID": 24,
    "time": "2025-04-11T15:03:04.423403976+02:00",
    "type": "LoginAttempt",
    "data": {
      "proxy": "127.0.0.1",
      "remoteAddress": "192.168.178.5",
      "success": false,
      "username": "sdfsd"
    }
  }
```

### Documentation

https://github.com/syncthing/docs/pull/907

---------

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-04-23 06:01:13 +00:00
Ross Smith II 93ae30d889 chore(gui): update dependency copyrights, add script for periodic maintenance (#10067)
### Purpose

This PR parses the output of `go mod graph` and updates the copyright
list in our [about
modal](https://github.com/syncthing/syncthing/blob/486eebc4ac911b86b3d110dd0f7a955edd0f5106/gui/default/syncthing/core/aboutModalView.html#L38).

If there are no changes, the program is silent. Otherwise, it reports
what additions, and deletions it made. It does not rewrite existing
copyright notices, but it does remove notices that we no longer use, as
well as add new ones.

It uses a GitHub API to try to determine the copyright string in the
license file. If one is not found, it defaults to `Copyright &copy;
<this_year> the <owner/repo> authors`. If a proper copyright is found,
simply update the notice in `aboutModalView.html`, and it will be used.
2025-04-23 12:41:05 +07:00
Syncthing Release Automation 486eebc4ac chore(gui, man, authors): update docs, translations, and contributors 2025-04-21 03:52:26 +00:00
Jakob Borg ff33d976d1 chore(syncthing): remove support for TLS 1.2 sync connections (#10064)
This cleans up the option to allow old TLS 1.2 sync connections. The
flag existed for compatibility with old Syncthing versions that don't
support TLS 1.3, which is approximately Syncthing 1.2.2 (September 2019)
and older. ("Approximately" because it depends on the Go version it's
built with and that's when we switched to building with Go 1.13.)

Ref #10062 because it reminded me this exists.
2025-04-21 10:30:43 +07:00
TheCreeper 69890b4282 fix(osutil): give threads same I/O priority on Linux (#10063) 2025-04-21 02:30:52 +00:00
Jakob Borg 533c9a6ab0 chore(stun): switch lookup warning to debug level 2025-04-17 07:29:10 +07:00
Syncthing Release Automation 9521bb3931 chore(gui, man, authors): update docs, translations, and contributors 2025-04-14 03:51:01 +00:00
Jakob Borg e46a0f99c3 chore: add missing copyright in new files from infra branch (#10055)
Let's see if it passes
2025-04-13 09:25:16 +00:00
Jakob Borg ed97e365b2 Merge branch 'infrastructure'
* infrastructure:
  feat(stdiscosrv): configurable desired not-found rate
  chore(blobs): generalised blob storage
  chore(stdiscosrv): path style s3
  feat(ursv): add os/arch/distribution metric
  chore(strelaypoolsrv): limit number of returned relays
  build(infra): run in Docker environment for pushes
  chore(stupgrades): expose latest release as a metric
2025-04-13 09:41:45 +02:00
Jakob Borg b4776ea4e0 feat(stdiscosrv): configurable desired not-found rate 2025-04-13 09:41:16 +02:00
Jakob Borg b5ffd0a796 chore(blobs): generalised blob storage 2025-04-13 09:41:16 +02:00
Jakob Borg c74299b59a chore(stdiscosrv): path style s3 2025-04-13 09:40:14 +02:00
Jakob Borg 8b6d837483 feat(ursv): add os/arch/distribution metric 2025-04-13 09:40:14 +02:00
Jakob Borg 3e74b3dee2 chore(strelaypoolsrv): limit number of returned relays
Avoid unnecessarily enormous responses by returning a random subset of
relays.
2025-04-13 09:40:14 +02:00
Jakob Borg 2902da996c build(infra): run in Docker environment for pushes 2025-04-13 09:40:14 +02:00
Jakob Borg f6f144bf17 chore(stupgrades): expose latest release as a metric 2025-04-13 09:40:11 +02:00
Sébastien WENSKE ab5c42f4a0 feat(api, gui): allow authentication bypass for metrics (#10045)
### Purpose

Give the ability to skip authentication for prometheus metrics
("/metrics").

### Testing

When authentication is enabled and "Metrics Without Auth" is checked
(not the default), the "/metrics" path remains accessible even when
disconnected.

### Screenshots


![image](https://github.com/user-attachments/assets/144b696b-dd72-46f4-94d5-cd21848e4a4c)

### Documentation

https://github.com/syncthing/docs/pull/906
2025-04-13 07:35:57 +00:00
Jakob Borg 7db3f7eaac Merge branch 'release-1.29.5'
* release-1.29.5:
  build: push artifacts to Azure (#10044)
  fix(syncthing): use separate lock file instead of locking the certificate (fixes #10053) (#10054)
2025-04-12 14:57:04 +02:00
Jakob Borg f0b666269b build: push artifacts to Azure (#10044)
Provider migration
2025-04-12 14:55:24 +02:00
Jakob Borg 190a59842c fix(syncthing): use separate lock file instead of locking the certificate (fixes #10053) (#10054)
Apparently that nukes the cert under some circumstances on some Windows
🤷
2025-04-12 14:49:23 +02:00
Jakob Borg 40888c1a66 fix(syncthing): use separate lock file instead of locking the certificate (fixes #10053) (#10054)
Apparently that nukes the cert under some circumstances on some Windows
🤷
2025-04-12 14:46:57 +02:00
Jakob Borg fa0d933e49 fix(gui): fix previous commit 2025-04-09 15:39:09 +02:00
tomasz1986 8372c0288f fix(gui): mark unseen disconnected devices as inactive (#10048)
Currently, the "Disconnected (Inactive)" status is only given to devices
that have not been seen for 7 days or longer. However, this is not the
case when adding a new device, or after resetting the database. Those
devices are only marked as "Disconnected", and they will stay like that
even if a long time passes without any connectivity. Moreover, the lack
of an "Inactive" status may confuse the user to believe that their
disconnect is only temporary.

For this reason, always mark devices that have not been seen yet as
"Disconnected (Inactive)".

Signed-off-by: Tomasz Wilczyński <twilczynski@naver.com>
2025-04-08 22:08:00 +02:00
Paul Donald 5f5d672a7d fix(strings): differentiate setup(n) and set(v) up (#10024)
Correct GUI strings, translations and comments to use proper grammar.
2025-04-08 12:45:05 +00:00
Tommy van der Vorst d23cd197e1 chore(fs): changes to allow Filesystem to be implemented externally (#10040)
### Purpose

The `fs.Filesystem` interface contains two parts that cannot be
implemented externally because they are private:

* `filesystemWrapperType`: this PR changes `unwrapFilesystem` to
downcast to a specific concrete type
* `underlying`: this PR simply moves it to an unexported interface

### Testing

Regular tests pass.
2025-04-08 12:39:39 +00:00
bt90 d7ca483df1 chore(config): resolve primary STUN servers via SRV record (fixes #10029) (#10031)
### Purpose

Fixes #10029

### Testing

```
[3JPXJ] 2025/04/03 14:36:44.601454 stun.go:146: DEBUG: Running stun for Stun@udp://[::]:22000 via fyc5mja4mz5s0vmz1txx.syncthing.net:9999
[3JPXJ] 2025/04/03 14:36:54.185157 stun.go:170: DEBUG: Stun@udp://[::]:22000 stun discovery on fyc5mja4mz5s0vmz1txx.syncthing.net:9999 resulted in no address
[3JPXJ] 2025/04/03 14:36:54.185204 stun.go:146: DEBUG: Running stun for Stun@udp://[::]:22000 via stun.internetcalls.com:3478
```

### Documentation

https://github.com/syncthing/docs/pull/904
2025-04-08 12:23:57 +00:00
Jakob Borg e48be98cd5 build: push artifacts to Azure (#10044)
Provider migration
2025-04-08 09:43:19 +02:00
Syncthing Release Automation e9a2ff3aa6 chore(gui, man, authors): update docs, translations, and contributors 2025-04-07 03:50:00 +00:00
Jakob Borg 2301f72c5b fix(config): zero filesystemtype is "basic" (#10038)
For legacy purposes
2025-04-04 19:28:39 +00:00
Tommy van der Vorst f7c8efd93c fix(config): properly apply defaults when reading folder configuration (#10034) 2025-04-04 16:46:12 +00:00
Sébastien WENSKE 3e7ccf7c48 chore(model): add metric for total number of conflicts (#10037) 2025-04-04 09:24:04 -07:00
bt90 6bc2784e9a build: replace underscore in Debian version (#10032)
The workflow building Debian packages chokes on branches containing
underscores:

```
{:timestamp=>"2025-04-03T10:31:46.749835+0000", :message=>"Invalid package configuration: The version looks invalid for Debian packages. Debian version field must contain only alphanumerics and . (period), + (plus), - (hyphen) or ~ (tilde). I have '1.29.5~dev.13.ga38df11f~srv_stun' which which isn't valid.", :level=>:error}
```

This replaces the offending `_` with a `~` which should yield a valid
version.
2025-04-03 14:28:33 +02:00
Tommy van der Vorst f15d50c2e8 feat(fs, config): add support for custom filesystem type construction (#9887)
For Synctrain I would like to create a virtual filesystem that exposes
iOS' photo library. This can only be accessed through APIs.
2025-04-03 10:12:23 +02:00
Jakob Borg f9007ed106 build(deps): update dependencies (#10020)
deps deps deps
2025-04-02 08:51:37 +02:00
bt90 05cc6b0f43 chore(fs): speed up case normalization (#10013)
### Purpose

Resurrecting https://github.com/syncthing/syncthing/pull/9365

### Testing

Current benchmark results:

```
goos: linux
goarch: amd64
pkg: github.com/syncthing/syncthing/lib/fs
cpu: AMD EPYC 7763 64-Core Processor                
                                           │  ../old.txt  │             ../new.txt              │
                                           │    sec/op    │   sec/op     vs base                │
UnicodeLowercase/ASCII_lowercase-4            43.68n ± 4%   12.19n ± 1%  -72.09% (p=0.000 n=10)
UnicodeLowercase/ASCII_mixedcase_start-4     200.65n ± 2%   59.49n ± 3%  -70.35% (p=0.000 n=10)
UnicodeLowercase/ASCII_mixedcase_end-4        95.50n ± 2%   59.10n ± 2%  -38.12% (p=0.000 n=10)
UnicodeLowercase/Latin1_lowercase-4           122.5n ± 1%   131.4n ± 1%   +7.27% (p=0.000 n=10)
UnicodeLowercase/Latin1_mixedcase_start-4     339.9n ± 2%   309.2n ± 1%   -9.05% (p=0.000 n=10)
UnicodeLowercase/Latin1_mixedcase_end-4       183.6n ± 2%   174.3n ± 1%   -5.04% (p=0.000 n=10)
UnicodeLowercase/Unicode_lowercase-4          456.6n ± 1%   440.5n ± 1%   -3.53% (p=0.000 n=10)
UnicodeLowercase/Unicode_mixedcase_start-4    625.9n ± 1%   595.6n ± 1%   -4.83% (p=0.000 n=10)
UnicodeLowercase/Unicode_mixedcase_end-4      516.2n ± 1%   495.5n ± 1%   -4.02% (p=0.000 n=10)
geomean                                       214.1n        150.4n       -29.72%

                                           │  ../old.txt  │             ../new.txt              │
                                           │     B/op     │    B/op     vs base                 │
UnicodeLowercase/ASCII_lowercase-4           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/ASCII_mixedcase_start-4     24.00 ± 0%     24.00 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/ASCII_mixedcase_end-4       24.00 ± 0%     24.00 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_lowercase-4          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_mixedcase_start-4    32.00 ± 0%     32.00 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_mixedcase_end-4      32.00 ± 0%     32.00 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_lowercase-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_mixedcase_start-4   48.00 ± 0%     48.00 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_mixedcase_end-4     48.00 ± 0%     48.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                 ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                           │  ../old.txt  │             ../new.txt              │
                                           │  allocs/op   │ allocs/op   vs base                 │
UnicodeLowercase/ASCII_lowercase-4           0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/ASCII_mixedcase_start-4     1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/ASCII_mixedcase_end-4       1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_lowercase-4          0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_mixedcase_start-4    1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Latin1_mixedcase_end-4      1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_lowercase-4         0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_mixedcase_start-4   1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
UnicodeLowercase/Unicode_mixedcase_end-4     1.000 ± 0%     1.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                                                 ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean
```

I think the `+7%` for the lowercase Latin1 testcase is easily outweighed
by the ASCII and unicode improvements 🙂
2025-04-01 13:41:57 +02:00
Marcus B Spencer 1efcfeb3ad chore(config): remove discontinued secondary STUN servers (fixes #10011) (#10012)
Similarly to #10009, we will remove some discontinued STUN servers,
except instead of being the official primary server, it's some
unofficial secondary STUN servers.

### Testing

Use a STUN client (like [`pystun3`](https://pypi.org/project/pystun3))
to probe that the removed STUN servers are inactive.

### Documentation

syncthing/docs#902
2025-03-31 06:41:33 +00:00
Syncthing Release Automation 93195911bd chore(gui, man, authors): update docs, translations, and contributors 2025-03-31 03:50:00 +00:00
Jakob Borg 6085e3a5eb fix(stun): better error handling (ref #10008) (#10010) 2025-03-30 11:56:29 -07:00
Marcus B SpencerandJakob Borg e5b72da607 fix(config): remove discontinued primary STUN server (fixes #10008) (#10009)
The mechanism for primary STUN servers, is still intact, in case this
gets retried with a different domain.

### Purpose

As seen in [stun.syncthing.net doesn’t resolve
anymore](https://forum.syncthing.net/t/stun-syncthing-net-doesnt-resolve-anymore/24075/2?u=marbens)
on the forums, stun.syncthing.net has been shut down, so I think it's
probably a good idea to remove it.

### Testing

1. Have two or more devices
2. Disable Relaying
3. Have no Internet ports open on either end for incoming connections
trigger STUN)
4. Enable the `stun` debugging facility in the Actions -> Logs ->
Debugging Facilities
5. Verify that it doesn't output something like this within a few
seconds:
```
2025-03-30 05:51:32 Enabled debug data for "stun"
2025-03-30 05:51:47 Starting stun for Stun@udp://[::]:22000
2025-03-30 05:51:47 Running stun for Stun@udp://[::]:22000 via stun.syncthing.net:3478
2025-03-30 05:51:47 Stun@udp://[::]:22000 stun addr resolution on stun.syncthing.net:3478: lookup stun.syncthing.net: no such host
```

---------

Co-authored-by: Jakob Borg <jakob@kastelo.net>
2025-03-30 15:17:55 +02:00
mathias4833 0d6117d585 fix(gui): validate device ID in canonical form (fixes #7291) (#10006)
### Purpose

In the GUI, the device ID validation was case-sensitive and didn’t
account for dash variations, which allowed users to enter an existing
device ID without receiving proper feedback.

This fix ensures the ID is validated in its canonical form, thus
preventing the user from submitting the request if the device ID already
exists.

### Testing

To test this change, try adding a new device with an ID that matches an
existing device, but with a different case or dashes.
2025-03-29 17:52:02 +01:00
436 changed files with 58239 additions and 9816 deletions
+1
View File
@@ -1,6 +1,7 @@
name: Feature request name: Feature request
description: File a new feature request description: File a new feature request
labels: ["enhancement", "needs-triage"] labels: ["enhancement", "needs-triage"]
type: Feature
body: body:
- type: textarea - type: textarea
+1
View File
@@ -1,6 +1,7 @@
name: Bug report name: Bug report
description: If you're actually looking for support instead, see "I need help / I have a question". description: If you're actually looking for support instead, see "I need help / I have a question".
labels: ["bug", "needs-triage"] labels: ["bug", "needs-triage"]
type: Bug
body: body:
- type: markdown - type: markdown
attributes: attributes:
+23
View File
@@ -0,0 +1,23 @@
version: 1
labels:
- label: enhancement
title: ^feat\b
- label: bug
title: ^fix\b
- label: documentation
title: ^docs\b
- label: chore
title: ^chore\b
- label: chore
title: ^refactor\b
- label: build
title: ^build\b
- label: dependencies
title: ^build\(deps\)\b
+17
View File
@@ -0,0 +1,17 @@
changelog:
exclude:
labels:
- dependencies
categories:
- title: Fixes
labels:
- bug
- title: Features
labels:
- enhancement
- title: Other
labels:
- '*'
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
name: Build and push Docker images name: Build and push Docker images
if: github.repository == 'syncthing/syncthing' if: github.repository == 'syncthing/syncthing'
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: release environment: docker
strategy: strategy:
matrix: matrix:
pkg: pkg:
+121 -129
View File
@@ -3,6 +3,9 @@ name: Build Syncthing
on: on:
pull_request: pull_request:
push: push:
branches-ignore:
- release
- release-rc*
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
@@ -13,6 +16,8 @@ env:
GO_VERSION: "~1.24.0" GO_VERSION: "~1.24.0"
# Optimize compatibility on the slow archictures. # Optimize compatibility on the slow archictures.
GO386: softfloat
GOARM: "5"
GOMIPS: softfloat GOMIPS: softfloat
# Avoid hilarious amounts of obscuring log output when running tests. # Avoid hilarious amounts of obscuring log output when running tests.
@@ -22,8 +27,6 @@ env:
BUILD_USER: builder BUILD_USER: builder
BUILD_HOST: github.syncthing.net BUILD_HOST: github.syncthing.net
TAGS: "netgo osusergo sqlite_omit_load_extension"
# A note on actions and third party code... The actions under actions/ (like # A note on actions and third party code... The actions under actions/ (like
# `uses: actions/checkout`) are maintained by GitHub, and we need to trust # `uses: actions/checkout`) are maintained by GitHub, and we need to trust
# GitHub to maintain their code and infrastructure or we're in deep shit in # GitHub to maintain their code and infrastructure or we're in deep shit in
@@ -85,27 +88,6 @@ jobs:
LOKI_USER: ${{ secrets.LOKI_USER }} LOKI_USER: ${{ secrets.LOKI_USER }}
LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }} LOKI_PASSWORD: ${{ secrets.LOKI_PASSWORD }}
LOKI_LABELS: "go=${{ matrix.go }},runner=${{ matrix.runner }},repo=${{ github.repository }},ref=${{ github.ref }}" LOKI_LABELS: "go=${{ matrix.go }},runner=${{ matrix.runner }},repo=${{ github.repository }},ref=${{ github.ref }}"
CGO_ENABLED: "1"
#
# 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 basic checks job is a virtual one that depends on the matrix tests,
@@ -121,7 +103,6 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- build-test - build-test
- correctness
- package-linux - package-linux
- package-cross - package-cross
- package-source - package-source
@@ -137,8 +118,17 @@ jobs:
package-windows: package-windows:
name: Package for Windows name: Package for Windows
runs-on: ubuntu-latest runs-on: windows-latest
steps: steps:
- name: Set git to use LF
# Without this, the checkout will happen with CRLF line endings,
# which is fine for the source code but messes up tests that depend
# on data on disk being as expected. Ideally, those tests should be
# fixed, but not today.
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
@@ -150,14 +140,17 @@ jobs:
cache: false cache: false
check-latest: true check-latest: true
- uses: mlugg/setup-zig@v1 - name: Get actual Go version
run: |
go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/go-build ~\AppData\Local\go-build
~/go/pkg/mod ~\go\pkg\mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-windows-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }}
- name: Install dependencies - name: Install dependencies
run: | run: |
@@ -165,14 +158,15 @@ jobs:
- name: Create packages - name: Create packages
run: | run: |
for tgt in syncthing stdiscosrv strelaysrv ; do $targets = 'syncthing', 'stdiscosrv', 'strelaysrv'
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch amd64 -cc "zig cc -target x86_64-windows" zip $tgt $archs = 'amd64', 'arm', 'arm64', '386'
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch 386 -cc "zig cc -target x86-windows" zip $tgt foreach ($arch in $archs) {
go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm64 -cc "zig cc -target aarch64-windows" zip $tgt foreach ($tgt in $targets) {
# go run build.go -tags "${{env.TAGS}}" -goos windows -goarch arm -cc "zig cc -target thumb-windows" zip $tgt # failes with linker errors go run build.go -goarch $arch zip $tgt
done }
}
env: env:
CGO_ENABLED: "1" CGO_ENABLED: "0"
- name: Archive artifacts - name: Archive artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -182,7 +176,7 @@ jobs:
codesign-windows: codesign-windows:
name: Codesign for Windows name: Codesign for Windows
if: github.repository_owner == 'syncthing' && (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 environment: release
runs-on: windows-latest runs-on: windows-latest
needs: needs:
@@ -257,8 +251,6 @@ jobs:
go version go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: mlugg/setup-zig@v1
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -268,25 +260,14 @@ jobs:
- name: Create packages - name: Create packages
run: | run: |
sudo apt-get install -y gcc-mips64-linux-gnuabi64 gcc-mips64el-linux-gnuabi64 archs=$(go tool dist list | grep linux | sed 's#linux/##')
for tgt in syncthing stdiscosrv strelaysrv ; do for goarch in $archs ; do
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" tar "$tgt" for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch 386 -cc "zig cc -target x86-linux-musl" tar "$tgt" go run build.go -goarch "$goarch" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi" tar "$tgt" done
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips -cc "zig cc -target mips-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mipsle -cc "zig cc -target mipsel-linux-musleabi" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64 -cc mips64-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch mips64le -cc mips64el-linux-gnuabi64-gcc tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch riscv64 -cc "zig cc -target riscv64-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch s390x -cc "zig cc -target s390x-linux-musl" tar "$tgt"
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch loong64 -cc "zig cc -target loongarch64-linux-musl" tar "$tgt"
# go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64 -cc "zig cc -target powerpc64-linux-musl" tar "$tgt" # fails with linkmode not supported
go run build.go -tags "${{env.TAGS}}" -goos linux -goarch ppc64le -cc "zig cc -target powerpc64le-linux-musl" tar "$tgt"
done done
env: env:
CGO_ENABLED: "1" CGO_ENABLED: "0"
EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static"
- name: Archive artifacts - name: Archive artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -302,10 +283,8 @@ jobs:
package-macos: package-macos:
name: Package for 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 environment: release
env:
CODESIGN_IDENTITY: ${{ secrets.CODESIGN_IDENTITY }}
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@@ -332,7 +311,6 @@ jobs:
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-package-${{ hashFiles('**/go.sum') }}
- name: Import signing certificate - name: Import signing certificate
if: env.CODESIGN_IDENTITY != ''
run: | run: |
# Set up a run-specific keychain, making it available for the # Set up a run-specific keychain, making it available for the
# `codesign` tool. # `codesign` tool.
@@ -360,7 +338,7 @@ jobs:
- name: Create package (amd64) - name: Create package (amd64)
run: | run: |
for tgt in syncthing stdiscosrv strelaysrv ; do for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -tags "${{env.TAGS}}" -goarch amd64 zip "$tgt" go run build.go -goarch amd64 zip "$tgt"
done done
env: env:
CGO_ENABLED: "1" CGO_ENABLED: "1"
@@ -376,7 +354,7 @@ jobs:
EOT EOT
chmod 755 xgo.sh chmod 755 xgo.sh
for tgt in syncthing stdiscosrv strelaysrv ; do for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -tags "${{env.TAGS}}" -gocmd ./xgo.sh -goarch arm64 zip "$tgt" go run build.go -gocmd ./xgo.sh -goarch arm64 zip "$tgt"
done done
env: env:
CGO_ENABLED: "1" CGO_ENABLED: "1"
@@ -405,7 +383,7 @@ jobs:
notarize-macos: notarize-macos:
name: Notarize for macOS name: Notarize for macOS
if: github.repository_owner == 'syncthing' && (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 environment: release
needs: needs:
- package-macos - package-macos
@@ -487,7 +465,7 @@ jobs:
goarch="${plat#*/}" goarch="${plat#*/}"
echo "::group ::$plat" echo "::group ::$plat"
for tgt in syncthing stdiscosrv strelaysrv ; do for tgt in syncthing stdiscosrv strelaysrv ; do
if ! go run build.go -goos "$goos" -goarch "$goarch" tar "$tgt" ; then if ! go run build.go -goos "$goos" -goarch "$goarch" tar "$tgt" 2>/dev/null; then
echo "::warning ::Failed to build $tgt for $plat" echo "::warning ::Failed to build $tgt for $plat"
fi fi
done done
@@ -549,7 +527,7 @@ jobs:
sign-for-upgrade: sign-for-upgrade:
name: Sign for upgrade name: Sign for upgrade
if: github.repository_owner == 'syncthing' && (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 environment: release
needs: needs:
- codesign-windows - codesign-windows
@@ -667,8 +645,6 @@ jobs:
run: | run: |
gem install fpm gem install fpm
- uses: mlugg/setup-zig@v1
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -676,17 +652,15 @@ jobs:
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-debian-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-debian-${{ hashFiles('**/go.sum') }}
- name: Package for Debian (CGO) - name: Package for Debian
run: | run: |
for tgt in syncthing stdiscosrv strelaysrv ; do for arch in amd64 i386 armhf armel arm64 ; do
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch amd64 -cc "zig cc -target x86_64-linux-musl" deb "$tgt" for tgt in syncthing stdiscosrv strelaysrv ; do
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm -cc "zig cc -target arm-linux-musleabi" deb "$tgt" go run build.go -no-upgrade -installsuffix=no-upgrade -goarch "$arch" deb "$tgt"
go run build.go -no-upgrade -installsuffix=no-upgrade -tags "${{env.TAGS}}" -goos linux -goarch arm64 -cc "zig cc -target aarch64-linux-musl" deb "$tgt" done
done done
env: env:
BUILD_USER: debian BUILD_USER: debian
CGO_ENABLED: "1"
EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static"
- name: Archive artifacts - name: Archive artifacts
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@@ -700,7 +674,7 @@ jobs:
publish-nightly: publish-nightly:
name: Publish nightly build name: Publish nightly build
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref, 'refs/heads/release-nightly') if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && startsWith(github.ref, 'refs/heads/release-nightly')
environment: release environment: release
needs: needs:
- sign-for-upgrade - sign-for-upgrade
@@ -742,7 +716,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }} RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read RCLONE_CONFIG_OBJSTORE_ACL: public-read
with: with:
args: sync packages objstore:${{ secrets.S3_BUCKET }}/nightly args: sync -v --no-update-modtime packages objstore:nightly
# #
# Push release artifacts to Spaces # Push release artifacts to Spaces
@@ -750,8 +724,10 @@ jobs:
publish-release-files: publish-release-files:
name: Publish release files name: Publish release files
if: github.repository_owner == 'syncthing' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (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' || startsWith(github.ref, 'refs/tags/v'))
environment: release environment: release
permissions:
contents: write
needs: needs:
- sign-for-upgrade - sign-for-upgrade
- package-debian - package-debian
@@ -796,7 +772,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }} RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read RCLONE_CONFIG_OBJSTORE_ACL: public-read
with: with:
args: sync packages objstore:${{ secrets.S3_BUCKET }}/release/${{ env.VERSION }} args: sync -v --no-update-modtime packages objstore:release/${{ env.VERSION }}
- name: Push to object store (latest) - name: Push to object store (latest)
uses: docker://docker.io/rclone/rclone:latest uses: docker://docker.io/rclone/rclone:latest
@@ -809,7 +785,43 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }} RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read RCLONE_CONFIG_OBJSTORE_ACL: public-read
with: with:
args: sync objstore:${{ secrets.S3_BUCKET }}/release/${{ env.VERSION }} objstore:${{ secrets.S3_BUCKET }}/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 # Push Debian/APT archive
@@ -817,7 +829,7 @@ jobs:
publish-apt: publish-apt:
name: Publish APT name: Publish APT
if: github.repository_owner == 'syncthing' && (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/v1')) if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && (github.ref == 'refs/heads/release-nightly' || startsWith(github.ref, 'refs/tags/v'))
environment: release environment: release
needs: needs:
- package-debian - package-debian
@@ -844,9 +856,7 @@ jobs:
- name: Prepare packages - name: Prepare packages
run: | run: |
kind=stable kind=stable
if [[ $VERSION == v2* ]] ; then if [[ $VERSION == *-rc.[0-9] ]] ; then
kind=v2
elif [[ $VERSION == *-rc.[0-9] ]] ; then
kind=candidate kind=candidate
elif [[ $VERSION == *-* ]] ; then elif [[ $VERSION == *-* ]] ; then
kind=nightly kind=nightly
@@ -866,7 +876,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }} RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read RCLONE_CONFIG_OBJSTORE_ACL: public-read
with: with:
args: sync objstore:syncthing-apt/dists dists args: sync objstore:apt/dists dists
- name: Update archive - name: Update archive
uses: docker://ghcr.io/kastelo/ezapt:latest uses: docker://ghcr.io/kastelo/ezapt:latest
@@ -889,7 +899,7 @@ jobs:
RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }} RCLONE_CONFIG_OBJSTORE_REGION: ${{ secrets.S3_REGION }}
RCLONE_CONFIG_OBJSTORE_ACL: public-read RCLONE_CONFIG_OBJSTORE_ACL: public-read
with: with:
args: sync dists -v objstore:syncthing-apt/dists args: sync -v --no-update-modtime dists objstore:apt/dists
# #
# Build and push to Docker Hub # Build and push to Docker Hub
@@ -898,10 +908,8 @@ jobs:
docker-syncthing: docker-syncthing:
name: Build and push Docker images name: Build and push Docker images
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' 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 environment: docker
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
permissions: permissions:
contents: read contents: read
packages: write packages: write
@@ -914,13 +922,13 @@ jobs:
include: include:
- pkg: syncthing - pkg: syncthing
dockerfile: Dockerfile dockerfile: Dockerfile
image: syncthing image: syncthing/syncthing
- pkg: strelaysrv - pkg: strelaysrv
dockerfile: Dockerfile.strelaysrv dockerfile: Dockerfile.strelaysrv
image: relaysrv image: syncthing/relaysrv
- pkg: stdiscosrv - pkg: stdiscosrv
dockerfile: Dockerfile.stdiscosrv dockerfile: Dockerfile.stdiscosrv
image: discosrv image: syncthing/discosrv
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -938,8 +946,6 @@ jobs:
go version go version
echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV echo "GO_VERSION=$(go version | sed 's#^.*go##;s# .*##')" >> $GITHUB_ENV
- uses: mlugg/setup-zig@v1
- uses: actions/cache@v4 - uses: actions/cache@v4
with: with:
path: | path: |
@@ -947,34 +953,33 @@ jobs:
~/go/pkg/mod ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }} key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-docker-${{ matrix.pkg }}-${{ hashFiles('**/go.sum') }}
- name: Build binaries (CGO) - name: Build binaries
run: | run: |
# amd64 for arch in amd64 arm64 arm; do
go run build.go -goos linux -goarch amd64 -tags "${{env.TAGS}}" -cc "zig cc -target x86_64-linux-musl" -no-upgrade build ${{ matrix.pkg }} go run build.go -goos linux -goarch "$arch" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-amd64 mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-"$arch"
done
# arm64
go run build.go -goos linux -goarch arm64 -tags "${{env.TAGS}}" -cc "zig cc -target aarch64-linux-musl" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm64
# arm
go run build.go -goos linux -goarch arm -tags "${{env.TAGS}}" -cc "zig cc -target arm-linux-musleabi" -no-upgrade build ${{ matrix.pkg }}
mv ${{ matrix.pkg }} ${{ matrix.pkg }}-linux-arm
env: env:
CGO_ENABLED: "1" CGO_ENABLED: "0"
BUILD_USER: docker BUILD_USER: docker
EXTRA_LDFLAGS: "-linkmode=external -extldflags=-static"
- name: Check if we will be able to push images
run: |
if [[ "${{ secrets.DOCKERHUB_TOKEN }}" != "" ]]; then
echo "DOCKER_PUSH=true" >> $GITHUB_ENV;
fi
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
if: env.DOCKERHUB_USERNAME != '' if: env.DOCKER_PUSH == 'true'
with: with:
registry: docker.io registry: docker.io
username: ${{ env.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR - name: Login to GHCR
uses: docker/login-action@v3 uses: docker/login-action@v3
if: env.DOCKER_PUSH == 'true'
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -987,31 +992,18 @@ jobs:
run: | run: |
version=$(go run build.go version) version=$(go run build.go version)
version=${version#v} version=${version#v}
repo=ghcr.io/${{ github.repository_owner }}/${{ matrix.image }}
ref="${{github.ref_name}}"
ref=${ref//\//-} # slashes to dashes
# List of tags for ghcr.io
if [[ $version == @([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]) ]] ; then if [[ $version == @([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]).@([0-9]|[0-9][0-9]) ]] ; then
echo Release version, pushing to :latest and version tags
major=${version%.*.*} major=${version%.*.*}
minor=${version%.*} minor=${version%.*}
tags=$repo:$version,$repo:$major,$repo:$minor,$repo:latest tags=docker.io/${{ matrix.image }}:$version,ghcr.io/${{ matrix.image }}:$version,docker.io/${{ matrix.image }}:$major,ghcr.io/${{ matrix.image }}:$major,docker.io/${{ matrix.image }}:$minor,ghcr.io/${{ matrix.image }}:$minor,docker.io/${{ matrix.image }}:latest,ghcr.io/${{ matrix.image }}:latest
elif [[ $version == *-rc.@([0-9]|[0-9][0-9]) ]] ; then elif [[ $version == *-rc.@([0-9]|[0-9][0-9]) ]] ; then
tags=$repo:$version,$repo:rc echo Release candidate, pushing to :rc and version tags
elif [[ $ref == "main" ]] ; then tags=docker.io/${{ matrix.image }}:$version,ghcr.io/${{ matrix.image }}:$version,docker.io/${{ matrix.image }}:rc,ghcr.io/${{ matrix.image }}:rc
tags=$repo:edge
else else
tags=$repo:$ref echo Development version, pushing to :edge
tags=docker.io/${{ matrix.image }}:edge,ghcr.io/${{ matrix.image }}:edge
fi fi
# If we have a Docker Hub secret, also push to there.
if [[ $DOCKERHUB_USERNAME != "" ]] ; then
dockerhubtags="${tags//ghcr.io\/syncthing/docker.io\/syncthing}"
tags="$tags,$dockerhubtags"
fi
echo Pushing to $tags
echo "DOCKER_TAGS=$tags" >> $GITHUB_ENV echo "DOCKER_TAGS=$tags" >> $GITHUB_ENV
echo "VERSION=$version" >> $GITHUB_ENV echo "VERSION=$version" >> $GITHUB_ENV
@@ -1021,8 +1013,8 @@ jobs:
context: . context: .
file: ${{ matrix.dockerfile }} file: ${{ matrix.dockerfile }}
platforms: linux/amd64,linux/arm64,linux/arm/7 platforms: linux/amd64,linux/arm64,linux/arm/7
push: ${{ env.DOCKER_PUSH == 'true' }}
tags: ${{ env.DOCKER_TAGS }} tags: ${{ env.DOCKER_TAGS }}
push: true
labels: | labels: |
org.opencontainers.image.version=${{ env.VERSION }} org.opencontainers.image.version=${{ env.VERSION }}
org.opencontainers.image.revision=${{ github.sha }} org.opencontainers.image.revision=${{ github.sha }}
+49
View File
@@ -0,0 +1,49 @@
name: Run PR linters
on:
pull_request:
workflow_dispatch:
permissions:
contents: read
pull-requests: read
jobs:
#
# golangci-lint runs a suite of static analysis checks on the code
#
golangci:
runs-on: ubuntu-latest
name: Golangci-lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- name: ensure asset generation
run: go run build.go assets
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
with:
only-new-issues: true
#
# Meta checks for formatting, copyright, etc
#
meta:
name: Meta checks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 'stable'
- run: |
go run build.go assets
go test -v ./meta
+27
View 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
View 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
View File
@@ -17,4 +17,5 @@ deb
*.bz2 *.bz2
/repos /repos
/proto/scripts/protoc-gen-gosyncthing /proto/scripts/protoc-gen-gosyncthing
/gui/next-gen-gui
/compat.json /compat.json
+35 -13
View File
@@ -1,45 +1,67 @@
version: "2"
linters: linters:
enable-all: true default: all
disable: disable:
- cyclop - cyclop
- depguard - depguard
- err113
- exhaustive - exhaustive
- exhaustruct - exhaustruct
- forbidigo
- funlen - funlen
- gci
- gochecknoglobals - gochecknoglobals
- gochecknoinits - gochecknoinits
- gocognit - gocognit
- goconst - goconst
- gocyclo - gocyclo
- godot
- godox - godox
- gofmt
- goimports
- gomoddirectives - gomoddirectives
- inamedparam - inamedparam
- interfacebloat - interfacebloat
- ireturn - ireturn
- lll - lll
- maintidx - maintidx
- mnd
- musttag - musttag
- nestif - nestif
- nlreturn - nlreturn
- nonamedreturns - nonamedreturns
- paralleltest - paralleltest
- prealloc - prealloc
- predeclared
- protogetter - protogetter
- scopelint - recvcheck
- revive
- tagalign - tagalign
- tagliatelle - tagliatelle
- testpackage - testpackage
- usetesting # go 1.24
- varnamelen - varnamelen
- whitespace
- wrapcheck - wrapcheck
- wsl - wsl
exclusions:
issues: generated: lax
exclude-dirs: presets:
- internal/gen - comments
- cmd/dev - common-false-positives
- repos - 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$
+9
View File
@@ -48,6 +48,7 @@ Arkadiusz Tymiński <gevleeog@gmail.com>
Aroun <login@b-vo.fr> Aroun <login@b-vo.fr>
Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com> Arthur Axel fREW Schmidt (frioux) <frew@afoolishmanifesto.com> <frioux@gmail.com>
Artur Zubilewicz <AkaZecik@users.noreply.github.com> Artur Zubilewicz <AkaZecik@users.noreply.github.com>
Ashish Bhate <bhate.ashish@gmail.com>
Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks> Audrius Butkevicius (AudriusButkevicius) <audrius.butkevicius@gmail.com> <github@audrius.rocks>
Aurélien Rainone <476650+arl@users.noreply.github.com> Aurélien Rainone <476650+arl@users.noreply.github.com>
BAHADIR YILMAZ <bahadiryilmaz32@gmail.com> BAHADIR YILMAZ <bahadiryilmaz32@gmail.com>
@@ -113,6 +114,7 @@ diemade <spamkill@posteo.ch>
digital <didev@dinid.net> digital <didev@dinid.net>
Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com>
Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com> Dmitry Saveliev (dsaveliev) <d.e.saveliev@gmail.com>
domain <32405309+szu17dmy@users.noreply.github.com>
Domenic Horner <domenic@tgxn.net> Domenic Horner <domenic@tgxn.net>
Dominik Heidler (asdil12) <dominik@heidler.eu> Dominik Heidler (asdil12) <dominik@heidler.eu>
Elias Jarlebring (jarlebring) <jarlebring@gmail.com> Elias Jarlebring (jarlebring) <jarlebring@gmail.com>
@@ -147,6 +149,7 @@ Gusted <postmaster@gusted.xyz> <williamzijl7@hotmail.com>
Han Boetes <han@boetes.org> Han Boetes <han@boetes.org>
HansK-p <42314815+HansK-p@users.noreply.github.com> HansK-p <42314815+HansK-p@users.noreply.github.com>
Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com> Harrison Jones (harrisonhjones) <harrisonhjones@users.noreply.github.com>
Hazem Krimi <me@hazemkrimi.tech>
Heiko Zuerker (Smiley73) <heiko@zuerker.org> Heiko Zuerker (Smiley73) <heiko@zuerker.org>
Hireworks <129852174+hireworksltd@users.noreply.github.com> Hireworks <129852174+hireworksltd@users.noreply.github.com>
Hugo Locurcio <hugo.locurcio@hugo.pro> Hugo Locurcio <hugo.locurcio@hugo.pro>
@@ -221,8 +224,10 @@ luzpaz <luzpaz@users.noreply.github.com>
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com> Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name> Marc Laporte (marclaporte) <marc@marclaporte.com> <marc@laporte.name>
Marc Pujol (kilburn) <kilburn@la3.org> Marc Pujol (kilburn) <kilburn@la3.org>
Marcel Meyer <mm.marcelmeyer@gmail.com>
Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com> Marcin Dziadus (marcindziadus) <dziadus.marcin@gmail.com>
marco-m <marco.molteni@laposte.net> marco-m <marco.molteni@laposte.net>
Marcus B Spencer <marcus@marcusspencer.xyz> <marcus@marcusspencer.us>
Marcus Legendre <marcus.legendre@gmail.com> Marcus Legendre <marcus.legendre@gmail.com>
Mario Majila <mariustshipichik@gmail.com> Mario Majila <mariustshipichik@gmail.com>
Mark Pulford (mpx) <mark@kyne.com.au> Mark Pulford (mpx) <mark@kyne.com.au>
@@ -276,6 +281,7 @@ Oyebanji Jacob Mayowa <oyebanji05@gmail.com>
Pablo <pbaeyens31+github@gmail.com> Pablo <pbaeyens31+github@gmail.com>
Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com> Pascal Jungblut (pascalj) <github@pascalj.com> <mail@pascal-jungblut.com>
Paul Brit <paulbrit44@gmail.com> Paul Brit <paulbrit44@gmail.com>
Paul Donald <newtwen+github@gmail.com>
Pawel Palenica (qepasa) <pawelpalenica11@gmail.com> Pawel Palenica (qepasa) <pawelpalenica11@gmail.com>
Paweł Rozlach <vespian@users.noreply.github.com> Paweł Rozlach <vespian@users.noreply.github.com>
perewa <cavalcante.ten@gmail.com> perewa <cavalcante.ten@gmail.com>
@@ -291,6 +297,7 @@ Pier Paolo Ramon <ramonpierre@gmail.com>
Piotr Bejda (piobpl) <piotrb10@gmail.com> Piotr Bejda (piobpl) <piotrb10@gmail.com>
polyfloyd <polyfloyd@users.noreply.github.com> polyfloyd <polyfloyd@users.noreply.github.com>
Pramodh KP (pramodhkp) <pramodh.p@directi.com> <1507241+pramodhkp@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> Quentin Hibon <qh.public@yahoo.com>
Rahmi Pruitt <rjpruitt16@gmail.com> Rahmi Pruitt <rjpruitt16@gmail.com>
red_led <red-led@users.noreply.github.com> red_led <red-led@users.noreply.github.com>
@@ -323,8 +330,10 @@ Suhas Gundimeda (snugghash) <suhas.gundimeda@gmail.com> <snugghash@gmail.com>
Sven Bachmann <dev@mcbachmann.de> Sven Bachmann <dev@mcbachmann.de>
Syncthing Automation <automation@syncthing.net> Syncthing Automation <automation@syncthing.net>
Syncthing Release Automation <release@syncthing.net> Syncthing Release Automation <release@syncthing.net>
Sébastien WENSKE <sebastien@wenske.fr>
Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com> Taylor Khan (nelsonkhan) <nelsonkhan@gmail.com>
Terrance <git@terrance.allofti.me> Terrance <git@terrance.allofti.me>
TheCreeper <TheCreeper@users.noreply.github.com>
Thomas <9749173+uhthomas@users.noreply.github.com> Thomas <9749173+uhthomas@users.noreply.github.com>
Thomas Hipp <thomashipp@gmail.com> Thomas Hipp <thomashipp@gmail.com>
Tim Abell (timabell) <tim@timwise.co.uk> Tim Abell (timabell) <tim@timwise.co.uk>
+85 -30
View File
@@ -38,26 +38,27 @@ import (
) )
var ( var (
goarch string goarch string
goos string goos string
noupgrade bool noupgrade bool
version string version string
goCmd string goCmd string
race bool race bool
debug = os.Getenv("BUILDDEBUG") != "" debug = os.Getenv("BUILDDEBUG") != ""
extraTags string extraTags string
installSuffix string installSuffix string
pkgdir string pkgdir string
cc string cc string
run string run string
benchRun string benchRun string
buildOut string buildOut string
debugBinary bool debugBinary bool
coverage bool coverage bool
long bool long bool
timeout = "120s" timeout = "120s"
longTimeout = "600s" longTimeout = "600s"
numVersions = 5 numVersions = 5
withNextGenGUI = os.Getenv("BUILD_NEXT_GEN_GUI") != ""
) )
type target struct { type target struct {
@@ -288,10 +289,10 @@ func runCommand(cmd string, target target) {
build(target, tags) build(target, tags)
case "test": case "test":
test(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") test(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
case "bench": case "bench":
bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/internal/...", "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...") bench(strings.Fields(extraTags), "github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
case "integration": case "integration":
integration(false) integration(false)
@@ -329,7 +330,7 @@ func runCommand(cmd string, target target) {
writeCompatJSON() writeCompatJSON()
case "deb": case "deb":
buildDeb(target) buildDeb(target, tags)
case "vet": case "vet":
metalintShort() metalintShort()
@@ -379,6 +380,7 @@ func parseFlags() {
flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command") flag.IntVar(&numVersions, "num-versions", numVersions, "Number of versions for changelog command")
flag.StringVar(&run, "run", "", "Specify which tests to run") flag.StringVar(&run, "run", "", "Specify which tests to run")
flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks to run") flag.StringVar(&benchRun, "bench", "", "Specify which benchmarks to run")
flag.BoolVar(&withNextGenGUI, "with-next-gen-gui", withNextGenGUI, "Also build 'newgui'")
flag.StringVar(&buildOut, "build-out", "", "Set the '-o' value for 'go build'") flag.StringVar(&buildOut, "build-out", "", "Set the '-o' value for 'go build'")
flag.Parse() flag.Parse()
} }
@@ -451,6 +453,10 @@ func benchArgs() []string {
} }
func install(target target, tags []string) { func install(target target, tags []string) {
if (target.name == "syncthing" || target.name == "") && !withNextGenGUI {
log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.")
}
lazyRebuildAssets() lazyRebuildAssets()
tags = append(target.tags, tags...) tags = append(target.tags, tags...)
@@ -474,12 +480,16 @@ func install(target target, tags []string) {
defer shouldCleanupSyso(sysoPath) defer shouldCleanupSyso(sysoPath)
} }
args := []string{"install"} args := []string{"install", "-v"}
args = appendParameters(args, tags, target.buildPkgs...) args = appendParameters(args, tags, target.buildPkgs...)
runPrint(goCmd, args...) runPrint(goCmd, args...)
} }
func build(target target, tags []string) { func build(target target, tags []string) {
if (target.name == "syncthing" || target.name == "") && !withNextGenGUI {
log.Println("Notice: Next generation GUI will not be built; see --with-next-gen-gui.")
}
lazyRebuildAssets() lazyRebuildAssets()
tags = append(target.tags, tags...) tags = append(target.tags, tags...)
@@ -502,7 +512,7 @@ func build(target target, tags []string) {
defer shouldCleanupSyso(sysoPath) defer shouldCleanupSyso(sysoPath)
} }
args := []string{"build"} args := []string{"build", "-v"}
if buildOut != "" { if buildOut != "" {
args = append(args, "-o", buildOut) args = append(args, "-o", buildOut)
} }
@@ -514,6 +524,13 @@ func setBuildEnvVars() {
os.Setenv("GOOS", goos) os.Setenv("GOOS", goos)
os.Setenv("GOARCH", goarch) os.Setenv("GOARCH", goarch)
os.Setenv("CC", cc) os.Setenv("CC", cc)
if os.Getenv("CGO_ENABLED") == "" {
switch goos {
case "darwin", "solaris":
default:
os.Setenv("CGO_ENABLED", "0")
}
}
} }
func appendParameters(args []string, tags []string, pkgs ...string) []string { func appendParameters(args []string, tags []string, pkgs ...string) []string {
@@ -592,7 +609,7 @@ func buildZip(target target, tags []string) {
fmt.Println(filename) fmt.Println(filename)
} }
func buildDeb(target target) { func buildDeb(target target, tags []string) {
os.RemoveAll("deb") os.RemoveAll("deb")
// "goarch" here is set to whatever the Debian packages expect. We correct // "goarch" here is set to whatever the Debian packages expect. We correct
@@ -606,7 +623,7 @@ func buildDeb(target target) {
goarch = "arm" goarch = "arm"
} }
build(target, []string{"noupgrade"}) build(target, append(tags, "noupgrade"))
for i := range target.installationFiles { for i := range target.installationFiles {
target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1) target.installationFiles[i].src = strings.Replace(target.installationFiles[i].src, "{{binary}}", target.BinaryName(), 1)
@@ -628,6 +645,9 @@ func buildDeb(target target) {
// than just 0.14.26. This rectifies that. // than just 0.14.26. This rectifies that.
debver = strings.Replace(debver, "-", "~", -1) debver = strings.Replace(debver, "-", "~", -1)
} }
if strings.Contains(debver, "_") {
debver = strings.Replace(debver, "_", "~", -1)
}
args := []string{ args := []string{
"-t", "deb", "-t", "deb",
"-s", "dir", "-s", "dir",
@@ -729,9 +749,12 @@ func shouldBuildSyso(dir string) (string, error) {
sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso") sysoPath := filepath.Join(dir, "cmd", "syncthing", "resource.syso")
// See https://github.com/josephspurrier/goversioninfo#command-line-flags // See https://github.com/josephspurrier/goversioninfo#command-line-flags
arm := strings.HasPrefix(goarch, "arm") armOption := ""
a64 := strings.Contains(goarch, "64") if strings.Contains(goarch, "arm") {
if _, err := runError("goversioninfo", "-o", sysoPath, fmt.Sprintf("-arm=%v", arm), fmt.Sprintf("-64=%v", a64)); err != nil { armOption = "-arm=true"
}
if _, err := runError("goversioninfo", "-o", sysoPath, armOption); err != nil {
return "", errors.New("failed to create " + sysoPath + ": " + err.Error()) return "", errors.New("failed to create " + sysoPath + ": " + err.Error())
} }
@@ -803,11 +826,43 @@ func lazyRebuildAssets() {
shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") || shouldRebuild := shouldRebuildAssets("lib/api/auto/gui.files.go", "gui") ||
shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui") shouldRebuildAssets("cmd/infra/strelaypoolsrv/auto/gui.files.go", "cmd/infra/strelaypoolsrv/gui")
if withNextGenGUI {
shouldRebuild = buildNextGenGUI() || shouldRebuild
}
if shouldRebuild { if shouldRebuild {
rebuildAssets() rebuildAssets()
} }
} }
func buildNextGenGUI() bool {
// Check if we need to run the npm process, and if so also set the flag
// to rebuild Go assets afterwards. The index.html is regenerated every
// time by the build process. This assumes the new GUI ends up in
// next-gen-gui/dist/next-gen-gui.
if !shouldRebuildAssets("gui/next-gen-gui/index.html", "next-gen-gui") {
// The GUI is up to date.
return false
}
runPrintInDir("next-gen-gui", "npm", "install")
runPrintInDir("next-gen-gui", "npm", "run", "build", "--", "--prod", "--subresource-integrity")
rmr("gui/tech-ui")
for _, src := range listFiles("next-gen-gui/dist") {
rel, _ := filepath.Rel("next-gen-gui/dist", src)
dst := filepath.Join("gui", rel)
if err := copyFile(src, dst, 0o644); err != nil {
fmt.Println("copy:", err)
os.Exit(1)
}
}
return true
}
func shouldRebuildAssets(target, srcdir string) bool { func shouldRebuildAssets(target, srcdir string) bool {
info, err := os.Stat(target) info, err := os.Stat(target)
if err != nil { if err != nil {
+1
View File
@@ -23,6 +23,7 @@ case "${1:-default}" in
prerelease) prerelease)
script authors script authors
script copyrights
build weblate build weblate
pushd man ; ./refresh.sh ; popd pushd man ; ./refresh.sh ; popd
git add -A gui man AUTHORS git add -A gui man AUTHORS
+1 -1
View File
@@ -72,7 +72,7 @@ func main() {
if *standardBlocks || blockSize < protocol.MinBlockSize { if *standardBlocks || blockSize < protocol.MinBlockSize {
blockSize = protocol.BlockSize(fi.Size()) blockSize = protocol.BlockSize(fi.Size())
} }
bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil) bs, err := scanner.Blocks(context.TODO(), fd, blockSize, fi.Size(), nil, true)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
+4 -3
View File
@@ -8,6 +8,7 @@ package main
import ( import (
"bytes" "bytes"
"cmp"
"compress/gzip" "compress/gzip"
"context" "context"
"io" "io"
@@ -15,7 +16,7 @@ import (
"math" "math"
"os" "os"
"path/filepath" "path/filepath"
"sort" "slices"
"time" "time"
) )
@@ -177,8 +178,8 @@ func (d *diskStore) inventory() error {
}) })
return nil return nil
}) })
sort.Slice(d.currentFiles, func(i, j int) bool { slices.SortFunc(d.currentFiles, func(a, b currentFile) int {
return d.currentFiles[i].mtime < d.currentFiles[j].mtime return cmp.Compare(a.mtime, b.mtime)
}) })
var oldest time.Duration var oldest time.Duration
if len(d.currentFiles) > 0 { if len(d.currentFiles) > 0 {
+1 -1
View File
@@ -10,7 +10,7 @@ to NAT or firewall issues.
There is very little reason why you'd want to run this yourself, as There is very little reason why you'd want to run this yourself, as
`relaypoolsrv` is just used for announcement and lookup of public relay `relaypoolsrv` is just used for announcement and lookup of public relay
servers. If you are looking to setup a private or a public relay, please servers. If you are looking to set up a private or a public relay, please
check the documentation for check the documentation for
[relaysrv](https://github.com/syncthing/relaysrv), which also explains how [relaysrv](https://github.com/syncthing/relaysrv), which also explains how
to join the default public pool. to join the default public pool.
+7
View File
@@ -29,6 +29,7 @@ import (
_ "github.com/syncthing/syncthing/lib/automaxprocs" _ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/geoip" "github.com/syncthing/syncthing/lib/geoip"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/relay/client" "github.com/syncthing/syncthing/lib/relay/client"
"github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/sync"
"github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/tlsutil"
@@ -110,6 +111,7 @@ var (
requestProcessors = 8 requestProcessors = 8
geoipLicenseKey = os.Getenv("GEOIP_LICENSE_KEY") geoipLicenseKey = os.Getenv("GEOIP_LICENSE_KEY")
geoipAccountID, _ = strconv.Atoi(os.Getenv("GEOIP_ACCOUNT_ID")) geoipAccountID, _ = strconv.Atoi(os.Getenv("GEOIP_ACCOUNT_ID"))
maxRelaysReturned = 100
requests chan request requests chan request
@@ -141,6 +143,7 @@ func main() {
flag.IntVar(&requestQueueLen, "request-queue", requestQueueLen, "Queue length for incoming test requests") flag.IntVar(&requestQueueLen, "request-queue", requestQueueLen, "Queue length for incoming test requests")
flag.IntVar(&requestProcessors, "request-processors", requestProcessors, "Number of request processor routines") flag.IntVar(&requestProcessors, "request-processors", requestProcessors, "Number of request processor routines")
flag.StringVar(&geoipLicenseKey, "geoip-license-key", geoipLicenseKey, "License key for GeoIP database") flag.StringVar(&geoipLicenseKey, "geoip-license-key", geoipLicenseKey, "License key for GeoIP database")
flag.IntVar(&maxRelaysReturned, "max-relays-returned", maxRelaysReturned, "Maximum number of relays returned for a normal endpoint query")
flag.Parse() flag.Parse()
@@ -331,6 +334,10 @@ func handleEndpointShort(rw http.ResponseWriter, r *http.Request) {
relays = append(relays, relayShort{URL: slimURL(r.URL)}) relays = append(relays, relayShort{URL: slimURL(r.URL)})
} }
mut.RUnlock() mut.RUnlock()
if len(relays) > maxRelaysReturned {
rand.Shuffle(relays)
relays = relays[:maxRelaysReturned]
}
_ = json.NewEncoder(rw).Encode(map[string][]relayShort{ _ = json.NewEncoder(rw).Encode(map[string][]relayShort{
"relays": relays, "relays": relays,
+33 -10
View File
@@ -201,17 +201,21 @@ func (p *proxy) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// looking for a prerelease at all. // looking for a prerelease at all.
func filterForLatest(rels []upgrade.Release) []upgrade.Release { func filterForLatest(rels []upgrade.Release) []upgrade.Release {
var filtered []upgrade.Release var filtered []upgrade.Release
var havePre bool havePre := make(map[string]bool)
haveStable := make(map[string]bool)
for _, rel := range rels { for _, rel := range rels {
if !rel.Prerelease { major, _, _ := strings.Cut(rel.Tag, ".")
// We found a stable version, we're good now. if !rel.Prerelease && !haveStable[major] {
// Remember the first non-pre for each major
filtered = append(filtered, rel) filtered = append(filtered, rel)
break haveStable[major] = true
continue
} }
if rel.Prerelease && !havePre { if rel.Prerelease && !havePre[major] && !haveStable[major] {
// We remember the first prerelease we find. // We remember the first prerelease we find, unless we've
// already found a non-pre of the same major.
filtered = append(filtered, rel) filtered = append(filtered, rel)
havePre = true havePre[major] = true
} }
} }
return filtered return filtered
@@ -258,9 +262,10 @@ func filterForCompabitility(rels []upgrade.Release, ua, osv string) []upgrade.Re
} }
type cachedReleases struct { type cachedReleases struct {
url string url string
mut sync.RWMutex mut sync.RWMutex
current []upgrade.Release current []upgrade.Release
latestRel, latestPre string
} }
func (c *cachedReleases) Releases() []upgrade.Release { func (c *cachedReleases) Releases() []upgrade.Release {
@@ -274,8 +279,26 @@ func (c *cachedReleases) Update(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
latestRel, latestPre := "", ""
for _, rel := range rels {
if !rel.Prerelease && latestRel == "" {
latestRel = rel.Tag
}
if rel.Prerelease && latestPre == "" {
latestPre = rel.Tag
}
if latestRel != "" && latestPre != "" {
break
}
}
c.mut.Lock() c.mut.Lock()
c.current = rels c.current = rels
if latestRel != c.latestRel || latestPre != c.latestPre {
metricLatestReleaseInfo.DeleteLabelValues(c.latestRel, c.latestPre)
metricLatestReleaseInfo.WithLabelValues(latestRel, latestPre).Set(1)
c.latestRel = latestRel
c.latestPre = latestPre
}
c.mut.Unlock() c.mut.Unlock()
return nil return nil
} }
+6
View File
@@ -27,4 +27,10 @@ var (
Subsystem: "upgrade", Subsystem: "upgrade",
Name: "http_requests", Name: "http_requests",
}, []string{"target", "result"}) }, []string{"target", "result"})
metricLatestReleaseInfo = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "syncthing",
Subsystem: "upgrade",
Name: "latest_release_info",
Help: "Release information",
}, []string{"latest_release", "latest_pre"})
) )
+34 -18
View File
@@ -26,9 +26,11 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/blob/azureblob"
"github.com/syncthing/syncthing/internal/blob/s3"
"github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/geoip" "github.com/syncthing/syncthing/lib/geoip"
"github.com/syncthing/syncthing/lib/s3"
"github.com/syncthing/syncthing/lib/ur/contract" "github.com/syncthing/syncthing/lib/ur/contract"
) )
@@ -40,11 +42,15 @@ type CLI struct {
DumpFile string `env:"UR_DUMP_FILE" default:"reports.jsons.gz"` DumpFile string `env:"UR_DUMP_FILE" default:"reports.jsons.gz"`
DumpInterval time.Duration `env:"UR_DUMP_INTERVAL" default:"5m"` DumpInterval time.Duration `env:"UR_DUMP_INTERVAL" default:"5m"`
S3Endpoint string `name:"s3-endpoint" hidden:"true" env:"UR_S3_ENDPOINT"` S3Endpoint string `name:"s3-endpoint" env:"UR_S3_ENDPOINT"`
S3Region string `name:"s3-region" hidden:"true" env:"UR_S3_REGION"` S3Region string `name:"s3-region" env:"UR_S3_REGION"`
S3Bucket string `name:"s3-bucket" hidden:"true" env:"UR_S3_BUCKET"` S3Bucket string `name:"s3-bucket" env:"UR_S3_BUCKET"`
S3AccessKeyID string `name:"s3-access-key-id" hidden:"true" env:"UR_S3_ACCESS_KEY_ID"` S3AccessKeyID string `name:"s3-access-key-id" env:"UR_S3_ACCESS_KEY_ID"`
S3SecretKey string `name:"s3-secret-key" hidden:"true" env:"UR_S3_SECRET_KEY"` S3SecretKey string `name:"s3-secret-key" env:"UR_S3_SECRET_KEY"`
AzureBlobAccount string `name:"azure-blob-account" env:"UR_AZUREBLOB_ACCOUNT"`
AzureBlobKey string `name:"azure-blob-key" env:"UR_AZUREBLOB_KEY"`
AzureBlobContainer string `name:"azure-blob-container" env:"UR_AZUREBLOB_CONTAINER"`
} }
var ( var (
@@ -77,6 +83,7 @@ var (
{regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"}, {regexp.MustCompile(`\ssyncthing@archlinux`), "Arch (3rd party)"},
{regexp.MustCompile(`@debian`), "Debian (3rd party)"}, {regexp.MustCompile(`@debian`), "Debian (3rd party)"},
{regexp.MustCompile(`@fedora`), "Fedora (3rd party)"}, {regexp.MustCompile(`@fedora`), "Fedora (3rd party)"},
{regexp.MustCompile(`@openSUSE`), "openSUSE (3rd party)"},
{regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"}, {regexp.MustCompile(`\sbrew@`), "Homebrew (3rd party)"},
{regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"}, {regexp.MustCompile(`\sroot@buildkitsandbox`), "LinuxServer.io (3rd party)"},
{regexp.MustCompile(`\sports@freebsd`), "FreeBSD (3rd party)"}, {regexp.MustCompile(`\sports@freebsd`), "FreeBSD (3rd party)"},
@@ -119,19 +126,25 @@ func (cli *CLI) Run() error {
go geo.Serve(context.TODO()) go geo.Serve(context.TODO())
} }
// s3 // Blob storage
var s3sess *s3.Session var blobs blob.Store
if cli.S3Endpoint != "" { if cli.S3Endpoint != "" {
s3sess, err = s3.NewSession(cli.S3Endpoint, cli.S3Region, cli.S3Bucket, cli.S3AccessKeyID, cli.S3SecretKey) blobs, err = s3.NewSession(cli.S3Endpoint, cli.S3Region, cli.S3Bucket, cli.S3AccessKeyID, cli.S3SecretKey)
if err != nil { if err != nil {
slog.Error("Failed to create S3 session", "error", err) slog.Error("Failed to create S3 session", "error", err)
return err return err
} }
} else if cli.AzureBlobAccount != "" {
blobs, err = azureblob.NewBlobStore(cli.AzureBlobAccount, cli.AzureBlobKey, cli.AzureBlobContainer)
if err != nil {
slog.Error("Failed to create Azure blob store", "error", err)
return err
}
} }
if _, err := os.Stat(cli.DumpFile); err != nil && s3sess != nil { if _, err := os.Stat(cli.DumpFile); err != nil && blobs != nil {
if err := cli.downloadDumpFile(s3sess); err != nil { if err := cli.downloadDumpFile(blobs); err != nil {
slog.Error("Failed to download dump file", "error", err) slog.Error("Failed to download dump file", "error", err)
} }
} }
@@ -153,7 +166,7 @@ func (cli *CLI) Run() error {
go func() { go func() {
for range time.Tick(cli.DumpInterval) { for range time.Tick(cli.DumpInterval) {
if err := cli.saveDumpFile(srv, s3sess); err != nil { if err := cli.saveDumpFile(srv, blobs); err != nil {
slog.Error("Failed to write dump file", "error", err) slog.Error("Failed to write dump file", "error", err)
} }
} }
@@ -192,8 +205,8 @@ func (cli *CLI) Run() error {
return metricsSrv.Serve(urListener) return metricsSrv.Serve(urListener)
} }
func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error { func (cli *CLI) downloadDumpFile(blobs blob.Store) error {
latestKey, err := s3sess.LatestKey() latestKey, err := blobs.LatestKey(context.Background())
if err != nil { if err != nil {
return fmt.Errorf("list latest S3 key: %w", err) return fmt.Errorf("list latest S3 key: %w", err)
} }
@@ -201,7 +214,7 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
if err != nil { if err != nil {
return fmt.Errorf("create dump file: %w", err) return fmt.Errorf("create dump file: %w", err)
} }
if err := s3sess.Download(fd, latestKey); err != nil { if err := blobs.Download(context.Background(), latestKey, fd); err != nil {
_ = fd.Close() _ = fd.Close()
return fmt.Errorf("download dump file: %w", err) return fmt.Errorf("download dump file: %w", err)
} }
@@ -212,7 +225,7 @@ func (cli *CLI) downloadDumpFile(s3sess *s3.Session) error {
return nil return nil
} }
func (cli *CLI) saveDumpFile(srv *server, s3sess *s3.Session) error { func (cli *CLI) saveDumpFile(srv *server, blobs blob.Store) error {
fd, err := os.Create(cli.DumpFile + ".tmp") fd, err := os.Create(cli.DumpFile + ".tmp")
if err != nil { if err != nil {
return fmt.Errorf("creating dump file: %w", err) return fmt.Errorf("creating dump file: %w", err)
@@ -233,13 +246,13 @@ func (cli *CLI) saveDumpFile(srv *server, s3sess *s3.Session) error {
} }
slog.Info("Dump file saved") slog.Info("Dump file saved")
if s3sess != nil { if blobs != nil {
key := fmt.Sprintf("reports-%s.jsons.gz", time.Now().UTC().Format("2006-01-02")) key := fmt.Sprintf("reports-%s.jsons.gz", time.Now().UTC().Format("2006-01-02"))
fd, err := os.Open(cli.DumpFile) fd, err := os.Open(cli.DumpFile)
if err != nil { if err != nil {
return fmt.Errorf("opening dump file: %w", err) return fmt.Errorf("opening dump file: %w", err)
} }
if err := s3sess.Upload(fd, key); err != nil { if err := blobs.Upload(context.Background(), key, fd); err != nil {
return fmt.Errorf("uploading dump file: %w", err) return fmt.Errorf("uploading dump file: %w", err)
} }
_ = fd.Close() _ = fd.Close()
@@ -351,6 +364,9 @@ func (s *server) addReport(rep *contract.Report) bool {
break break
} }
} }
rep.DistDist = rep.Distribution
rep.DistOS = rep.OS
rep.DistArch = rep.Arch
_, loaded := s.reports.LoadAndStore(rep.UniqueID, rep) _, loaded := s.reports.LoadAndStore(rep.UniqueID, rep)
return loaded return loaded
+3 -3
View File
@@ -66,7 +66,7 @@ type contextKey int
const idKey contextKey = iota const idKey contextKey = iota
func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator, useHTTP, compression bool) *apiSrv { func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator, useHTTP, compression bool, desiredNotFoundRate float64) *apiSrv {
return &apiSrv{ return &apiSrv{
addr: addr, addr: addr,
cert: cert, cert: cert,
@@ -77,13 +77,13 @@ func newAPISrv(addr string, cert tls.Certificate, db database, repl replicator,
seenTracker: &retryAfterTracker{ seenTracker: &retryAfterTracker{
name: "seenTracker", name: "seenTracker",
bucketStarts: time.Now(), bucketStarts: time.Now(),
desiredRate: 250, desiredRate: desiredNotFoundRate / 2,
currentDelay: notFoundRetryUnknownMinSeconds, currentDelay: notFoundRetryUnknownMinSeconds,
}, },
notSeenTracker: &retryAfterTracker{ notSeenTracker: &retryAfterTracker{
name: "notSeenTracker", name: "notSeenTracker",
bucketStarts: time.Now(), bucketStarts: time.Now(),
desiredRate: 250, desiredRate: desiredNotFoundRate / 2,
currentDelay: notFoundRetryUnknownMaxSeconds / 2, currentDelay: notFoundRetryUnknownMaxSeconds / 2,
}, },
} }
+1 -1
View File
@@ -111,7 +111,7 @@ func BenchmarkAPIRequests(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
go db.Serve(ctx) go db.Serve(ctx)
api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true) api := newAPISrv("127.0.0.1:0", tls.Certificate{}, db, nil, true, true, 1000)
srv := httptest.NewServer(http.HandlerFunc(api.handler)) srv := httptest.NewServer(http.HandlerFunc(api.handler))
kf := b.TempDir() + "/cert" kf := b.TempDir() + "/cert"
+16 -16
View File
@@ -24,11 +24,11 @@ import (
"github.com/puzpuzpuz/xsync/v3" "github.com/puzpuzpuz/xsync/v3"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/gen/discosrv" "github.com/syncthing/syncthing/internal/gen/discosrv"
"github.com/syncthing/syncthing/internal/protoutil" "github.com/syncthing/syncthing/internal/protoutil"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/s3"
) )
type clock interface { type clock interface {
@@ -51,12 +51,12 @@ type inMemoryStore struct {
m *xsync.MapOf[protocol.DeviceID, *discosrv.DatabaseRecord] m *xsync.MapOf[protocol.DeviceID, *discosrv.DatabaseRecord]
dir string dir string
flushInterval time.Duration flushInterval time.Duration
s3 *s3.Session blobs blob.Store
objKey string objKey string
clock clock clock clock
} }
func newInMemoryStore(dir string, flushInterval time.Duration, s3sess *s3.Session) *inMemoryStore { func newInMemoryStore(dir string, flushInterval time.Duration, blobs blob.Store) *inMemoryStore {
hn, err := os.Hostname() hn, err := os.Hostname()
if err != nil { if err != nil {
hn = rand.String(8) hn = rand.String(8)
@@ -65,25 +65,25 @@ func newInMemoryStore(dir string, flushInterval time.Duration, s3sess *s3.Sessio
m: xsync.NewMapOf[protocol.DeviceID, *discosrv.DatabaseRecord](), m: xsync.NewMapOf[protocol.DeviceID, *discosrv.DatabaseRecord](),
dir: dir, dir: dir,
flushInterval: flushInterval, flushInterval: flushInterval,
s3: s3sess, blobs: blobs,
objKey: hn + ".db", objKey: hn + ".db",
clock: defaultClock{}, clock: defaultClock{},
} }
nr, err := s.read() nr, err := s.read()
if os.IsNotExist(err) && s3sess != nil { if os.IsNotExist(err) && blobs != nil {
// Try to read from AWS // Try to read from blob storage
latestKey, cerr := s3sess.LatestKey() latestKey, cerr := blobs.LatestKey(context.Background())
if cerr != nil { if cerr != nil {
log.Println("Error reading database from S3:", err) log.Println("Error finding database from blob storage:", cerr)
return s return s
} }
fd, cerr := os.Create(path.Join(s.dir, "records.db")) fd, cerr := os.Create(path.Join(s.dir, "records.db"))
if cerr != nil { if cerr != nil {
log.Println("Error creating database file:", err) log.Println("Error creating database file:", cerr)
return s return s
} }
if cerr := s3sess.Download(fd, latestKey); cerr != nil { if cerr := blobs.Download(context.Background(), latestKey, fd); cerr != nil {
log.Printf("Error reading database from S3: %v", err) log.Printf("Error downloading database from blob storage: %v", cerr)
} }
_ = fd.Close() _ = fd.Close()
nr, err = s.read() nr, err = s.read()
@@ -310,16 +310,16 @@ func (s *inMemoryStore) write() (err error) {
return err return err
} }
// Upload to S3 // Upload to blob storage
if s.s3 != nil { if s.blobs != nil {
fd, err = os.Open(dbf) fd, err = os.Open(dbf)
if err != nil { if err != nil {
log.Printf("Error uploading database to S3: %v", err) log.Printf("Error uploading database to blob storage: %v", err)
return nil return nil
} }
defer fd.Close() defer fd.Close()
if err := s.s3.Upload(fd, s.objKey); err != nil { if err := s.blobs.Upload(context.Background(), s.objKey, fd); err != nil {
log.Printf("Error uploading database to S3: %v", err) log.Printf("Error uploading database to blob storage: %v", err)
} }
log.Println("Finished uploading database") log.Println("Finished uploading database")
} }
+25 -16
View File
@@ -21,11 +21,13 @@ import (
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/thejerf/suture/v4" "github.com/thejerf/suture/v4"
"github.com/syncthing/syncthing/internal/blob"
"github.com/syncthing/syncthing/internal/blob/azureblob"
"github.com/syncthing/syncthing/internal/blob/s3"
_ "github.com/syncthing/syncthing/lib/automaxprocs" _ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/protocol" "github.com/syncthing/syncthing/lib/protocol"
"github.com/syncthing/syncthing/lib/rand" "github.com/syncthing/syncthing/lib/rand"
"github.com/syncthing/syncthing/lib/s3"
"github.com/syncthing/syncthing/lib/tlsutil" "github.com/syncthing/syncthing/lib/tlsutil"
) )
@@ -58,12 +60,13 @@ const (
var debug = false var debug = false
type CLI struct { type CLI struct {
Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"` Cert string `group:"Listen" help:"Certificate file" default:"./cert.pem" env:"DISCOVERY_CERT_FILE"`
Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"` Key string `group:"Listen" help:"Key file" default:"./key.pem" env:"DISCOVERY_KEY_FILE"`
HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"` HTTP bool `group:"Listen" help:"Listen on HTTP (behind an HTTPS proxy)" env:"DISCOVERY_HTTP"`
Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"` Compression bool `group:"Listen" help:"Enable GZIP compression of responses" env:"DISCOVERY_COMPRESSION"`
Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"` Listen string `group:"Listen" help:"Listen address" default:":8443" env:"DISCOVERY_LISTEN"`
MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"` MetricsListen string `group:"Listen" help:"Metrics listen address" env:"DISCOVERY_METRICS_LISTEN"`
DesiredNotFoundRate float64 `group:"Listen" help:"Desired maximum rate of not-found replies (/s)" default:"1000"`
DBDir string `group:"Database" help:"Database directory" default:"." env:"DISCOVERY_DB_DIR"` DBDir string `group:"Database" help:"Database directory" default:"." env:"DISCOVERY_DB_DIR"`
DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"` DBFlushInterval time.Duration `group:"Database" help:"Interval between database flushes" default:"5m" env:"DISCOVERY_DB_FLUSH_INTERVAL"`
@@ -74,6 +77,10 @@ type CLI struct {
DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"` DBS3AccessKeyID string `name:"db-s3-access-key-id" group:"Database (S3 backup)" hidden:"true" help:"S3 access key ID for database" env:"DISCOVERY_DB_S3_ACCESS_KEY_ID"`
DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"` DBS3SecretKey string `name:"db-s3-secret-key" group:"Database (S3 backup)" hidden:"true" help:"S3 secret key for database" env:"DISCOVERY_DB_S3_SECRET_KEY"`
DBAzureBlobAccount string `name:"db-azure-blob-account" env:"DISCOVERY_DB_AZUREBLOB_ACCOUNT"`
DBAzureBlobKey string `name:"db-azure-blob-key" env:"DISCOVERY_DB_AZUREBLOB_KEY"`
DBAzureBlobContainer string `name:"db-azure-blob-container" env:"DISCOVERY_DB_AZUREBLOB_CONTAINER"`
AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"` AMQPAddress string `group:"AMQP replication" hidden:"true" help:"Address to AMQP broker" env:"DISCOVERY_AMQP_ADDRESS"`
Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"` Debug bool `short:"d" help:"Print debug output" env:"DISCOVERY_DEBUG"`
@@ -117,18 +124,20 @@ func main() {
Timeout: 2 * time.Minute, Timeout: 2 * time.Minute,
}) })
// If configured, use S3 for database backups. // If configured, use blob storage for database backups.
var s3c *s3.Session var blobs blob.Store
var err error
if cli.DBS3Endpoint != "" { if cli.DBS3Endpoint != "" {
var err error blobs, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey)
s3c, err = s3.NewSession(cli.DBS3Endpoint, cli.DBS3Region, cli.DBS3Bucket, cli.DBS3AccessKeyID, cli.DBS3SecretKey) } else if cli.DBAzureBlobAccount != "" {
if err != nil { blobs, err = azureblob.NewBlobStore(cli.DBAzureBlobAccount, cli.DBAzureBlobKey, cli.DBAzureBlobContainer)
log.Fatalf("Failed to create S3 session: %v", err) }
} if err != nil {
log.Fatalf("Failed to create blob store: %v", err)
} }
// Start the database. // Start the database.
db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, s3c) db := newInMemoryStore(cli.DBDir, cli.DBFlushInterval, blobs)
main.Add(db) main.Add(db)
// If we have an AMQP broker for replication, start that // If we have an AMQP broker for replication, start that
@@ -141,7 +150,7 @@ func main() {
} }
// Start the main API server. // Start the main API server.
qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression) qs := newAPISrv(cli.Listen, cert, db, repl, cli.HTTP, cli.Compression, cli.DesiredNotFoundRate)
main.Add(qs) main.Add(qs)
// If we have a metrics port configured, start a metrics handler. // If we have a metrics port configured, start a metrics handler.
+1 -1
View File
@@ -184,7 +184,7 @@ func protocolConnectionHandler(tcpConn net.Conn, config *tls.Config, token strin
continue continue
} }
// requestedPeer is the server, id is the client // requestedPeer is the server, id is the client
ses := newSession(requestedPeer, id, sessionLimiter, globalLimiter) ses := newSession(requestedPeer, id, sessionLimitBps, globalLimiter)
go ses.Serve() go ses.Serve()
-4
View File
@@ -51,7 +51,6 @@ var (
globalLimitBps int globalLimitBps int
overLimit atomic.Bool overLimit atomic.Bool
descriptorLimit int64 descriptorLimit int64
sessionLimiter *rate.Limiter
globalLimiter *rate.Limiter globalLimiter *rate.Limiter
networkBufferSize int networkBufferSize int
@@ -228,9 +227,6 @@ func main() {
} }
} }
if sessionLimitBps > 0 {
sessionLimiter = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
}
if globalLimitBps > 0 { if globalLimitBps > 0 {
globalLimiter = rate.NewLimiter(rate.Limit(globalLimitBps), 2*globalLimitBps) globalLimiter = rate.NewLimiter(rate.Limit(globalLimitBps), 2*globalLimitBps)
} }
+7 -1
View File
@@ -27,7 +27,7 @@ var (
bytesProxied atomic.Int64 bytesProxied atomic.Int64
) )
func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionRateLimit, globalRateLimit *rate.Limiter) *session { func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionLimitBps int, globalRateLimit *rate.Limiter) *session {
serverkey := make([]byte, 32) serverkey := make([]byte, 32)
_, err := rand.Read(serverkey) _, err := rand.Read(serverkey)
if err != nil { if err != nil {
@@ -40,12 +40,17 @@ func newSession(serverid, clientid syncthingprotocol.DeviceID, sessionRateLimit,
return nil return nil
} }
var sessionRateLimit *rate.Limiter
if sessionLimitBps > 0 {
sessionRateLimit = rate.NewLimiter(rate.Limit(sessionLimitBps), 2*sessionLimitBps)
}
ses := &session{ ses := &session{
serverkey: serverkey, serverkey: serverkey,
serverid: serverid, serverid: serverid,
clientkey: clientkey, clientkey: clientkey,
clientid: clientid, clientid: clientid,
rateLimit: makeRateLimitFunc(sessionRateLimit, globalRateLimit), rateLimit: makeRateLimitFunc(sessionRateLimit, globalRateLimit),
limiter: sessionRateLimit,
connsChan: make(chan net.Conn), connsChan: make(chan net.Conn),
conns: make([]net.Conn, 0, 2), conns: make([]net.Conn, 0, 2),
} }
@@ -109,6 +114,7 @@ type session struct {
clientid syncthingprotocol.DeviceID clientid syncthingprotocol.DeviceID
rateLimit func(bytes int) rateLimit func(bytes int)
limiter *rate.Limiter
connsChan chan net.Conn connsChan chan net.Conn
conns []net.Conn conns []net.Conn
+1
View File
@@ -41,4 +41,5 @@ func (p *profileCommand) Run(ctx Context) error {
type debugCommand struct { type debugCommand struct {
File fileCommand `cmd:"" help:"Show information about a file (or directory/symlink)"` File fileCommand `cmd:"" help:"Show information about a file (or directory/symlink)"`
Profile profileCommand `cmd:"" help:"Save a profile to help figuring out what Syncthing does"` Profile profileCommand `cmd:"" help:"Save a profile to help figuring out what Syncthing does"`
Index indexCommand `cmd:"" help:"Show information about the index (database)"`
} }
+32
View File
@@ -0,0 +1,32 @@
// Copyright (C) 2014 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 cli
import (
"github.com/alecthomas/kong"
)
type indexCommand struct {
Dump struct{} `cmd:"" help:"Print the entire db"`
DumpSize struct{} `cmd:"" help:"Print the db size of different categories of information"`
Check struct{} `cmd:"" help:"Check the database for inconsistencies"`
Account struct{} `cmd:"" help:"Print key and value size statistics per key type"`
}
func (*indexCommand) Run(kongCtx *kong.Context) error {
switch kongCtx.Selected().Name {
case "dump":
return indexDump()
case "dump-size":
return indexDumpSize()
case "check":
return indexCheck()
case "account":
return indexAccount()
}
return nil
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright (C) 2020 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 cli
import (
"fmt"
"os"
"text/tabwriter"
)
// indexAccount prints key and data size statistics per class
func indexAccount() error {
ldb, err := getDB()
if err != nil {
return err
}
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
return err
}
var ksizes [256]int
var dsizes [256]int
var counts [256]int
var max [256]int
for it.Next() {
key := it.Key()
t := key[0]
ds := len(it.Value())
ks := len(key)
s := ks + ds
counts[t]++
ksizes[t] += ks
dsizes[t] += ds
if s > max[t] {
max[t] = s
}
}
tw := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', tabwriter.AlignRight)
toti, totds, totks := 0, 0, 0
for t := range ksizes {
if ksizes[t] > 0 {
// yes metric kilobytes 🤘
fmt.Fprintf(tw, "0x%02x:\t%d items,\t%d KB keys +\t%d KB data,\t%d B +\t%d B avg,\t%d B max\t\n", t, counts[t], ksizes[t]/1000, dsizes[t]/1000, ksizes[t]/counts[t], dsizes[t]/counts[t], max[t])
toti += counts[t]
totds += dsizes[t]
totks += ksizes[t]
}
}
fmt.Fprintf(tw, "Total\t%d items,\t%d KB keys +\t%d KB data.\t\n", toti, totks/1000, totds/1000)
tw.Flush()
return nil
}
+162
View File
@@ -0,0 +1,162 @@
// Copyright (C) 2015 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 cli
import (
"encoding/binary"
"fmt"
"time"
"google.golang.org/protobuf/proto"
"github.com/syncthing/syncthing/internal/gen/bep"
"github.com/syncthing/syncthing/internal/gen/dbproto"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/protocol"
)
func indexDump() error {
ldb, err := getDB()
if err != nil {
return err
}
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
return err
}
for it.Next() {
key := it.Key()
switch key[0] {
case db.KeyTypeDevice:
folder := binary.BigEndian.Uint32(key[1:])
device := binary.BigEndian.Uint32(key[1+4:])
name := nulString(key[1+4+4:])
fmt.Printf("[device] F:%d D:%d N:%q", folder, device, name)
var f bep.FileInfo
err := proto.Unmarshal(it.Value(), &f)
if err != nil {
return err
}
fmt.Printf(" V:%v\n", &f)
case db.KeyTypeGlobal:
folder := binary.BigEndian.Uint32(key[1:])
name := nulString(key[1+4:])
var flv dbproto.VersionList
proto.Unmarshal(it.Value(), &flv)
fmt.Printf("[global] F:%d N:%q V:%s\n", folder, name, &flv)
case db.KeyTypeBlock:
folder := binary.BigEndian.Uint32(key[1:])
hash := key[1+4 : 1+4+32]
name := nulString(key[1+4+32:])
fmt.Printf("[block] F:%d H:%x N:%q I:%d\n", folder, hash, name, binary.BigEndian.Uint32(it.Value()))
case db.KeyTypeDeviceStatistic:
fmt.Printf("[dstat] K:%x V:%x\n", key, it.Value())
case db.KeyTypeFolderStatistic:
fmt.Printf("[fstat] K:%x V:%x\n", key, it.Value())
case db.KeyTypeVirtualMtime:
folder := binary.BigEndian.Uint32(key[1:])
name := nulString(key[1+4:])
val := it.Value()
var realTime, virtualTime time.Time
realTime.UnmarshalBinary(val[:len(val)/2])
virtualTime.UnmarshalBinary(val[len(val)/2:])
fmt.Printf("[mtime] F:%d N:%q R:%v V:%v\n", folder, name, realTime, virtualTime)
case db.KeyTypeFolderIdx:
key := binary.BigEndian.Uint32(key[1:])
fmt.Printf("[folderidx] K:%d V:%q\n", key, it.Value())
case db.KeyTypeDeviceIdx:
key := binary.BigEndian.Uint32(key[1:])
val := it.Value()
device := "<nil>"
if len(val) > 0 {
dev, err := protocol.DeviceIDFromBytes(val)
if err != nil {
device = fmt.Sprintf("<invalid %d bytes>", len(val))
} else {
device = dev.String()
}
}
fmt.Printf("[deviceidx] K:%d V:%s\n", key, device)
case db.KeyTypeIndexID:
device := binary.BigEndian.Uint32(key[1:])
folder := binary.BigEndian.Uint32(key[5:])
fmt.Printf("[indexid] D:%d F:%d I:%x\n", device, folder, it.Value())
case db.KeyTypeFolderMeta:
folder := binary.BigEndian.Uint32(key[1:])
fmt.Printf("[foldermeta] F:%d", folder)
var cs dbproto.CountsSet
if err := proto.Unmarshal(it.Value(), &cs); err != nil {
fmt.Printf(" (invalid)\n")
} else {
fmt.Printf(" V:%v\n", &cs)
}
case db.KeyTypeMiscData:
fmt.Printf("[miscdata] K:%q V:%q\n", key[1:], it.Value())
case db.KeyTypeSequence:
folder := binary.BigEndian.Uint32(key[1:])
seq := binary.BigEndian.Uint64(key[5:])
fmt.Printf("[sequence] F:%d S:%d V:%q\n", folder, seq, it.Value())
case db.KeyTypeNeed:
folder := binary.BigEndian.Uint32(key[1:])
file := string(key[5:])
fmt.Printf("[need] F:%d V:%q\n", folder, file)
case db.KeyTypeBlockList:
fmt.Printf("[blocklist] H:%x\n", key[1:])
case db.KeyTypeBlockListMap:
folder := binary.BigEndian.Uint32(key[1:])
hash := key[5:37]
fileName := string(key[37:])
fmt.Printf("[blocklistmap] F:%d H:%x N:%s\n", folder, hash, fileName)
case db.KeyTypeVersion:
fmt.Printf("[version] H:%x", key[1:])
var v bep.Vector
err := proto.Unmarshal(it.Value(), &v)
if err != nil {
fmt.Printf(" (invalid)\n")
} else {
fmt.Printf(" V:%v\n", &v)
}
case db.KeyTypePendingFolder:
device := binary.BigEndian.Uint32(key[1:])
folder := string(key[5:])
var of dbproto.ObservedFolder
proto.Unmarshal(it.Value(), &of)
fmt.Printf("[pendingFolder] D:%d F:%s V:%v\n", device, folder, &of)
case db.KeyTypePendingDevice:
device := "<invalid>"
dev, err := protocol.DeviceIDFromBytes(key[1:])
if err == nil {
device = dev.String()
}
var od dbproto.ObservedDevice
proto.Unmarshal(it.Value(), &od)
fmt.Printf("[pendingDevice] D:%v V:%v\n", device, &od)
default:
fmt.Printf("[??? %d]\n %x\n %x\n", key[0], key, it.Value())
}
}
return nil
}
+89
View File
@@ -0,0 +1,89 @@
// Copyright (C) 2015 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 cli
import (
"cmp"
"encoding/binary"
"fmt"
"slices"
"github.com/syncthing/syncthing/lib/db"
)
func indexDumpSize() error {
type sizedElement struct {
key string
size int
}
ldb, err := getDB()
if err != nil {
return err
}
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
return err
}
var elems []sizedElement
for it.Next() {
var ele sizedElement
key := it.Key()
switch key[0] {
case db.KeyTypeDevice:
folder := binary.BigEndian.Uint32(key[1:])
device := binary.BigEndian.Uint32(key[1+4:])
name := nulString(key[1+4+4:])
ele.key = fmt.Sprintf("DEVICE:%d:%d:%s", folder, device, name)
case db.KeyTypeGlobal:
folder := binary.BigEndian.Uint32(key[1:])
name := nulString(key[1+4:])
ele.key = fmt.Sprintf("GLOBAL:%d:%s", folder, name)
case db.KeyTypeBlock:
folder := binary.BigEndian.Uint32(key[1:])
hash := key[1+4 : 1+4+32]
name := nulString(key[1+4+32:])
ele.key = fmt.Sprintf("BLOCK:%d:%x:%s", folder, hash, name)
case db.KeyTypeDeviceStatistic:
ele.key = fmt.Sprintf("DEVICESTATS:%s", key[1:])
case db.KeyTypeFolderStatistic:
ele.key = fmt.Sprintf("FOLDERSTATS:%s", key[1:])
case db.KeyTypeVirtualMtime:
ele.key = fmt.Sprintf("MTIME:%s", key[1:])
case db.KeyTypeFolderIdx:
id := binary.BigEndian.Uint32(key[1:])
ele.key = fmt.Sprintf("FOLDERIDX:%d", id)
case db.KeyTypeDeviceIdx:
id := binary.BigEndian.Uint32(key[1:])
ele.key = fmt.Sprintf("DEVICEIDX:%d", id)
default:
ele.key = fmt.Sprintf("UNKNOWN:%x", key)
}
ele.size = len(it.Value())
elems = append(elems, ele)
}
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)
}
return nil
}
+435
View File
@@ -0,0 +1,435 @@
// Copyright (C) 2018 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 cli
import (
"bytes"
"cmp"
"encoding/binary"
"errors"
"fmt"
"slices"
"google.golang.org/protobuf/proto"
"github.com/syncthing/syncthing/internal/gen/bep"
"github.com/syncthing/syncthing/internal/gen/dbproto"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/protocol"
)
type fileInfoKey struct {
folder uint32
device uint32
name string
}
type globalKey struct {
folder uint32
name string
}
type sequenceKey struct {
folder uint32
sequence uint64
}
func indexCheck() (err error) {
ldb, err := getDB()
if err != nil {
return err
}
folders := make(map[uint32]string)
devices := make(map[uint32]string)
deviceToIDs := make(map[string]uint32)
fileInfos := make(map[fileInfoKey]*bep.FileInfo)
globals := make(map[globalKey]*dbproto.VersionList)
sequences := make(map[sequenceKey]string)
needs := make(map[globalKey]struct{})
blocklists := make(map[string]struct{})
versions := make(map[string]*bep.Vector)
usedBlocklists := make(map[string]struct{})
usedVersions := make(map[string]struct{})
var localDeviceKey uint32
success := true
defer func() {
if err == nil {
if success {
fmt.Println("Index check completed successfully.")
} else {
err = errors.New("Inconsistencies found in the index")
}
}
}()
it, err := ldb.NewPrefixIterator(nil)
if err != nil {
return err
}
for it.Next() {
key := it.Key()
switch key[0] {
case db.KeyTypeDevice:
folder := binary.BigEndian.Uint32(key[1:])
device := binary.BigEndian.Uint32(key[1+4:])
name := nulString(key[1+4+4:])
var f bep.FileInfo
err := proto.Unmarshal(it.Value(), &f)
if err != nil {
fmt.Println("Unable to unmarshal FileInfo:", err)
success = false
continue
}
fileInfos[fileInfoKey{folder, device, name}] = &f
case db.KeyTypeGlobal:
folder := binary.BigEndian.Uint32(key[1:])
name := nulString(key[1+4:])
var flv dbproto.VersionList
if err := proto.Unmarshal(it.Value(), &flv); err != nil {
fmt.Println("Unable to unmarshal VersionList:", err)
success = false
continue
}
globals[globalKey{folder, name}] = &flv
case db.KeyTypeFolderIdx:
key := binary.BigEndian.Uint32(it.Key()[1:])
folders[key] = string(it.Value())
case db.KeyTypeDeviceIdx:
key := binary.BigEndian.Uint32(it.Key()[1:])
devices[key] = string(it.Value())
deviceToIDs[string(it.Value())] = key
if bytes.Equal(it.Value(), protocol.LocalDeviceID[:]) {
localDeviceKey = key
}
case db.KeyTypeSequence:
folder := binary.BigEndian.Uint32(key[1:])
seq := binary.BigEndian.Uint64(key[5:])
val := it.Value()
sequences[sequenceKey{folder, seq}] = string(val[9:])
case db.KeyTypeNeed:
folder := binary.BigEndian.Uint32(key[1:])
name := nulString(key[1+4:])
needs[globalKey{folder, name}] = struct{}{}
case db.KeyTypeBlockList:
hash := string(key[1:])
blocklists[hash] = struct{}{}
case db.KeyTypeVersion:
hash := string(key[1:])
var v bep.Vector
if err := proto.Unmarshal(it.Value(), &v); err != nil {
fmt.Println("Unable to unmarshal Vector:", err)
success = false
continue
}
versions[hash] = &v
}
}
if localDeviceKey == 0 {
fmt.Println("Missing key for local device in device index (bailing out)")
success = false
return
}
var missingSeq []sequenceKey
for fk, fi := range fileInfos {
if fk.name != fi.Name {
fmt.Printf("Mismatching FileInfo name, %q (key) != %q (actual)\n", fk.name, fi.Name)
success = false
}
folder := folders[fk.folder]
if folder == "" {
fmt.Printf("Unknown folder ID %d for FileInfo %q\n", fk.folder, fk.name)
success = false
continue
}
if devices[fk.device] == "" {
fmt.Printf("Unknown device ID %d for FileInfo %q, folder %q\n", fk.folder, fk.name, folder)
success = false
}
if fk.device == localDeviceKey {
sk := sequenceKey{fk.folder, uint64(fi.Sequence)}
name, ok := sequences[sk]
if !ok {
fmt.Printf("Sequence entry missing for FileInfo %q, folder %q, seq %d\n", fi.Name, folder, fi.Sequence)
missingSeq = append(missingSeq, sk)
success = false
continue
}
if name != fi.Name {
fmt.Printf("Sequence entry refers to wrong name, %q (seq) != %q (FileInfo), folder %q, seq %d\n", name, fi.Name, folder, fi.Sequence)
success = false
}
}
if len(fi.Blocks) == 0 && len(fi.BlocksHash) != 0 {
key := string(fi.BlocksHash)
if _, ok := blocklists[key]; !ok {
fmt.Printf("Missing block list for file %q, block list hash %x\n", fi.Name, fi.BlocksHash)
success = false
} else {
usedBlocklists[key] = struct{}{}
}
}
if fi.VersionHash != nil {
key := string(fi.VersionHash)
if _, ok := versions[key]; !ok {
fmt.Printf("Missing version vector for file %q, version hash %x\n", fi.Name, fi.VersionHash)
success = false
} else {
usedVersions[key] = struct{}{}
}
}
_, ok := globals[globalKey{fk.folder, fk.name}]
if !ok {
fmt.Printf("Missing global for file %q\n", fi.Name)
success = false
continue
}
}
// Aggregate the ranges of missing sequence entries, print them
slices.SortFunc(missingSeq, func(a, b sequenceKey) int {
if a.folder != b.folder {
return cmp.Compare(a.folder, b.folder)
}
return cmp.Compare(a.sequence, b.sequence)
})
var folder uint32
var startSeq, prevSeq uint64
for _, sk := range missingSeq {
if folder != sk.folder || sk.sequence != prevSeq+1 {
if folder != 0 {
fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
}
startSeq = sk.sequence
folder = sk.folder
}
prevSeq = sk.sequence
}
if folder != 0 {
fmt.Printf("Folder %d missing %d sequence entries: #%d - #%d\n", folder, prevSeq-startSeq+1, startSeq, prevSeq)
}
for gk, vl := range globals {
folder := folders[gk.folder]
if folder == "" {
fmt.Printf("Unknown folder ID %d for VersionList %q\n", gk.folder, gk.name)
success = false
}
checkGlobal := func(i int, device []byte, version protocol.Vector, invalid, deleted bool) {
dev, ok := deviceToIDs[string(device)]
if !ok {
fmt.Printf("VersionList %q, folder %q refers to unknown device %q\n", gk.name, folder, device)
success = false
}
fi, ok := fileInfos[fileInfoKey{gk.folder, dev, gk.name}]
if !ok {
fmt.Printf("VersionList %q, folder %q, entry %d refers to unknown FileInfo\n", gk.name, folder, i)
success = false
}
fiv := fi.Version
if fi.VersionHash != nil {
fiv = versions[string(fi.VersionHash)]
}
if !protocol.VectorFromWire(fiv).Equal(version) {
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo version mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, version, fi.Version)
success = false
}
ffi := protocol.FileInfoFromDB(fi)
if ffi.IsInvalid() != invalid {
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo invalid mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, invalid, ffi.IsInvalid())
success = false
}
if ffi.IsDeleted() != deleted {
fmt.Printf("VersionList %q, folder %q, entry %d, FileInfo deleted mismatch, %v (VersionList) != %v (FileInfo)\n", gk.name, folder, i, deleted, ffi.IsDeleted())
success = false
}
}
for i, fv := range vl.Versions {
ver := protocol.VectorFromWire(fv.Version)
for _, device := range fv.Devices {
checkGlobal(i, device, ver, false, fv.Deleted)
}
for _, device := range fv.InvalidDevices {
checkGlobal(i, device, ver, true, fv.Deleted)
}
}
// If we need this file we should have a need entry for it. False
// positives from needsLocally for deleted files, where we might
// legitimately lack an entry if we never had it, and ignored files.
if needsLocally(vl) {
_, ok := needs[gk]
if !ok {
fv, _ := vlGetGlobal(vl)
devB, _ := fvFirstDevice(fv)
dev := deviceToIDs[string(devB)]
fi := protocol.FileInfoFromDB(fileInfos[fileInfoKey{gk.folder, dev, gk.name}])
if !fi.IsDeleted() && !fi.IsIgnored() {
fmt.Printf("Missing need entry for needed file %q, folder %q\n", gk.name, folder)
}
}
}
}
seenSeq := make(map[fileInfoKey]uint64)
for sk, name := range sequences {
folder := folders[sk.folder]
if folder == "" {
fmt.Printf("Unknown folder ID %d for sequence entry %d, %q\n", sk.folder, sk.sequence, name)
success = false
continue
}
if prev, ok := seenSeq[fileInfoKey{folder: sk.folder, name: name}]; ok {
fmt.Printf("Duplicate sequence entry for %q, folder %q, seq %d (prev %d)\n", name, folder, sk.sequence, prev)
success = false
}
seenSeq[fileInfoKey{folder: sk.folder, name: name}] = sk.sequence
fi, ok := fileInfos[fileInfoKey{sk.folder, localDeviceKey, name}]
if !ok {
fmt.Printf("Missing FileInfo for sequence entry %d, folder %q, %q\n", sk.sequence, folder, name)
success = false
continue
}
if fi.Sequence != int64(sk.sequence) {
fmt.Printf("Sequence mismatch for %q, folder %q, %d (key) != %d (FileInfo)\n", name, folder, sk.sequence, fi.Sequence)
success = false
}
}
for nk := range needs {
folder := folders[nk.folder]
if folder == "" {
fmt.Printf("Unknown folder ID %d for need entry %q\n", nk.folder, nk.name)
success = false
continue
}
vl, ok := globals[nk]
if !ok {
fmt.Printf("Missing global for need entry %q, folder %q\n", nk.name, folder)
success = false
continue
}
if !needsLocally(vl) {
fmt.Printf("Need entry for file we don't need, %q, folder %q\n", nk.name, folder)
success = false
}
}
if d := len(blocklists) - len(usedBlocklists); d > 0 {
fmt.Printf("%d block list entries out of %d needs GC\n", d, len(blocklists))
}
if d := len(versions) - len(usedVersions); d > 0 {
fmt.Printf("%d version entries out of %d needs GC\n", d, len(versions))
}
return nil
}
func needsLocally(vl *dbproto.VersionList) bool {
gfv, gok := vlGetGlobal(vl)
if !gok { // That's weird, but we hardly need something non-existent
return false
}
fv, ok := vlGet(vl, protocol.LocalDeviceID[:])
return db.Need(gfv, ok, protocol.VectorFromWire(fv.Version))
}
// Get returns a FileVersion that contains the given device and whether it has
// been found at all.
func vlGet(vl *dbproto.VersionList, device []byte) (*dbproto.FileVersion, bool) {
_, i, _, ok := vlFindDevice(vl, device)
if !ok {
return &dbproto.FileVersion{}, false
}
return vl.Versions[i], true
}
// GetGlobal returns the current global FileVersion. The returned FileVersion
// may be invalid, if all FileVersions are invalid. Returns false only if
// VersionList is empty.
func vlGetGlobal(vl *dbproto.VersionList) (*dbproto.FileVersion, bool) {
i := vlFindGlobal(vl)
if i == -1 {
return nil, false
}
return vl.Versions[i], true
}
// findGlobal returns the first version that isn't invalid, or if all versions are
// invalid just the first version (i.e. 0) or -1, if there's no versions at all.
func vlFindGlobal(vl *dbproto.VersionList) int {
for i := range vl.Versions {
if !fvIsInvalid(vl.Versions[i]) {
return i
}
}
if len(vl.Versions) == 0 {
return -1
}
return 0
}
// findDevice returns whether the device is in InvalidVersions or Versions and
// in InvalidDevices or Devices (true for invalid), the positions in the version
// and device slices and whether it has been found at all.
func vlFindDevice(vl *dbproto.VersionList, device []byte) (bool, int, int, bool) {
for i, v := range vl.Versions {
if j := deviceIndex(v.Devices, device); j != -1 {
return false, i, j, true
}
if j := deviceIndex(v.InvalidDevices, device); j != -1 {
return true, i, j, true
}
}
return false, -1, -1, false
}
func deviceIndex(devices [][]byte, device []byte) int {
for i, dev := range devices {
if bytes.Equal(device, dev) {
return i
}
}
return -1
}
func fvFirstDevice(fv *dbproto.FileVersion) ([]byte, bool) {
if len(fv.Devices) != 0 {
return fv.Devices[0], true
}
if len(fv.InvalidDevices) != 0 {
return fv.InvalidDevices[0], true
}
return nil, false
}
func fvIsInvalid(fv *dbproto.FileVersion) bool {
return fv == nil || len(fv.Devices) == 0
}
+6
View File
@@ -17,6 +17,8 @@ import (
"path/filepath" "path/filepath"
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db/backend"
"github.com/syncthing/syncthing/lib/locations"
) )
func responseToBArray(response *http.Response) ([]byte, error) { func responseToBArray(response *http.Response) ([]byte, error) {
@@ -131,6 +133,10 @@ func prettyPrintResponse(response *http.Response) error {
return prettyPrintJSON(data) return prettyPrintJSON(data)
} }
func getDB() (backend.Backend, error) {
return backend.OpenLevelDBRO(locations.Get(locations.Database))
}
func nulString(bs []byte) string { func nulString(bs []byte) string {
for i := range bs { for i := range bs {
if bs[i] == 0 { if bs[i] == 0 {
+4 -2
View File
@@ -14,7 +14,7 @@ import (
"net/http" "net/http"
"os" "os"
"path/filepath" "path/filepath"
"sort" "slices"
"strings" "strings"
"time" "time"
) )
@@ -37,7 +37,9 @@ func uploadPanicLogs(ctx context.Context, urlBase, dir string) {
return 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 { for _, file := range files {
if strings.Contains(file, ".reported.") { if strings.Contains(file, ".reported.") {
// We've already sent this file. It'll be cleaned out at some // We've already sent this file. It'll be cleaned out at some
+1 -1
View File
@@ -238,7 +238,7 @@ func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo,
} }
// Verify the hash against the plaintext block info // Verify the hash against the plaintext block info
if !scanner.Validate(dec, plainBlock.Hash) { if !scanner.Validate(dec, plainBlock.Hash, 0) {
// The block decrypted correctly but fails the hash check. This // The block decrypted correctly but fails the hash check. This
// is odd and unexpected, but it it's still a valid block from // is odd and unexpected, but it it's still a valid block from
// the source. The file might have changed while we pulled it? // the source. The file might have changed while we pulled it?
+86 -63
View File
@@ -22,9 +22,8 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"runtime/pprof" "runtime/pprof"
"sort" "slices"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@@ -39,10 +38,10 @@ import (
"github.com/syncthing/syncthing/cmd/syncthing/cmdutil" "github.com/syncthing/syncthing/cmd/syncthing/cmdutil"
"github.com/syncthing/syncthing/cmd/syncthing/decrypt" "github.com/syncthing/syncthing/cmd/syncthing/decrypt"
"github.com/syncthing/syncthing/cmd/syncthing/generate" "github.com/syncthing/syncthing/cmd/syncthing/generate"
"github.com/syncthing/syncthing/internal/db"
_ "github.com/syncthing/syncthing/lib/automaxprocs" _ "github.com/syncthing/syncthing/lib/automaxprocs"
"github.com/syncthing/syncthing/lib/build" "github.com/syncthing/syncthing/lib/build"
"github.com/syncthing/syncthing/lib/config" "github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/db"
"github.com/syncthing/syncthing/lib/dialer" "github.com/syncthing/syncthing/lib/dialer"
"github.com/syncthing/syncthing/lib/events" "github.com/syncthing/syncthing/lib/events"
"github.com/syncthing/syncthing/lib/fs" "github.com/syncthing/syncthing/lib/fs"
@@ -140,41 +139,42 @@ var entrypoint struct {
// serveOptions are the options for the `syncthing serve` command. // serveOptions are the options for the `syncthing serve` command.
type serveOptions struct { type serveOptions struct {
cmdutil.CommonOptions cmdutil.CommonOptions
AllowNewerConfig bool `help:"Allow loading newer than current config version"` AllowNewerConfig bool `help:"Allow loading newer than current config version"`
Audit bool `help:"Write events to audit file"` Audit bool `help:"Write events to audit file"`
AuditFile string `name:"auditfile" placeholder:"PATH" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)"` AuditFile string `name:"auditfile" placeholder:"PATH" help:"Specify audit file (use \"-\" for stdout, \"--\" for stderr)"`
BrowserOnly bool `help:"Open GUI in browser"` BrowserOnly bool `help:"Open GUI in browser"`
DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"` DataDir string `name:"data" placeholder:"PATH" env:"STDATADIR" help:"Set data directory (database and logs)"`
DeviceID bool `help:"Show the device ID"` DeviceID bool `help:"Show the device ID"`
GenerateDir string `name:"generate" placeholder:"PATH" help:"Generate key and config in specified dir, then exit"` // DEPRECATED: replaced by subcommand! GenerateDir string `name:"generate" placeholder:"PATH" help:"Generate key and config in specified dir, then exit"` // DEPRECATED: replaced by subcommand!
GUIAddress string `name:"gui-address" placeholder:"URL" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")"` GUIAddress string `name:"gui-address" placeholder:"URL" help:"Override GUI address (e.g. \"http://192.0.2.42:8443\")"`
GUIAPIKey string `name:"gui-apikey" placeholder:"API-KEY" help:"Override GUI API key"` GUIAPIKey string `name:"gui-apikey" placeholder:"API-KEY" help:"Override GUI API key"`
LogFile string `name:"logfile" default:"${logFile}" placeholder:"PATH" help:"Log file name (see below)"` LogFile string `name:"logfile" default:"${logFile}" placeholder:"PATH" help:"Log file name (see below)"`
LogFlags int `name:"logflags" default:"${logFlags}" placeholder:"BITS" help:"Select information in log line prefix (see below)"` LogFlags int `name:"logflags" default:"${logFlags}" placeholder:"BITS" help:"Select information in log line prefix (see below)"`
LogMaxFiles int `placeholder:"N" default:"${logMaxFiles}" name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)"` LogMaxFiles int `placeholder:"N" default:"${logMaxFiles}" name:"log-max-old-files" help:"Number of old files to keep (zero to keep only current)"`
LogMaxSize int `placeholder:"BYTES" default:"${logMaxSize}" help:"Maximum size of any file (zero to disable log rotation)"` LogMaxSize int `placeholder:"BYTES" default:"${logMaxSize}" help:"Maximum size of any file (zero to disable log rotation)"`
NoBrowser bool `help:"Do not start browser"` NoBrowser bool `help:"Do not start browser"`
NoRestart bool `env:"STNORESTART" help:"Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash"` NoRestart bool `env:"STNORESTART" help:"Do not restart Syncthing when exiting due to API/GUI command, upgrade, or crash"`
NoUpgrade bool `env:"STNOUPGRADE" help:"Disable automatic upgrades"` NoUpgrade bool `env:"STNOUPGRADE" help:"Disable automatic upgrades"`
Paths bool `help:"Show configuration paths"` Paths bool `help:"Show configuration paths"`
Paused bool `help:"Start with all devices and folders paused"` Paused bool `help:"Start with all devices and folders paused"`
Unpaused bool `help:"Start with all devices and folders unpaused"` Unpaused bool `help:"Start with all devices and folders unpaused"`
Upgrade bool `help:"Perform upgrade"` Upgrade bool `help:"Perform upgrade"`
UpgradeCheck bool `help:"Check for available upgrade"` UpgradeCheck bool `help:"Check for available upgrade"`
UpgradeTo string `placeholder:"URL" help:"Force upgrade directly from specified URL"` UpgradeTo string `placeholder:"URL" help:"Force upgrade directly from specified URL"`
Verbose bool `help:"Print verbose log output"` Verbose bool `help:"Print verbose log output"`
Version bool `help:"Show version"` Version bool `help:"Show version"`
DBMaintenanceInterval time.Duration `env:"STDBMAINTINTERVAL" help:"Database maintenance interval" default:"8h"`
// Debug options below // Debug options below
DebugGUIAssetsDir string `placeholder:"PATH" help:"Directory to load GUI assets from" env:"STGUIASSETS"` DebugDBIndirectGCInterval time.Duration `env:"STGCINDIRECTEVERY" help:"Database indirection GC interval"`
DebugPerfStats bool `env:"STPERFSTATS" help:"Write running performance statistics to perf-$pid.csv (Unix only)"` DebugDBRecheckInterval time.Duration `env:"STRECHECKDBEVERY" help:"Database metadata recalculation interval"`
DebugProfileBlock bool `env:"STBLOCKPROFILE" help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds"` DebugGUIAssetsDir string `placeholder:"PATH" help:"Directory to load GUI assets from" env:"STGUIASSETS"`
DebugProfileCPU bool `help:"Write a CPU profile to cpu-$pid.pprof on exit" env:"STCPUPROFILE"` DebugPerfStats bool `env:"STPERFSTATS" help:"Write running performance statistics to perf-$pid.csv (Unix only)"`
DebugProfileHeap bool `env:"STHEAPPROFILE" help:"Write heap profiles to heap-$pid-$timestamp.pprof each time heap usage increases"` DebugProfileBlock bool `env:"STBLOCKPROFILE" help:"Write block profiles to block-$pid-$timestamp.pprof every 20 seconds"`
DebugProfilerListen string `placeholder:"ADDR" env:"STPROFILER" help:"Network profiler listen address"` DebugProfileCPU bool `help:"Write a CPU profile to cpu-$pid.pprof on exit" env:"STCPUPROFILE"`
DebugResetDatabase bool `name:"reset-database" help:"Reset the database, forcing a full rescan and resync"` DebugProfileHeap bool `env:"STHEAPPROFILE" help:"Write heap profiles to heap-$pid-$timestamp.pprof each time heap usage increases"`
DebugResetDeltaIdxs bool `name:"reset-deltas" help:"Reset delta index IDs, forcing a full index exchange"` DebugProfilerListen string `placeholder:"ADDR" env:"STPROFILER" help:"Network profiler listen address"`
DebugResetDatabase bool `name:"reset-database" help:"Reset the database, forcing a full rescan and resync"`
DebugResetDeltaIdxs bool `name:"reset-deltas" help:"Reset delta index IDs, forcing a full index exchange"`
// Internal options, not shown to users // Internal options, not shown to users
InternalRestarting bool `env:"STRESTART" hidden:"1"` InternalRestarting bool `env:"STRESTART" hidden:"1"`
@@ -349,10 +349,12 @@ func (options serveOptions) Run() error {
return nil return nil
} }
// Ensure that our home directory exists. // Ensure that our config and data directories exist.
if err := syncthing.EnsureDir(locations.GetBaseDir(locations.ConfigBaseDir), 0o700); err != nil { for _, loc := range []locations.BaseDirEnum{locations.ConfigBaseDir, locations.DataBaseDir} {
l.Warnln("Failure on home directory:", err) if err := syncthing.EnsureDir(locations.GetBaseDir(loc), 0o700); err != nil {
os.Exit(svcutil.ExitError.AsInt()) l.Warnln("Failed to ensure directory exists:", err)
os.Exit(svcutil.ExitError.AsInt())
}
} }
if options.UpgradeTo != "" { if options.UpgradeTo != "" {
@@ -376,7 +378,7 @@ func (options serveOptions) Run() error {
if options.Upgrade { if options.Upgrade {
release, err := checkUpgrade() release, err := checkUpgrade()
if err == nil { if err == nil {
lf := flock.New(locations.Get(locations.CertFile)) lf := flock.New(locations.Get(locations.LockFile))
locked, err := lf.TryLock() locked, err := lf.TryLock()
if err != nil { if err != nil {
l.Warnln("Upgrade:", err) l.Warnln("Upgrade:", err)
@@ -386,6 +388,8 @@ func (options serveOptions) Run() error {
} else { } else {
err = upgrade.To(release) err = upgrade.To(release)
} }
_ = lf.Unlock()
_ = os.Remove(locations.Get(locations.LockFile))
} }
if err != nil { if err != nil {
l.Warnln("Upgrade:", err) l.Warnln("Upgrade:", err)
@@ -439,7 +443,7 @@ func debugFacilities() string {
maxLen = len(name) maxLen = len(name)
} }
} }
sort.Strings(names) slices.Sort(names)
// Format the choices // Format the choices
b := new(bytes.Buffer) b := new(bytes.Buffer)
@@ -546,7 +550,7 @@ func syncthingMain(options serveOptions) {
} }
// Ensure we are the only running instance // Ensure we are the only running instance
lf := flock.New(locations.Get(locations.CertFile)) lf := flock.New(locations.Get(locations.LockFile))
locked, err := lf.TryLock() locked, err := lf.TryLock()
if err != nil { if err != nil {
l.Warnln("Failed to acquire lock:", err) l.Warnln("Failed to acquire lock:", err)
@@ -574,6 +578,7 @@ func syncthingMain(options serveOptions) {
os.Exit(svcutil.ExitError.AsInt()) os.Exit(svcutil.ExitError.AsInt())
} }
earlyService.Add(cfgWrapper) earlyService.Add(cfgWrapper)
config.RegisterInfoMetrics(cfgWrapper)
// Candidate builds should auto upgrade. Make sure the option is set, // Candidate builds should auto upgrade. Make sure the option is set,
// unless we are in a build where it's disabled or the STNOUPGRADE // unless we are in a build where it's disabled or the STNOUPGRADE
@@ -592,12 +597,8 @@ func syncthingMain(options serveOptions) {
}) })
} }
if err := syncthing.TryMigrateDatabase(); err != nil { dbFile := locations.Get(locations.Database)
l.Warnln("Failed to migrate old-style database:", err) ldb, err := syncthing.OpenDBBackend(dbFile, cfgWrapper.Options().DatabaseTuning)
os.Exit(1)
}
sdb, err := syncthing.OpenDatabase(locations.Get(locations.Database))
if err != nil { if err != nil {
l.Warnln("Error opening database:", err) l.Warnln("Error opening database:", err)
os.Exit(1) os.Exit(1)
@@ -606,11 +607,11 @@ func syncthingMain(options serveOptions) {
// Check if auto-upgrades is possible, and if yes, and it's enabled do an initial // Check if auto-upgrades is possible, and if yes, and it's enabled do an initial
// upgrade immediately. The auto-upgrade routine can only be started // upgrade immediately. The auto-upgrade routine can only be started
// later after App is initialised. // later after App is initialised.
autoUpgradePossible := autoUpgradePossible(options) autoUpgradePossible := autoUpgradePossible(options)
if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() { if autoUpgradePossible && cfgWrapper.Options().AutoUpgradeEnabled() {
// try to do upgrade directly and log the error if relevant. // try to do upgrade directly and log the error if relevant.
miscDB := db.NewMiscDB(sdb) release, err := initialAutoUpgradeCheck(db.NewMiscDataNamespace(ldb))
release, err := initialAutoUpgradeCheck(miscDB)
if err == nil { if err == nil {
err = upgrade.To(release) err = upgrade.To(release)
} }
@@ -621,7 +622,7 @@ func syncthingMain(options serveOptions) {
l.Infoln("Initial automatic upgrade:", err) l.Infoln("Initial automatic upgrade:", err)
} }
} else { } else {
l.Infof("Upgraded to %q, should exit now.", release.Tag) l.Infof("Upgraded to %q, exiting now.", release.Tag)
os.Exit(svcutil.ExitUpgrade.AsInt()) os.Exit(svcutil.ExitUpgrade.AsInt())
} }
} }
@@ -633,17 +634,36 @@ func syncthingMain(options serveOptions) {
} }
appOpts := syncthing.Options{ appOpts := syncthing.Options{
NoUpgrade: options.NoUpgrade, NoUpgrade: options.NoUpgrade,
ProfilerAddr: options.DebugProfilerListen, ProfilerAddr: options.DebugProfilerListen,
ResetDeltaIdxs: options.DebugResetDeltaIdxs, ResetDeltaIdxs: options.DebugResetDeltaIdxs,
Verbose: options.Verbose, Verbose: options.Verbose,
DBMaintenanceInterval: options.DBMaintenanceInterval, DBRecheckInterval: options.DebugDBRecheckInterval,
} DBIndirectGCInterval: options.DebugDBIndirectGCInterval,
if options.Audit {
appOpts.AuditWriter = auditWriter(options.AuditFile)
} }
app, err := syncthing.New(cfgWrapper, sdb, evLogger, cert, appOpts) if options.Audit || cfgWrapper.Options().AuditEnabled {
l.Infoln("Auditing is enabled.")
auditFile := cfgWrapper.Options().AuditFile
// Ignore config option if command-line option is set
if options.AuditFile != "" {
l.Debugln("Using the audit file from the command-line parameter.")
auditFile = options.AuditFile
}
appOpts.AuditWriter = auditWriter(auditFile)
}
if dur, err := time.ParseDuration(os.Getenv("STRECHECKDBEVERY")); err == nil {
appOpts.DBRecheckInterval = dur
}
if dur, err := time.ParseDuration(os.Getenv("STGCINDIRECTEVERY")); err == nil {
appOpts.DBIndirectGCInterval = dur
}
app, err := syncthing.New(cfgWrapper, ldb, evLogger, cert, appOpts)
if err != nil { if err != nil {
l.Warnln("Failed to start Syncthing:", err) l.Warnln("Failed to start Syncthing:", err)
os.Exit(svcutil.ExitError.AsInt()) os.Exit(svcutil.ExitError.AsInt())
@@ -689,7 +709,10 @@ func syncthingMain(options serveOptions) {
pprof.StopCPUProfile() pprof.StopCPUProfile()
} }
runtime.KeepAlive(lf) // ensure lock is still held to this point // Best effort remove lockfile, doesn't matter if it succeeds
_ = lf.Unlock()
_ = os.Remove(locations.Get(locations.LockFile))
os.Exit(int(status)) os.Exit(int(status))
} }
@@ -831,7 +854,7 @@ func autoUpgrade(cfg config.Wrapper, app *syncthing.App, evLogger events.Logger)
} }
} }
func initialAutoUpgradeCheck(misc *db.Typed) (upgrade.Release, error) { func initialAutoUpgradeCheck(misc *db.NamespacedKV) (upgrade.Release, error) {
if last, ok, err := misc.Time(upgradeCheckKey); err == nil && ok && time.Since(last) < upgradeCheckInterval { if last, ok, err := misc.Time(upgradeCheckKey); err == nil && ok && time.Since(last) < upgradeCheckInterval {
return upgrade.Release{}, errTooEarlyUpgradeCheck return upgrade.Release{}, errTooEarlyUpgradeCheck
} }
+24 -26
View File
@@ -64,7 +64,7 @@ func monitorMain(options serveOptions) {
fileDst, err = open(logFile) fileDst, err = open(logFile)
} }
if err != nil { if err != nil {
l.Warnln("Failed to setup logging to file, proceeding with logging to stdout only:", err) l.Warnln("Failed to set up logging to file, proceeding with logging to stdout only:", err)
} else { } else {
if build.IsWindows { if build.IsWindows {
// Translate line breaks to Windows standard // Translate line breaks to Windows standard
@@ -238,19 +238,18 @@ func copyStderr(stderr io.Reader, dst io.Writer) {
return return
} }
if panicFd == nil { dst.Write([]byte(line))
dst.Write([]byte(line))
if strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:") { if panicFd == nil && (strings.HasPrefix(line, "panic:") || strings.HasPrefix(line, "fatal error:")) {
panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog)) panicFd, err = os.Create(locations.GetTimestamped(locations.PanicLog))
if err != nil { if err != nil {
l.Warnln("Create panic log:", err) l.Warnln("Create panic log:", err)
continue continue
} }
l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name()) l.Warnf("Panic detected, writing to \"%s\"", panicFd.Name())
if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") { if strings.Contains(line, "leveldb") && strings.Contains(line, "corrupt") {
l.Warnln(` l.Warnln(`
********************************************************************************* *********************************************************************************
* Crash due to corrupt database. * * 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 * * https://docs.syncthing.net/users/faq.html#my-syncthing-database-is-corrupt *
********************************************************************************* *********************************************************************************
`) `)
} else { } else {
l.Warnln("Please check for existing issues with similar panic message at https://github.com/syncthing/syncthing/issues/") 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") 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()
} }
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") panicFd.WriteString("Panic at " + time.Now().Format(time.RFC3339) + "\n")
} }
+27 -33
View File
@@ -4,80 +4,79 @@ go 1.23.0
require ( require (
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f
github.com/alecthomas/kong v1.9.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
github.com/aws/aws-sdk-go v1.55.6 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/incontainer v1.0.0
github.com/calmh/xdr v1.2.0 github.com/calmh/xdr v1.2.0
github.com/ccding/go-stun v0.1.5 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/d4l3k/messagediff v1.2.1
github.com/getsentry/raven-go v0.2.0 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/gobwas/glob v0.2.3
github.com/gofrs/flock v0.12.1 github.com/gofrs/flock v0.12.1
github.com/greatroar/blobloom v0.8.0
github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jackpal/gateway v1.0.16 github.com/jackpal/gateway v1.0.16
github.com/jackpal/go-nat-pmp v1.0.2 github.com/jackpal/go-nat-pmp v1.0.2
github.com/jmoiron/sqlx v1.4.0
github.com/julienschmidt/httprouter v1.3.0 github.com/julienschmidt/httprouter v1.3.0
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/maruel/panicparse/v2 v2.4.0 github.com/maruel/panicparse/v2 v2.5.0
github.com/mattn/go-sqlite3 v1.14.24
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2
github.com/maxmind/geoipupdate/v6 v6.1.0 github.com/maxmind/geoipupdate/v6 v6.1.0
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75
github.com/oschwald/geoip2-golang v1.11.0 github.com/oschwald/geoip2-golang v1.11.0
github.com/pierrec/lz4/v4 v4.1.22 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/puzpuzpuz/xsync/v3 v3.5.1
github.com/quic-go/quic-go v0.50.0 github.com/quic-go/quic-go v0.52.0
github.com/rabbitmq/amqp091-go v1.10.0 github.com/rabbitmq/amqp091-go v1.10.0
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9
github.com/shirou/gopsutil/v4 v4.25.2 github.com/shirou/gopsutil/v4 v4.25.4
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc github.com/syncthing/notify v0.0.0-20250528144937-c7027d4f7465
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d
github.com/thejerf/suture/v4 v4.0.6 github.com/thejerf/suture/v4 v4.0.6
github.com/urfave/cli v1.22.16 github.com/urfave/cli v1.22.16
github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0 github.com/vitrun/qart v0.0.0-20160531060029-bf64b92db6b0
github.com/willabides/kongplete v0.4.0 github.com/willabides/kongplete v0.4.0
go.uber.org/automaxprocs v1.6.0 go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.36.0 golang.org/x/crypto v0.38.0
golang.org/x/net v0.37.0 golang.org/x/net v0.40.0
golang.org/x/sys v0.31.0 golang.org/x/sys v0.33.0
golang.org/x/text v0.23.0 golang.org/x/text v0.25.0
golang.org/x/time v0.11.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.5 google.golang.org/protobuf v1.36.6
modernc.org/sqlite v1.36.0
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.4.0
) )
require ( require (
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/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/cespare/xxhash/v2 v2.3.0 // 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/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.8.3 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/fsnotify/fsnotify v1.7.0 // 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-ole/go-ole v1.3.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/golang/snappy v0.0.4 // 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/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // 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/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nxadm/tail v1.4.11 // 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/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
@@ -86,7 +85,6 @@ require (
github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/objx v0.5.2 // indirect
@@ -94,14 +92,10 @@ require (
github.com/tklauser/go-sysconf v0.3.14 // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect
github.com/tklauser/numcpus v0.9.0 // indirect github.com/tklauser/numcpus v0.9.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.uber.org/mock v0.5.0 // indirect go.uber.org/mock v0.5.2 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.24.0 // 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 gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
) )
// https://github.com/gobwas/glob/pull/55 // https://github.com/gobwas/glob/pull/55
+70 -150
View File
@@ -1,20 +1,30 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
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 h1:GmH5lT+moM7PbAJFBq57nH9WJ+wRnBXr/tyaYWbSAx8=
github.com/AudriusButkevicius/recli v0.0.7-0.20220911121932-d000ce8fbf0f/go.mod h1:Nhfib1j/VFnLrXL9cHgA+/n2O6P5THuWelOnbfPNd78= 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.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 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
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/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 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/kong v1.9.0 h1:Wgg0ll5Ys7xDnpgYBuBn/wPeLGAuK0NvYmEcisJgrIs= github.com/alecthomas/kong v1.11.0 h1:y++1gI7jf8O7G7l4LZo5ASFhrhJvzc+WgF/arranEmM=
github.com/alecthomas/kong v1.9.0/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= 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 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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 h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= 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.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 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= github.com/calmh/glob v0.0.0-20220615080505-1d823af5017b h1:Fjm4GuJ+TGMgqfGHN42IQArJb77CfD/mAwLbDUoJe6g=
@@ -31,9 +41,13 @@ github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chmduquesne/rollinghash v4.0.0+incompatible h1:hnREQO+DXjqIw3rUTzWN7/+Dpw+N5Um8zpKV0JOEgbo=
github.com/chmduquesne/rollinghash v4.0.0+incompatible/go.mod h1:Uc2I36RRfTAf7Dge82bi3RU0OQUmXT9iweIcPqvr8A0=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 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/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/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 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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= github.com/d4l3k/messagediff v1.2.1 h1:ZcAIMYsUg0EAp9X+tt8/enBE/Q8Yd5kzPynLyKptt9U=
@@ -41,10 +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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
github.com/ebitengine/purego v0.8.2/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.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.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
@@ -53,22 +65,22 @@ 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/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 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= 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.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= 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.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU= github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY= 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 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 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 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
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.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/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -89,19 +101,18 @@ 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 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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-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-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/greatroar/blobloom v0.8.0 h1:I9RlEkfqK9/6f1v9mFmDYegDQ/x0mISCpiNpAm23Pt4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 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= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 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 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 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 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 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= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@@ -130,31 +141,22 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= 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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= 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 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/maruel/panicparse/v2 v2.4.0 h1:yQKMIbQ0DKfinzVkTkcUzQyQ60UCiNnYfR7PWwTs2VI= github.com/maruel/panicparse/v2 v2.5.0 h1:yCtuS0FWjfd0RTYMXGpDvWcb0kINm8xJGu18/xMUh00=
github.com/maruel/panicparse/v2 v2.4.0/go.mod h1:nOY2OKe8csO3F3SA5+hsxot05JLgukrF54B9x88fVp4= github.com/maruel/panicparse/v2 v2.5.0/go.mod h1:DA2fDiBk63bKfBf4CVZP9gb4fuvzdPbLDsSI873hweQ=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU=
github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ=
github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA= github.com/maxmind/geoipupdate/v6 v6.1.0 h1:sdtTHzzQNJlXF5+fd/EoPTucRHyMonYt/Cok8xzzfqA=
@@ -163,8 +165,6 @@ github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+U
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w= github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
@@ -175,20 +175,22 @@ 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 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 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.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.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= 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.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.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.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= 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.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 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 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo= 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= github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -200,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/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 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= 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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= 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 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
@@ -210,24 +212,22 @@ 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/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 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/quic-go/quic-go v0.50.0 h1:3H/ld1pa3CYhkcc20TPIyG1bNsdhn9qZBGN3b9/UyUo= github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.50.0/go.mod h1:Vim6OmUvlYdwBhXP9ZVrtGmCMWa3wEqhq3NgYrI8b4E= 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 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY= github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab h1:ZjX6I48eZSFetPb41dHudEyVr5v953N15TsNZXlkcWY=
github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E= github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0EntGc3QAAyUaviy4S9tzy4Zp0e2ilq4voC6E=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shirou/gopsutil/v4 v4.25.2 h1:NMscG3l2CqtWFS86kj3vP7soOczqrQYIEhO/pMvvQkk= github.com/shirou/gopsutil/v4 v4.25.4 h1:cdtFO363VEOOFrUCjZRh4XVJkb548lyF0q0uTeMqYPw=
github.com/shirou/gopsutil/v4 v4.25.2/go.mod h1:34gBYJzyqCDT11b6bMHP0XCvWeU3J61XRT7a2EmCRTA= 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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -238,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.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.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.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.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.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 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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-20250528144937-c7027d4f7465 h1:yhxdTGmFkAM2TFA65c3NgGwpnIkUM8oVqPX2e9S7IVg=
github.com/syncthing/notify v0.0.0-20250207082249-f0fa8f99c2bc/go.mod h1:J0q59IWjLtpRIJulohwqEZvjzwOfTEPp8SVhDJl+y0Y= 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 h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= 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= github.com/thejerf/suture/v4 v4.0.6 h1:QsuCEsCqb03xF9tPAsWAj8QOAJBgQI1c0VqJNaingg8=
@@ -261,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 h1:eivXxkp5ud5+4+NVN9e4goxC5mSh3n1RHov+gsblM2g=
github.com/willabides/kongplete v0.4.0/go.mod h1:0P0jtWD9aTsqPSUAl4de35DLghrr57XcayPyvqSi2X8= 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.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 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 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 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= 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 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 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.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= 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-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-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-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.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 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-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-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-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-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-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-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-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-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.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
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.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-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-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.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
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/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -341,50 +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-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-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-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.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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
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/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.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.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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/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.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.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
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/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 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-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-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.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.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -398,8 +342,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -415,29 +359,5 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.36.0 h1:EQXNRn4nIS+gfsKeUTymHIz1waxuv5BzU7558dHSfH8=
modernc.org/sqlite v1.36.0/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
+4 -2
View File
@@ -2,7 +2,7 @@
"A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.", "A device with that ID is already added.": "أضيف هذا الجهاز بالفعل.",
"A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.", "A negative number of days doesn't make sense.": "لا يمكن استخدام قيمة سالبة لعدد الأيام.",
"A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.", "A new major version may not be compatible with previous versions.": "الإصدار الجديد قد لا يتوافق مع الإصدارات السابقة.",
"API Key": "مفتاح API", "API Key": "مفتاح واجهة برمجة التطبيقات \"API\"",
"About": "حول", "About": "حول",
"Action": "إجراء", "Action": "إجراء",
"Actions": "الإجراءات", "Actions": "الإجراءات",
@@ -27,6 +27,7 @@
"Allowed Networks": "الشبكات المسموح بها", "Allowed Networks": "الشبكات المسموح بها",
"Alphabetic": "أبجدية", "Alphabetic": "أبجدية",
"Altered by ignoring deletes.": "تغير بتجاهل عمليات الحذف.", "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.": "الإصدار يعالج بواسطة أمر خارجي. يجب إزالة الملف من المجلدات المشتركة. إذا كان المسار للتطبيق يحتوي على مسافات، يجب وضعها بين علامتي تنصيص دلالة على الاقتباس.", "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 Reporting": "تقارير الإستخدام المجهولة",
"Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟", "Anonymous usage report format has changed. Would you like to move to the new format?": "هل تريد الانتقال الى التصميم الجديد لتقرير الاستخدام المجهول ؟",
@@ -52,6 +53,7 @@
"Body:": "جسم:", "Body:": "جسم:",
"Bugs": "أخطاء برمجية", "Bugs": "أخطاء برمجية",
"Cancel": "إلغاء", "Cancel": "إلغاء",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "لا يمكن تفعيله عندما يكون نوع المجلد هو \"{{foldertype}}\".",
"Changelog": "سجل التغيير", "Changelog": "سجل التغيير",
"Clean out after": "نظف بعد", "Clean out after": "نظف بعد",
"Cleaning Versions": "إصدارات نظيفة", "Cleaning Versions": "إصدارات نظيفة",
@@ -154,7 +156,7 @@
"Failed Items": "العناصر الفاشلة", "Failed Items": "العناصر الفاشلة",
"Failed to load file versions.": "لم يُتَوَصَّل لنسخة الملف.", "Failed to load file versions.": "لم يُتَوَصَّل لنسخة الملف.",
"Failed to load ignore patterns.": "فشل التَّوَصُّل إلى مُرَشِّحات التجاهل.", "Failed to load ignore patterns.": "فشل التَّوَصُّل إلى مُرَشِّحات التجاهل.",
"Failed to setup, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى", "Failed to set up, retrying": "فشل الإعداد، تجري المحاولة مرة أخرى",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "يُتوقع فشل الاتصال بخوادم IPv6، إذا لم يكن IPv6 متاحا.",
"File Pull Order": "ترتيب استيراد الملفات", "File Pull Order": "ترتيب استيراد الملفات",
"File Versioning": "إصدارات الملف", "File Versioning": "إصدارات الملف",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Разрешени мрежи", "Allowed Networks": "Разрешени мрежи",
"Alphabetic": "Азбучен ред", "Alphabetic": "Азбучен ред",
"Altered by ignoring deletes.": "Промяна чрез пренебрегване на премахваните елементи.", "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.": "Външна команда управлява версиите. Тя трябва да премахне файла от синхронизираната папка. Ако в пътя до приложението има интервали, то той трябва да бъде поставен в кавички.", "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 Reporting": "Анонимно отчитане на употреба",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Форматът на данните за анонимно отчитане на употреба е променен. Желаете ли да използвате него вместо стария?",
@@ -52,6 +53,7 @@
"Body:": "Съдържание:", "Body:": "Съдържание:",
"Bugs": "Дефекти", "Bugs": "Дефекти",
"Cancel": "Отказ", "Cancel": "Отказ",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Не мож да бъде включено, ако вида на папката е „{{foldertype}}“.",
"Changelog": "Дневник на промените", "Changelog": "Дневник на промените",
"Clean out after": "Почистване след", "Clean out after": "Почистване след",
"Cleaning Versions": "Почистване на версии", "Cleaning Versions": "Почистване на версии",
@@ -154,7 +156,7 @@
"Failed Items": "Елементи с грешка", "Failed Items": "Елементи с грешка",
"Failed to load file versions.": "Грешка при зареждане на версии.", "Failed to load file versions.": "Грешка при зареждане на версии.",
"Failed to load ignore patterns.": "Грешка при зареждане на шаблони за пренебрегване.", "Failed to load ignore patterns.": "Грешка при зареждане на шаблони за пренебрегване.",
"Failed to setup, retrying": "Грешка при настройване, извършва се повторен опит", "Failed to set up, retrying": "Грешка при настройване, извършва се повторен опит",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към сървъри по IPv6 може да се очаква ако няма свързаност по IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Неуспешна връзка към сървъри по IPv6 може да се очаква ако няма свързаност по IPv6.",
"File Pull Order": "Ред на изтегляне", "File Pull Order": "Ред на изтегляне",
"File Versioning": "Версии на файловете", "File Versioning": "Версии на файловете",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Elements fallats", "Failed Items": "Elements fallats",
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.", "Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
"Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.", "Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.",
"Failed to setup, retrying": "No s'ha pogut configurar, s'està tornant a provar", "Failed to set up, retrying": "No s'ha pogut configurar, s'està tornant a provar",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "S'espera que no es pugui connectar als servidors IPv6 si no hi ha connectivitat IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "S'espera que no es pugui connectar als servidors IPv6 si no hi ha connectivitat IPv6.",
"File Pull Order": "Ordre d'agafar fitxers", "File Pull Order": "Ordre d'agafar fitxers",
"File Versioning": "Versionat de Fitxers", "File Versioning": "Versionat de Fitxers",
@@ -150,7 +150,7 @@
"Failed Items": "Objectes fallits", "Failed Items": "Objectes fallits",
"Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.", "Failed to load file versions.": "No s'han pogut carregar les versions dels fitxers.",
"Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.", "Failed to load ignore patterns.": "No s'han pogut carregar els patrons ignorats.",
"Failed to setup, retrying": "Errada en la configuració, reintentant", "Failed to set up, retrying": "Errada en la configuració, reintentant",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "És possible que es produïsca una fallada al connectar als servidors IPv6 si no hi ha connectivitat IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "És possible que es produïsca una fallada al connectar als servidors IPv6 si no hi ha connectivitat IPv6.",
"File Pull Order": "Ordre de fitxers del pull", "File Pull Order": "Ordre de fitxers del pull",
"File Versioning": "Versionat de fitxer", "File Versioning": "Versionat de fitxer",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Nezdařené položky", "Failed Items": "Nezdařené položky",
"Failed to load file versions.": "Nepodařilo se nahrát verze souboru.", "Failed to load file versions.": "Nepodařilo se nahrát verze souboru.",
"Failed to load ignore patterns.": "Načtení vzorů ignorovaného se nezdařilo.", "Failed to load ignore patterns.": "Načtení vzorů ignorovaného se nezdařilo.",
"Failed to setup, retrying": "Nastavování se nezdařilo, zkouší se znovu", "Failed to set up, retrying": "Nastavování se nezdařilo, zkouší se znovu",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Je v pořádku, když se připojení k IPv6 serverům nezdaří, pokud není k dispozici IPv6 konektivita.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Je v pořádku, když se připojení k IPv6 serverům nezdaří, pokud není k dispozici IPv6 konektivita.",
"File Pull Order": "Pořadí stahování souborů", "File Pull Order": "Pořadí stahování souborů",
"File Versioning": "Správa verzí souborů", "File Versioning": "Správa verzí souborů",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Mislykkede filer", "Failed Items": "Mislykkede filer",
"Failed to load file versions.": "Fil versioner kunne ikke indlæses.", "Failed to load file versions.": "Fil versioner kunne ikke indlæses.",
"Failed to load ignore patterns.": "Ignorerings-mønstre kunne ikke indlæses.", "Failed to load ignore patterns.": "Ignorerings-mønstre kunne ikke indlæses.",
"Failed to setup, retrying": "Opsætning mislykkedes; prøver igen", "Failed to set up, retrying": "Opsætning mislykkedes; prøver igen",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Fejl i forbindelse med opkobling til IPv6-servere skal forventes, hvis der ikke er IPv6-forbindelse.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Fejl i forbindelse med opkobling til IPv6-servere skal forventes, hvis der ikke er IPv6-forbindelse.",
"File Pull Order": "Hentningsrækkefølge for filer", "File Pull Order": "Hentningsrækkefølge for filer",
"File Versioning": "Filversionering", "File Versioning": "Filversionering",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Erlaubte Netzwerke", "Allowed Networks": "Erlaubte Netzwerke",
"Alphabetic": "Alphabetisch", "Alphabetic": "Alphabetisch",
"Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.", "Altered by ignoring deletes.": "Weicht ab, weil Löschungen ignoriert werden.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Immer eingeschaltet, wenn der Ordnertyp „{{foldertype}}“ ist.",
"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.": "Die Versionierung erfolgt über einen externen Befehl. Er muss die Datei aus dem geteilten Ordner entfernen. Wenn der Pfad zur Anwendung Leerzeichen enthält, sollte er in Anführungszeichen gesetzt werden.", "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.": "Die Versionierung erfolgt über einen externen Befehl. Er muss die Datei aus dem geteilten Ordner entfernen. Wenn der Pfad zur Anwendung Leerzeichen enthält, sollte er in Anführungszeichen gesetzt werden.",
"Anonymous Usage Reporting": "Anonymer Nutzungsbericht", "Anonymous Usage Reporting": "Anonymer Nutzungsbericht",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Das Format des anonymen Nutzungsberichts hat sich geändert. Möchten Sie auf das neue Format umsteigen?",
@@ -52,6 +53,7 @@
"Body:": "Nachrichtentext:", "Body:": "Nachrichtentext:",
"Bugs": "Fehler", "Bugs": "Fehler",
"Cancel": "Abbrechen", "Cancel": "Abbrechen",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kann nicht aktiviert werden, wenn der Ordnertyp „{{foldertype}}“ ist.",
"Changelog": "Änderungsprotokoll", "Changelog": "Änderungsprotokoll",
"Clean out after": "Löschen nach", "Clean out after": "Löschen nach",
"Cleaning Versions": "Versionen bereinigen", "Cleaning Versions": "Versionen bereinigen",
@@ -154,7 +156,7 @@
"Failed Items": "Fehlgeschlagene Elemente", "Failed Items": "Fehlgeschlagene Elemente",
"Failed to load file versions.": "Fehler beim Laden der Dateiversionen.", "Failed to load file versions.": "Fehler beim Laden der Dateiversionen.",
"Failed to load ignore patterns.": "Fehler beim Laden der Ignoriermuster.", "Failed to load ignore patterns.": "Fehler beim Laden der Ignoriermuster.",
"Failed to setup, retrying": "Fehler beim Einrichten, erneuter Versuch", "Failed to set up, retrying": "Fehler beim Einrichten, erneuter Versuch",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Ein Verbindungsfehler zu IPv6-Servern ist zu erwarten, wenn es keine IPv6-Konnektivität gibt.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Ein Verbindungsfehler zu IPv6-Servern ist zu erwarten, wenn es keine IPv6-Konnektivität gibt.",
"File Pull Order": "Dateiübertragungsreihenfolge", "File Pull Order": "Dateiübertragungsreihenfolge",
"File Versioning": "Dateiversionierung", "File Versioning": "Dateiversionierung",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Αρχεία που απέτυχαν", "Failed Items": "Αρχεία που απέτυχαν",
"Failed to load file versions.": "Η φόρτωση των εκδόσεων αρχείων απέτυχε.", "Failed to load file versions.": "Η φόρτωση των εκδόσεων αρχείων απέτυχε.",
"Failed to load ignore patterns.": "Αποτυχία φόρτωσης μοτίβων παράβλεψης.", "Failed to load ignore patterns.": "Αποτυχία φόρτωσης μοτίβων παράβλεψης.",
"Failed to setup, retrying": "Αποτυχία ενεργοποίησης, γίνεται νέα προσπάθεια", "Failed to set up, retrying": "Αποτυχία ενεργοποίησης, γίνεται νέα προσπάθεια",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Είναι φυσιολογική η αποτυχία σύνδεσης σε εξυπηρετητές IPv6 όταν δεν υπάρχει συνδεσιμότητα IPv6.",
"File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία", "File Pull Order": "Σειρά με την οποία θα κατεβαίνουν τα αρχεία",
"File Versioning": "Τήρηση εκδόσεων αρχείων", "File Versioning": "Τήρηση εκδόσεων αρχείων",
+1 -1
View File
@@ -150,7 +150,7 @@
"Failed Items": "Failed Items", "Failed Items": "Failed Items",
"Failed to load file versions.": "Failed to load file versions.", "Failed to load file versions.": "Failed to load file versions.",
"Failed to load ignore patterns.": "Failed to load ignore patterns.", "Failed to load ignore patterns.": "Failed to load ignore patterns.",
"Failed to setup, retrying": "Failed to setup, retrying", "Failed to set up, retrying": "Failed to set up, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order", "File Pull Order": "File Pull Order",
"File Versioning": "File Versioning", "File Versioning": "File Versioning",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Allowed Networks", "Allowed Networks": "Allowed Networks",
"Alphabetic": "Alphabetic", "Alphabetic": "Alphabetic",
"Altered by ignoring deletes.": "Altered by ignoring deletes.", "Altered by ignoring deletes.": "Altered by ignoring deletes.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Always turned on when the folder type is \"{{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.": "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.", "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.": "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 Reporting", "Anonymous Usage Reporting": "Anonymous Usage Reporting",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymous usage report format has changed. Would you like to move to the new format?",
@@ -52,6 +53,7 @@
"Body:": "Body:", "Body:": "Body:",
"Bugs": "Bugs", "Bugs": "Bugs",
"Cancel": "Cancel", "Cancel": "Cancel",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Cannot be enabled when the folder type is \"{{foldertype}}\".",
"Changelog": "Changelog", "Changelog": "Changelog",
"Clean out after": "Clean out after", "Clean out after": "Clean out after",
"Cleaning Versions": "Cleaning Versions", "Cleaning Versions": "Cleaning Versions",
@@ -154,7 +156,7 @@
"Failed Items": "Failed Items", "Failed Items": "Failed Items",
"Failed to load file versions.": "Failed to load file versions.", "Failed to load file versions.": "Failed to load file versions.",
"Failed to load ignore patterns.": "Failed to load ignore patterns.", "Failed to load ignore patterns.": "Failed to load ignore patterns.",
"Failed to setup, retrying": "Failed to setup, retrying", "Failed to set up, retrying": "Failed to set up, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order", "File Pull Order": "File Pull Order",
"File Versioning": "File Versioning", "File Versioning": "File Versioning",
+1 -1
View File
@@ -156,7 +156,7 @@
"Failed Items": "Failed Items", "Failed Items": "Failed Items",
"Failed to load file versions.": "Failed to load file versions.", "Failed to load file versions.": "Failed to load file versions.",
"Failed to load ignore patterns.": "Failed to load ignore patterns.", "Failed to load ignore patterns.": "Failed to load ignore patterns.",
"Failed to setup, retrying": "Failed to setup, retrying", "Failed to set up, retrying": "Failed to set up, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order", "File Pull Order": "File Pull Order",
"File Versioning": "File Versioning", "File Versioning": "File Versioning",
+1 -1
View File
@@ -106,7 +106,7 @@
"Error": "Eraro", "Error": "Eraro",
"External File Versioning": "Ekstera Versionado de Dosiero", "External File Versioning": "Ekstera Versionado de Dosiero",
"Failed Items": "Malsukcesaj Eroj", "Failed Items": "Malsukcesaj Eroj",
"Failed to setup, retrying": "Malsukcesis agordi, provante denove", "Failed to set up, retrying": "Malsukcesis agordi, provante denove",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Malsukceso por konekti al IPv6 serviloj atendante se ekzistas neniu IPv6 konektebleco.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Malsukceso por konekti al IPv6 serviloj atendante se ekzistas neniu IPv6 konektebleco.",
"File Pull Order": "Ordo por Tiri Dosieron", "File Pull Order": "Ordo por Tiri Dosieron",
"File Versioning": "Versionado de Dosieroj", "File Versioning": "Versionado de Dosieroj",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Elementos fallidos", "Failed Items": "Elementos fallidos",
"Failed to load file versions.": "Error al cargar las versiones de los archivos.", "Failed to load file versions.": "Error al cargar las versiones de los archivos.",
"Failed to load ignore patterns.": "No se pudieron cargar los patrones de ignorar.", "Failed to load ignore patterns.": "No se pudieron cargar los patrones de ignorar.",
"Failed to setup, retrying": "Fallo en la configuración, reintentando", "Failed to set up, retrying": "Fallo en la configuración, reintentando",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Se espera un fallo al conectar a los servidores IPv6 si no hay conectividad IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Se espera un fallo al conectar a los servidores IPv6 si no hay conectividad IPv6.",
"File Pull Order": "Orden de Obtención de los Archivos", "File Pull Order": "Orden de Obtención de los Archivos",
"File Versioning": "Versionado de ficheros", "File Versioning": "Versionado de ficheros",
+6
View File
@@ -9,9 +9,15 @@
"Add Folder": "Lisa kaust", "Add Folder": "Lisa kaust",
"Add new folder?": "Lisa uus kaust?", "Add new folder?": "Lisa uus kaust?",
"Address": "Aadress", "Address": "Aadress",
"Addresses": "Aadressid",
"All Data": "Kõik andmed",
"All Time": "Kõik ajad",
"Allowed Networks": "Lubatud võrgud",
"Alphabetic": "Tähestikuline", "Alphabetic": "Tähestikuline",
"Automatic upgrades": "Automaatsed uuendused", "Automatic upgrades": "Automaatsed uuendused",
"Be careful!": "Ettevaatust!", "Be careful!": "Ettevaatust!",
"Cancel": "Loobu",
"Changelog": "Muudatuste nimekiri",
"Close": "Sulge", "Close": "Sulge",
"Configured": "Seadistatud", "Configured": "Seadistatud",
"Connection Error": "Ühenduse viga", "Connection Error": "Ühenduse viga",
+1 -1
View File
@@ -130,7 +130,7 @@
"External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa", "External File Versioning": "Fitxategi bertsioen kanpoko kudeaketa",
"Failed Items": "Huts egin duten fitxategiak", "Failed Items": "Huts egin duten fitxategiak",
"Failed to load ignore patterns.": "Huts egin du baztertze ereduak kargatzean.", "Failed to load ignore patterns.": "Huts egin du baztertze ereduak kargatzean.",
"Failed to setup, retrying": "Konfigurazioan huts egitea, berriro saiatuz", "Failed to set up, retrying": "Konfigurazioan huts egitea, berriro saiatuz",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 zerbitzariei buruzko konexioak huts eginen du, IPv6 konektibitaterik ez bada", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 zerbitzariei buruzko konexioak huts eginen du, IPv6 konektibitaterik ez bada",
"File Pull Order": "Fitxategiak berreskuratzeko ordena", "File Pull Order": "Fitxategiak berreskuratzeko ordena",
"File Versioning": "Fitxategiak zaintzeko metodoa", "File Versioning": "Fitxategiak zaintzeko metodoa",
+1 -1
View File
@@ -122,7 +122,7 @@
"External": "Ulkoinen", "External": "Ulkoinen",
"External File Versioning": "Ulkoinen tiedostoversionti", "External File Versioning": "Ulkoinen tiedostoversionti",
"Failed Items": "Epäonnistuneet kohteet", "Failed Items": "Epäonnistuneet kohteet",
"Failed to setup, retrying": "Käyttöönotto epäonnistui, Yritetään uudelleen", "Failed to set up, retrying": "Käyttöönotto epäonnistui, Yritetään uudelleen",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Yhteys IPv6-palvelimiin todennäköisesti epäonnistuu, koska IPv6-yhteyksiä ei ole.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Yhteys IPv6-palvelimiin todennäköisesti epäonnistuu, koska IPv6-yhteyksiä ei ole.",
"File Pull Order": "Tiedostojen noutojärjestys", "File Pull Order": "Tiedostojen noutojärjestys",
"File Versioning": "Tiedostoversiointi", "File Versioning": "Tiedostoversiointi",
+4 -2
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Mga Pinapayagang Network", "Allowed Networks": "Mga Pinapayagang Network",
"Alphabetic": "Alpabetiko", "Alphabetic": "Alpabetiko",
"Altered by ignoring deletes.": "Binago sa pamamagitan ng hindi pagpansin sa mga pagtanggal.", "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.", "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 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?", "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:", "Body:": "Body:",
"Bugs": "Mga Bug", "Bugs": "Mga Bug",
"Cancel": "Kanselahin", "Cancel": "Kanselahin",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Hindi maaaring paganahin kapag ang uri ng folder ay \"{{foldertype}}\".",
"Changelog": "Mga Pagbabago", "Changelog": "Mga Pagbabago",
"Clean out after": "Linisin pagkatapos", "Clean out after": "Linisin pagkatapos",
"Cleaning Versions": "Mga Bersyon ng Paglinis", "Cleaning Versions": "Mga Bersyon ng Paglinis",
@@ -154,7 +156,7 @@
"Failed Items": "Mga Nabigong Item", "Failed Items": "Mga Nabigong Item",
"Failed to load file versions.": "Nabigong i-load ang mga bersyon ng file.", "Failed to load file versions.": "Nabigong i-load ang mga bersyon ng file.",
"Failed to load ignore patterns.": "Nabigong i-load ang mga ignore pattern.", "Failed to load ignore patterns.": "Nabigong i-load ang mga ignore pattern.",
"Failed to setup, retrying": "Nabigong i-set up, sinusubukan muli", "Failed to set up, retrying": "Nabigong i-set up, sinusubukan muli",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Inaasahan ang pagbigo sa pagkonekta sa mga IPv6 na server kapag walang konektibidad sa IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Inaasahan ang pagbigo sa pagkonekta sa mga IPv6 na server kapag walang konektibidad sa IPv6.",
"File Pull Order": "Order ng Pagkuha ng File", "File Pull Order": "Order ng Pagkuha ng File",
"File Versioning": "File Versioning", "File Versioning": "File Versioning",
@@ -311,7 +313,7 @@
"Receive Encrypted": "Makatanggap Naka-Encrypt", "Receive Encrypted": "Makatanggap Naka-Encrypt",
"Receive Only": "Makatanggap Lamang", "Receive Only": "Makatanggap Lamang",
"Received data is already encrypted": "Naka-encrypt na ang natanggap na data", "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", "Reduced by ignore patterns": "Binabawasan ng mga ignore pattern",
"Relay LAN": "Relay na LAN", "Relay LAN": "Relay na LAN",
"Relay WAN": "Relay na WAN", "Relay WAN": "Relay na WAN",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Réseaux autorisés", "Allowed Networks": "Réseaux autorisés",
"Alphabetic": "Alphabétique", "Alphabetic": "Alphabétique",
"Altered by ignoring deletes.": "Protégé par \"Ignore Delete\".", "Altered by ignoring deletes.": "Protégé par \"Ignore Delete\".",
"Always turned on when the folder type is \"{%foldertype%}\".": "Toujours activé pour le type de partage \"{{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.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.", "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.": "Une commande externe gère les versions de fichiers. Il lui incombe de supprimer les fichiers du répertoire partagé. Si le chemin contient des espaces, il doit être spécifié entre guillemets.",
"Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation", "Anonymous Usage Reporting": "Rapport anonyme de statistiques d'utilisation",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Le format du rapport anonyme d'utilisation a changé. Voulez-vous passer au nouveau format ?",
@@ -52,6 +53,7 @@
"Body:": "Corps du message :", "Body:": "Corps du message :",
"Bugs": "Bogues", "Bugs": "Bogues",
"Cancel": "Annuler", "Cancel": "Annuler",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Ne peut être activé pour le type de partage \"{{foldertype}}\".",
"Changelog": "Historique des versions", "Changelog": "Historique des versions",
"Clean out after": "Conserver pendant", "Clean out after": "Conserver pendant",
"Cleaning Versions": "Purge des versions", "Cleaning Versions": "Purge des versions",
@@ -154,7 +156,7 @@
"Failed Items": "Éléments en échec", "Failed Items": "Éléments en échec",
"Failed to load file versions.": "Échec de chargement des versions de fichiers.", "Failed to load file versions.": "Échec de chargement des versions de fichiers.",
"Failed to load ignore patterns.": "Échec du chargement des masques d'exclusions.", "Failed to load ignore patterns.": "Échec du chargement des masques d'exclusions.",
"Failed to setup, retrying": "Échec, nouvel essai", "Failed to set up, retrying": "Échec, nouvel essai",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connexion aux serveurs en IPv6 va échouer s'il n'y a pas de connectivité IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connexion aux serveurs en IPv6 va échouer s'il n'y a pas de connectivité IPv6.",
"File Pull Order": "Ordre de récupération des fichiers", "File Pull Order": "Ordre de récupération des fichiers",
"File Versioning": "Préservation des fichiers", "File Versioning": "Préservation des fichiers",
+1 -1
View File
@@ -146,7 +146,7 @@
"Error": "Flater", "Error": "Flater",
"External File Versioning": "Ekstern ferzjebehear foar triemen", "External File Versioning": "Ekstern ferzjebehear foar triemen",
"Failed Items": "Mislearre items", "Failed Items": "Mislearre items",
"Failed to setup, retrying": "Ynskeakeljen mislearre, wurd no opnij besocht", "Failed to set up, retrying": "Ynskeakeljen mislearre, wurd no opnij besocht",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Mislearjen fan it ferbinen mei IPv6-tsjinners wurd ferwachte as der gjin stipe foar IPv6-ferbinings is.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Mislearjen fan it ferbinen mei IPv6-tsjinners wurd ferwachte as der gjin stipe foar IPv6-ferbinings is.",
"File Pull Order": "Triemlûkfolchoarder", "File Pull Order": "Triemlûkfolchoarder",
"File Versioning": "Triemferzjebehear", "File Versioning": "Triemferzjebehear",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Líonraí Ceadaithe", "Allowed Networks": "Líonraí Ceadaithe",
"Alphabetic": "Aibítreach", "Alphabetic": "Aibítreach",
"Altered by ignoring deletes.": "Athraithe trí neamhaird a dhéanamh ar scriosadh.", "Altered by ignoring deletes.": "Athraithe trí neamhaird a dhéanamh ar scriosadh.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Cuirtear ar siúl i gcónaí é nuair is é \"{{foldertype}}\" an cineál fillteáin.",
"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.": "Láimhseálann ordú seachtrach an leagan. Caithfidh sé an comhad a bhaint den fhillteán comhroinnte. Má tá spásanna sa chosán chuig an bhfeidhmchlár, ba chóir é a lua.", "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.": "Láimhseálann ordú seachtrach an leagan. Caithfidh sé an comhad a bhaint den fhillteán comhroinnte. Má tá spásanna sa chosán chuig an bhfeidhmchlár, ba chóir é a lua.",
"Anonymous Usage Reporting": "Tuairisciú Úsáide Gan Ainm", "Anonymous Usage Reporting": "Tuairisciú Úsáide Gan Ainm",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Tá athrú tagtha ar fhormáid na tuarascála úsáide gan ainm. Ar mhaith leat bogadh go dtí an fhormáid nua?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Tá athrú tagtha ar fhormáid na tuarascála úsáide gan ainm. Ar mhaith leat bogadh go dtí an fhormáid nua?",
@@ -52,6 +53,7 @@
"Body:": "Comhlacht:", "Body:": "Comhlacht:",
"Bugs": "Fabhtanna", "Bugs": "Fabhtanna",
"Cancel": "Cuir ar ceal", "Cancel": "Cuir ar ceal",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Ní féidir é a chumasú nuair is é \"{{foldertype}}\" an cineál fillteáin.",
"Changelog": "ChangelogName", "Changelog": "ChangelogName",
"Clean out after": "Glan amach tar éis", "Clean out after": "Glan amach tar éis",
"Cleaning Versions": "Leaganacha Glantacháin", "Cleaning Versions": "Leaganacha Glantacháin",
@@ -154,7 +156,7 @@
"Failed Items": "Míreanna Teipthe", "Failed Items": "Míreanna Teipthe",
"Failed to load file versions.": "Theip ar luchtú leaganacha comhaid.", "Failed to load file versions.": "Theip ar luchtú leaganacha comhaid.",
"Failed to load ignore patterns.": "Theip ar phatrúin neamhairde a luchtú.", "Failed to load ignore patterns.": "Theip ar phatrúin neamhairde a luchtú.",
"Failed to setup, retrying": "Theip ar thus, ag triail arís", "Failed to set up, retrying": "Theip ar thus, ag triail arís",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Táthar ag súil le mainneachtain ceangal le freastalaithe IPv6 mura bhfuil nascacht IPv6 ann.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Táthar ag súil le mainneachtain ceangal le freastalaithe IPv6 mura bhfuil nascacht IPv6 ann.",
"File Pull Order": "Ordú Tarraingthe Comhad", "File Pull Order": "Ordú Tarraingthe Comhad",
"File Versioning": "Leagan Comhaid", "File Versioning": "Leagan Comhaid",
+1 -1
View File
@@ -153,7 +153,7 @@
"Failed Items": "Elmentos fallados", "Failed Items": "Elmentos fallados",
"Failed to load file versions.": "Fallou a carga das versións dos ficheiros.", "Failed to load file versions.": "Fallou a carga das versións dos ficheiros.",
"Failed to load ignore patterns.": "Fallou a carga de patróns ignorados.", "Failed to load ignore patterns.": "Fallou a carga de patróns ignorados.",
"Failed to setup, retrying": "Fallou a configuración, reintentando", "Failed to set up, retrying": "Fallou a configuración, reintentando",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "É de agardar o fallo ao conectar con servidores IPv6 se non hai conexión por IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "É de agardar o fallo ao conectar con servidores IPv6 se non hai conexión por IPv6.",
"File Pull Order": "Orde de Obtención de Arquivos", "File Pull Order": "Orde de Obtención de Arquivos",
"File Versioning": "Versionado de Ficheiros", "File Versioning": "Versionado de Ficheiros",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "פריטים שנכשלו", "Failed Items": "פריטים שנכשלו",
"Failed to load file versions.": "טעינת גרסאות קבצים נכשלה.", "Failed to load file versions.": "טעינת גרסאות קבצים נכשלה.",
"Failed to load ignore patterns.": "טעינת דפוסי התעלמות נכשלה.", "Failed to load ignore patterns.": "טעינת דפוסי התעלמות נכשלה.",
"Failed to setup, retrying": "ההגדרה נכשלה, מנסה שוב", "Failed to set up, retrying": "ההגדרה נכשלה, מנסה שוב",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "צפוי כשל בהתחברות לשרתי IPv6 אם אין קישוריות IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "צפוי כשל בהתחברות לשרתי IPv6 אם אין קישוריות IPv6.",
"File Pull Order": "סדר משיכת קבצים", "File Pull Order": "סדר משיכת קבצים",
"File Versioning": "ניהול גרסאות קבצים", "File Versioning": "ניהול גרסאות קבצים",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "विफल वस्तुएं", "Failed Items": "विफल वस्तुएं",
"Failed to load file versions.": "फाइल संस्करण लोड करने में विफल।", "Failed to load file versions.": "फाइल संस्करण लोड करने में विफल।",
"Failed to load ignore patterns.": "नजरअंदाज प्रतिमान लोड करने में विफल।", "Failed to load ignore patterns.": "नजरअंदाज प्रतिमान लोड करने में विफल।",
"Failed to setup, retrying": "स्थापना करने में विफल, पुनः प्रयास किया जा रहा है", "Failed to set up, retrying": "स्थापना करने में विफल, पुनः प्रयास किया जा रहा है",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "यदि IPv6 संयोजकता नहीं है तो IPv6 सर्वर से जुड़ने में विफलता अपेक्षित है।", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "यदि IPv6 संयोजकता नहीं है तो IPv6 सर्वर से जुड़ने में विफलता अपेक्षित है।",
"File Pull Order": "फाइल खींचने का क्रम", "File Pull Order": "फाइल खींचने का क्रम",
"File Versioning": "फाइल संस्करणीकरण", "File Versioning": "फाइल संस्करणीकरण",
+1 -1
View File
@@ -146,7 +146,7 @@
"Failed Items": "Hibás elemek", "Failed Items": "Hibás elemek",
"Failed to load file versions.": "Nem sikerült betölteni a fájlverziókat.", "Failed to load file versions.": "Nem sikerült betölteni a fájlverziókat.",
"Failed to load ignore patterns.": "Nem sikerült betölteni a mellőzési mintákat.", "Failed to load ignore patterns.": "Nem sikerült betölteni a mellőzési mintákat.",
"Failed to setup, retrying": "Telepítés nem sikerült, újrapróbálkozás", "Failed to set up, retrying": "Telepítés nem sikerült, újrapróbálkozás",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Mivel nincs IPv6 kapcsolat, ezért várhatóan nem fog sikerülni IPv6-os szerverekhez csatlakozni.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Mivel nincs IPv6 kapcsolat, ezért várhatóan nem fog sikerülni IPv6-os szerverekhez csatlakozni.",
"File Pull Order": "Fájlküldési sorrend", "File Pull Order": "Fájlküldési sorrend",
"File Versioning": "Fájlverzió-követés", "File Versioning": "Fájlverzió-követés",
+1 -1
View File
@@ -146,7 +146,7 @@
"Failed Items": "Berkas yang gagal", "Failed Items": "Berkas yang gagal",
"Failed to load file versions.": "Gagal memuat versi berkas.", "Failed to load file versions.": "Gagal memuat versi berkas.",
"Failed to load ignore patterns.": "Gagal memuat pola pengabaian.", "Failed to load ignore patterns.": "Gagal memuat pola pengabaian.",
"Failed to setup, retrying": "Gagal menyiapkan, mengulang", "Failed to set up, retrying": "Gagal menyiapkan, mengulang",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Gagal untuk menyambung ke server IPv6 itu disangka apabila tidak ada konektivitas IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Gagal untuk menyambung ke server IPv6 itu disangka apabila tidak ada konektivitas IPv6.",
"File Pull Order": "Urutan Penarikan Berkas", "File Pull Order": "Urutan Penarikan Berkas",
"File Versioning": "Pemversian Berkas", "File Versioning": "Pemversian Berkas",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Reti Consentite", "Allowed Networks": "Reti Consentite",
"Alphabetic": "Alfabetico", "Alphabetic": "Alfabetico",
"Altered by ignoring deletes.": "Modificato ignorando le eliminazioni.", "Altered by ignoring deletes.": "Modificato ignorando le eliminazioni.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre attivato quando il tipo di cartella è \"{{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.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.", "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.": "Il controllo versione è gestito da un comando esterno. Quest'ultimo deve rimuovere il file dalla cartella condivisa. Se il percorso dell'applicazione contiene spazi, deve essere indicato tra virgolette.",
"Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo", "Anonymous Usage Reporting": "Statistiche Anonime di Utilizzo",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Il formato delle statistiche anonime di utilizzo è cambiato. Vuoi passare al nuovo formato?",
@@ -52,6 +53,7 @@
"Body:": "Corpo:", "Body:": "Corpo:",
"Bugs": "Bug", "Bugs": "Bug",
"Cancel": "Annulla", "Cancel": "Annulla",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Non può essere abilitato se il tipo di cartella è \"{{foldertype}}\".",
"Changelog": "Registro modifiche", "Changelog": "Registro modifiche",
"Clean out after": "Svuota dopo", "Clean out after": "Svuota dopo",
"Cleaning Versions": "Pulizia Versioni", "Cleaning Versions": "Pulizia Versioni",
@@ -154,7 +156,7 @@
"Failed Items": "Elementi Errati", "Failed Items": "Elementi Errati",
"Failed to load file versions.": "Impossibile caricare le versioni dei file.", "Failed to load file versions.": "Impossibile caricare le versioni dei file.",
"Failed to load ignore patterns.": "Impossibile caricare gli schemi di esclusione.", "Failed to load ignore patterns.": "Impossibile caricare gli schemi di esclusione.",
"Failed to setup, retrying": "Configurazione fallita, riprovo", "Failed to set up, retrying": "Configurazione fallita, riprovo",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connessione a server IPv6 fallisce se non c'è connettività IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "La connessione a server IPv6 fallisce se non c'è connettività IPv6.",
"File Pull Order": "Ordine Prelievo File", "File Pull Order": "Ordine Prelievo File",
"File Versioning": "Controllo Versione File", "File Versioning": "Controllo Versione File",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "허가된 망", "Allowed Networks": "허가된 망",
"Alphabetic": "가나다순", "Alphabetic": "가나다순",
"Altered by ignoring deletes.": "삭제 항목 무시로 변경됨", "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.": "외부 명령이 파일 버전을 관리합니다. 공유 폴더에서 파일을 삭제해야 합니다. 응용 프로그램의 경로에 공백이 있으면 따옴표로 묶어야 합니다.", "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 Reporting": "익명 사용 보고",
"Anonymous usage report format has changed. Would you like to move to the new format?": "익명 사용 보고의 형식이 변경되었습니다. 새 형식으로 설정을 변경하시겠습니까?", "Anonymous usage report format has changed. Would you like to move to the new format?": "익명 사용 보고의 형식이 변경되었습니다. 새 형식으로 설정을 변경하시겠습니까?",
@@ -52,6 +53,7 @@
"Body:": "내용:", "Body:": "내용:",
"Bugs": "버그", "Bugs": "버그",
"Cancel": "취소", "Cancel": "취소",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "{{foldertype}} 폴더 유형일 때는 활성화할 수 없습니다.",
"Changelog": "변경 기록", "Changelog": "변경 기록",
"Clean out after": "보관 기간", "Clean out after": "보관 기간",
"Cleaning Versions": "버전 정리", "Cleaning Versions": "버전 정리",
@@ -154,7 +156,7 @@
"Failed Items": "실패 항목", "Failed Items": "실패 항목",
"Failed to load file versions.": "파일 버전을 불러오기에 실패했습니다.", "Failed to load file versions.": "파일 버전을 불러오기에 실패했습니다.",
"Failed to load ignore patterns.": "무시 양식을 불러오기에 실패했습니다.", "Failed to load ignore patterns.": "무시 양식을 불러오기에 실패했습니다.",
"Failed to setup, retrying": "설정 적용 실패; 재시도 중", "Failed to set up, retrying": "설정 적용 실패; 재시도 중",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6에 연결되어 있지 않을 때는 IPv6 서버에 접속하지 못하는 것이 정상입니다.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6에 연결되어 있지 않을 때는 IPv6 서버에 접속하지 못하는 것이 정상입니다.",
"File Pull Order": "파일 수신 순서", "File Pull Order": "파일 수신 순서",
"File Versioning": "파일 버전 관리", "File Versioning": "파일 버전 관리",
+1 -1
View File
@@ -144,7 +144,7 @@
"Failed Items": "Nepavykę siuntimai", "Failed Items": "Nepavykę siuntimai",
"Failed to load file versions.": "Nepavyko įkelti failo versijų.", "Failed to load file versions.": "Nepavyko įkelti failo versijų.",
"Failed to load ignore patterns.": "Nepavyko įkelti nepaisymo šablonų.", "Failed to load ignore patterns.": "Nepavyko įkelti nepaisymo šablonų.",
"Failed to setup, retrying": "Nepavyko nustatyti, bandoma iš naujo", "Failed to set up, retrying": "Nepavyko nustatyti, bandoma iš naujo",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Nesėkmė prisijungti prie IPv6 serverių yra tikėtina, jei nėra IPv6 ryšio.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Nesėkmė prisijungti prie IPv6 serverių yra tikėtina, jei nėra IPv6 ryšio.",
"File Pull Order": "Failų siuntimo tvarka", "File Pull Order": "Failų siuntimo tvarka",
"File Versioning": "Versijų valdymas", "File Versioning": "Versijų valdymas",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Elementsynkronisering som har mislyktes", "Failed Items": "Elementsynkronisering som har mislyktes",
"Failed to load file versions.": "Lasting av fil-versjoner feilet.", "Failed to load file versions.": "Lasting av fil-versjoner feilet.",
"Failed to load ignore patterns.": "Lasting av ignorer mønstre feilet.", "Failed to load ignore patterns.": "Lasting av ignorer mønstre feilet.",
"Failed to setup, retrying": "Klarte ikke å utføre oppsett, prøver igjen", "Failed to set up, retrying": "Klarte ikke å utføre oppsett, prøver igjen",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Å ikke klare å koble til IPv6-tjenere er forventet hvis det ikke er noen IPv6-tilknytning.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Å ikke klare å koble til IPv6-tjenere er forventet hvis det ikke er noen IPv6-tilknytning.",
"File Pull Order": "Filenes henterekkefølge", "File Pull Order": "Filenes henterekkefølge",
"File Versioning": "Versjonskontroll", "File Versioning": "Versjonskontroll",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "Mislukte items", "Failed Items": "Mislukte items",
"Failed to load file versions.": "Laden van bestandsversies mislukt.", "Failed to load file versions.": "Laden van bestandsversies mislukt.",
"Failed to load ignore patterns.": "Laden van negeerpatronen mislukt.", "Failed to load ignore patterns.": "Laden van negeerpatronen mislukt.",
"Failed to setup, retrying": "Instellen mislukt, opnieuw proberen", "Failed to set up, retrying": "Instellen mislukt, opnieuw proberen",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Als er geen IPv6-connectiviteit is worden problemen bij verbinden met IPv6-servers verwacht.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Als er geen IPv6-connectiviteit is worden problemen bij verbinden met IPv6-servers verwacht.",
"File Pull Order": "Volgorde voor binnenhalen van bestanden", "File Pull Order": "Volgorde voor binnenhalen van bestanden",
"File Versioning": "Versiebeheer", "File Versioning": "Versiebeheer",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Dozwolone sieci", "Allowed Networks": "Dozwolone sieci",
"Alphabetic": "Alfabetycznie", "Alphabetic": "Alfabetycznie",
"Altered by ignoring deletes.": "Zmieniono przez ignorowanie usuniętych", "Altered by ignoring deletes.": "Zmieniono przez ignorowanie usuniętych",
"Always turned on when the folder type is \"{%foldertype%}\".": "Zawsze włączone, gdy typ folderu to \"{{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.": "Zewnętrzne polecenie odpowiedzialne jest za wersjonowanie. Musi ono usunąć plik ze współdzielonego folderu. Jeżeli ścieżka do aplikacji zawiera spacje, to powinna ona być zamknięta w cudzysłowie.", "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.": "Zewnętrzne polecenie odpowiedzialne jest za wersjonowanie. Musi ono usunąć plik ze współdzielonego folderu. Jeżeli ścieżka do aplikacji zawiera spacje, to powinna ona być zamknięta w cudzysłowie.",
"Anonymous Usage Reporting": "Anonimowe statystyki użycia", "Anonymous Usage Reporting": "Anonimowe statystyki użycia",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Format anonimowych statystyk użycia uległ zmianie. Czy chcesz przejść na nowy format?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Format anonimowych statystyk użycia uległ zmianie. Czy chcesz przejść na nowy format?",
@@ -52,6 +53,7 @@
"Body:": "Treść:", "Body:": "Treść:",
"Bugs": "Błędy", "Bugs": "Błędy",
"Cancel": "Anuluj", "Cancel": "Anuluj",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Nie można włączyć, jeśli typem folderu jest \"{{foldertype}}\".",
"Changelog": "Historia zmian", "Changelog": "Historia zmian",
"Clean out after": "Opróżnij po", "Clean out after": "Opróżnij po",
"Cleaning Versions": "Czyszczenie wersji", "Cleaning Versions": "Czyszczenie wersji",
@@ -154,7 +156,7 @@
"Failed Items": "Elementy zakończone niepowodzeniem", "Failed Items": "Elementy zakończone niepowodzeniem",
"Failed to load file versions.": "Nie udało się załadować wersji plików.", "Failed to load file versions.": "Nie udało się załadować wersji plików.",
"Failed to load ignore patterns.": "Nie udało się załadować wzorców ignorowania.", "Failed to load ignore patterns.": "Nie udało się załadować wzorców ignorowania.",
"Failed to setup, retrying": "Nie udało się ustawić; ponawiam próbę", "Failed to set up, retrying": "Nie udało się ustawić; ponawiam próbę",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Błąd połączenia do serwerów IPv6 może wystąpić, gdy w ogóle nie ma połączenia po IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Błąd połączenia do serwerów IPv6 może wystąpić, gdy w ogóle nie ma połączenia po IPv6.",
"File Pull Order": "Kolejność pobierania plików", "File Pull Order": "Kolejność pobierania plików",
"File Versioning": "Wersjonowanie plików", "File Versioning": "Wersjonowanie plików",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Redes permitidas", "Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabética", "Alphabetic": "Alfabética",
"Altered by ignoring deletes.": "Alterado ignorando exclusões.", "Altered by ignoring deletes.": "Alterado ignorando exclusões.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre ativado quando o tipo de pasta for \"{{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.": "Um comando externo controla o controle de versão. Tem que remover o arquivo da pasta compartilhada. Se o caminho para o aplicativo contiver espaços, ele deve ser colocado entre aspas.", "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.": "Um comando externo controla o controle de versão. Tem que remover o arquivo da pasta compartilhada. Se o caminho para o aplicativo contiver espaços, ele deve ser colocado entre aspas.",
"Anonymous Usage Reporting": "Relatórios anônimos de uso", "Anonymous Usage Reporting": "Relatórios anônimos de uso",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anônimo de uso mudou. Gostaria de usar o formato novo?", "Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anônimo de uso mudou. Gostaria de usar o formato novo?",
@@ -52,6 +53,7 @@
"Body:": "Corpo:", "Body:": "Corpo:",
"Bugs": "Erros", "Bugs": "Erros",
"Cancel": "Cancelar", "Cancel": "Cancelar",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Não pode ser ativado se o tipo de pasta for \"{{foldertype}}\".",
"Changelog": "Registro de alterações", "Changelog": "Registro de alterações",
"Clean out after": "Limpar depois de", "Clean out after": "Limpar depois de",
"Cleaning Versions": "Limpando Versões", "Cleaning Versions": "Limpando Versões",
@@ -154,7 +156,7 @@
"Failed Items": "Itens com falha", "Failed Items": "Itens com falha",
"Failed to load file versions.": "Falha ao carregar versões do arquivo.", "Failed to load file versions.": "Falha ao carregar versões do arquivo.",
"Failed to load ignore patterns.": "Falha ao carregar os padrões para ignorar.", "Failed to load ignore patterns.": "Falha ao carregar os padrões para ignorar.",
"Failed to setup, retrying": "Não foi possível configurar, tentando novamente", "Failed to set up, retrying": "Não foi possível configurar, tentando novamente",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Falhas na conexão a servidores IPv6 são esperadas caso não haja conectividade IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Falhas na conexão a servidores IPv6 são esperadas caso não haja conectividade IPv6.",
"File Pull Order": "Ordem de retirada do arquivo", "File Pull Order": "Ordem de retirada do arquivo",
"File Versioning": "Versionamento de arquivos", "File Versioning": "Versionamento de arquivos",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Redes permitidas", "Allowed Networks": "Redes permitidas",
"Alphabetic": "Alfabética", "Alphabetic": "Alfabética",
"Altered by ignoring deletes.": "Alterada por terem sido ignoradas as eliminações.", "Altered by ignoring deletes.": "Alterada por terem sido ignoradas as eliminações.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Sempre ligado quando o tipo de pasta é \"{{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.": "Um comando externo gere as versões. Esse comando tem que remover o ficheiro da pasta partilhada. Se o caminho para a aplicação contiver espaços, então terá de o escrever entre aspas.", "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.": "Um comando externo gere as versões. Esse comando tem que remover o ficheiro da pasta partilhada. Se o caminho para a aplicação contiver espaços, então terá de o escrever entre aspas.",
"Anonymous Usage Reporting": "Enviar relatórios anónimos de utilização", "Anonymous Usage Reporting": "Enviar relatórios anónimos de utilização",
"Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anónimo de utilização foi alterado. Gostaria de mudar para o novo formato?", "Anonymous usage report format has changed. Would you like to move to the new format?": "O formato do relatório anónimo de utilização foi alterado. Gostaria de mudar para o novo formato?",
@@ -52,6 +53,7 @@
"Body:": "Corpo:", "Body:": "Corpo:",
"Bugs": "Erros", "Bugs": "Erros",
"Cancel": "Cancelar", "Cancel": "Cancelar",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Não pode ser habilitado quando o tipo de pasta é \"{{foldertype}}\".",
"Changelog": "Registo de alterações", "Changelog": "Registo de alterações",
"Clean out after": "Esvaziar ao fim de", "Clean out after": "Esvaziar ao fim de",
"Cleaning Versions": "Limpando versões", "Cleaning Versions": "Limpando versões",
@@ -154,7 +156,7 @@
"Failed Items": "Itens que falharam", "Failed Items": "Itens que falharam",
"Failed to load file versions.": "Falha ao carregar as versões do ficheiro.", "Failed to load file versions.": "Falha ao carregar as versões do ficheiro.",
"Failed to load ignore patterns.": "Falha ao carregar os padrões de exclusão.", "Failed to load ignore patterns.": "Falha ao carregar os padrões de exclusão.",
"Failed to setup, retrying": "A preparação falhou, a tentar novamente", "Failed to set up, retrying": "A preparação falhou, a tentar novamente",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "São esperadas falhas na ligação a servidores IPv6 se não existir conectividade IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "São esperadas falhas na ligação a servidores IPv6 se não existir conectividade IPv6.",
"File Pull Order": "Ordem de obtenção de ficheiros", "File Pull Order": "Ordem de obtenção de ficheiros",
"File Versioning": "Gestão de versões de ficheiros", "File Versioning": "Gestão de versões de ficheiros",
+1 -1
View File
@@ -26,6 +26,7 @@
"Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?", "Allow Anonymous Usage Reporting?": "Permiteţi raportarea anonimă de folosire a aplicaţiei?",
"Allowed Networks": "Rețele permise", "Allowed Networks": "Rețele permise",
"Alphabetic": "Alfabetic", "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.", "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 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?", "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?",
@@ -143,7 +144,6 @@
"Error": "Eroare", "Error": "Eroare",
"External File Versioning": "Administrare externă a versiunilor documentului", "External File Versioning": "Administrare externă a versiunilor documentului",
"Failed Items": "Failed Items", "Failed Items": "Failed Items",
"Failed to setup, retrying": "Failed to setup, retrying",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.",
"File Pull Order": "File Pull Order", "File Pull Order": "File Pull Order",
"File Versioning": "Versiune Fișier", "File Versioning": "Versiune Fișier",
+5 -3
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Разрешённые сети", "Allowed Networks": "Разрешённые сети",
"Alphabetic": "По алфавиту", "Alphabetic": "По алфавиту",
"Altered by ignoring deletes.": "Изменено, игнорируя удаления.", "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.": "Для версионирования используется внешняя программа. Ей нужно удалить файл из общей папки. Если путь к приложению содержит пробелы, его нужно взять в кавычки.", "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 Reporting": "Анонимный отчет об использовании",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимных отчётов изменился. Хотите переключиться на новый формат?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимных отчётов изменился. Хотите переключиться на новый формат?",
@@ -52,6 +53,7 @@
"Body:": "Тело:", "Body:": "Тело:",
"Bugs": "Ошибки", "Bugs": "Ошибки",
"Cancel": "Отмена", "Cancel": "Отмена",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Не может быть включено для папок с типом «{{foldertype}}».",
"Changelog": "Журнал изменений", "Changelog": "Журнал изменений",
"Clean out after": "Очистить после", "Clean out after": "Очистить после",
"Cleaning Versions": "Очистка версий", "Cleaning Versions": "Очистка версий",
@@ -154,7 +156,7 @@
"Failed Items": "Сбойные объекты", "Failed Items": "Сбойные объекты",
"Failed to load file versions.": "Не удалось загрузить версии файлов.", "Failed to load file versions.": "Не удалось загрузить версии файлов.",
"Failed to load ignore patterns.": "Не удалось загрузить шаблоны игнорирования.", "Failed to load ignore patterns.": "Не удалось загрузить шаблоны игнорирования.",
"Failed to setup, retrying": "Не удалось настроить, пробуем ещё", "Failed to set up, retrying": "Не удалось настроить, пробуем ещё",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Если нет IPv6-соединений, при подключении к IPv6-серверам произойдёт ошибка.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Если нет IPv6-соединений, при подключении к IPv6-серверам произойдёт ошибка.",
"File Pull Order": "Порядок получения файлов", "File Pull Order": "Порядок получения файлов",
"File Versioning": "Управление версиями", "File Versioning": "Управление версиями",
@@ -171,8 +173,8 @@
"Folder Path": "Путь к папке", "Folder Path": "Путь к папке",
"Folder Status": "Статус папки", "Folder Status": "Статус папки",
"Folder Type": "Тип папки", "Folder Type": "Тип папки",
"Folder type \"{%receiveEncrypted%}\" can only be set when adding a new folder.": "Тип папки «{{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}}» не может быть изменён после добавления. Вам необходимо убрать папку, удалить или дешифровать данные на диске, а затем снова добавить папку.", "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": "Папки", "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.": "Для следующих папок произошла ошибка при запуске отслеживания изменений. Попытки будут повторяться раз в минуту, и ошибки скоро могут быть устранены. Если этого не произойдёт, попробуйте разобраться в причинах и попросите поддержки, если у вас не получится.", "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": "Вечно", "Forever": "Вечно",
+1 -1
View File
@@ -143,7 +143,7 @@
"Failed Items": "අසාර්ථක අයිතම", "Failed Items": "අසාර්ථක අයිතම",
"Failed to load file versions.": "ගොනු අනුවාද පූරණය කිරීමට අසමත් විය.", "Failed to load file versions.": "ගොනු අනුවාද පූරණය කිරීමට අසමත් විය.",
"Failed to load ignore patterns.": "නොසලකා හැරීමේ රටා පූරණය කිරීමට අසමත් විය.", "Failed to load ignore patterns.": "නොසලකා හැරීමේ රටා පූරණය කිරීමට අසමත් විය.",
"Failed to setup, retrying": "පිහිටුවීමට අසමත් විය, උත්සාහ කරමින්", "Failed to set up, retrying": "පිහිටුවීමට අසමත් විය, උත්සාහ කරමින්",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 සම්බන්ධතාවක් නොමැති නම් IPv6 සේවාදායක වෙත සම්බන්ධ වීමට අසමත් වීම අපේක්ෂා කෙරේ.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 සම්බන්ධතාවක් නොමැති නම් IPv6 සේවාදායක වෙත සම්බන්ධ වීමට අසමත් වීම අපේක්ෂා කෙරේ.",
"File Pull Order": "ගොනු ඇදීමේ නියෝගය", "File Pull Order": "ගොනු ඇදීමේ නියෝගය",
"File Versioning": "ගොනු අනුවාදය", "File Versioning": "ගොනු අනුවාදය",
+1 -1
View File
@@ -153,7 +153,7 @@
"Failed Items": "Zlyhané položky", "Failed Items": "Zlyhané položky",
"Failed to load file versions.": "Nepodarilo sa načítať verzie súborov.", "Failed to load file versions.": "Nepodarilo sa načítať verzie súborov.",
"Failed to load ignore patterns.": "Nepodarilo sa načítať ignorované vzory.", "Failed to load ignore patterns.": "Nepodarilo sa načítať ignorované vzory.",
"Failed to setup, retrying": "Nepodarilo sa nastaviť, opakujem", "Failed to set up, retrying": "Nepodarilo sa nastaviť, opakujem",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Zlyhanie pripojenia k IPv6 serverom je očakávané ak neexistujú žiadne IPv6 pripojenia.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Zlyhanie pripojenia k IPv6 serverom je očakávané ak neexistujú žiadne IPv6 pripojenia.",
"File Pull Order": "Poradie sťahovania súborov", "File Pull Order": "Poradie sťahovania súborov",
"File Versioning": "Verzie súborov", "File Versioning": "Verzie súborov",
+1 -1
View File
@@ -131,7 +131,7 @@
"External File Versioning": "Zunanje beleženje različic datotek", "External File Versioning": "Zunanje beleženje različic datotek",
"Failed Items": "Neuspeli predmeti", "Failed Items": "Neuspeli predmeti",
"Failed to load ignore patterns.": "Prezrih vzorcev ni bilo mogoče naložiti.", "Failed to load ignore patterns.": "Prezrih vzorcev ni bilo mogoče naložiti.",
"Failed to setup, retrying": "Nastavitev ni uspela, ponovni poskus", "Failed to set up, retrying": "Nastavitev ni uspela, ponovni poskus",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Neuspeh povezav z IPv6 strežniki je pričakovan, če ni IPv6 povezljivost.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Neuspeh povezav z IPv6 strežniki je pričakovan, če ni IPv6 povezljivost.",
"File Pull Order": "Vrstni red prenosa datotek", "File Pull Order": "Vrstni red prenosa datotek",
"File Versioning": "Beleženje različic datotek", "File Versioning": "Beleženje različic datotek",
+36
View File
@@ -0,0 +1,36 @@
{
"A device with that ID is already added.": "Уређај са тим идентификатором је већ додат.",
"A negative number of days doesn't make sense.": "Негативан број дана нема смисла.",
"A new major version may not be compatible with previous versions.": "Нова верзија можда неће радити са претходним верзијама.",
"API Key": "АПИ кључ",
"About": "Информације",
"Action": "Радња",
"Actions": "Радње",
"Active filter rules": "Активна правила филтера",
"Add": "Додај",
"Add Device": "Додај уређај",
"Add Folder": "Додај фасциклу",
"Add Remote Device": "Додаај удаљени уређај",
"Add devices from the introducer to our device list, for mutually shared folders.": "Додај уређаје од иницијатора на нашу листу уређаја, за међусобно дељене фасцикле.",
"Add filter entry": "Додај ставку филтера",
"Add ignore patterns": "Додај правила за игнорисање",
"Add new folder?": "Додај нову фасциклу?",
"Additionally the full rescan interval will be increased (times 60, i.e. new default of 1h). You can also configure it manually for every folder later after choosing No.": "Додатно, интервал потпуног поновног скенирања ће бити повећан (60 пута, тј. нови подразумевани интервал од 1 сат). Такође можете ручно да га подесите за сваку фасциклу касније након што изаберете Не.",
"Address": "Адреса",
"Addresses": "Адресе",
"Advanced": "Напредно",
"Advanced Configuration": "Напредна конфигурација",
"All Data": "Сви подаци",
"All Time": "Све време",
"All folders shared with this device must be protected by a password, such that all sent data is unreadable without the given password.": "Све фасцикле које се деле са овим уређајем морају бити заштићене лозинком, тако да сви послати подаци не могу бити прочитани без дате лозинке.",
"Allow Anonymous Usage Reporting?": "Дозволити анонимно слање података о коришћењу?",
"Allowed Networks": "Дозвољене мреже",
"Alphabetic": "Абецедним редом",
"Altered by ignoring deletes.": "Промењено због игнорисања брисања.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Увек укључено када је тип фасцикле „{{foldertype}}\".",
"An external command handles the versioning. It has to remove the file from the shared folder. If the path to the application contains spaces, it should be quoted.": "Екстерна команда управља верзионирањем. Она мора да уклони фајл из дељене фасцикле. Ако путања до апликације садржи размаке, треба да буде под наводницима.",
"Anonymous Usage Reporting": "Анонимно слање података о употреби",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонимног слања података о коришћењу је промењен. Желите ли да пређете на нови формат?",
"Applied to LAN": "Важи за локалну мрежу",
"Apply": "Примени"
}
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Tillåtna nätverk", "Allowed Networks": "Tillåtna nätverk",
"Alphabetic": "Alfabetisk", "Alphabetic": "Alfabetisk",
"Altered by ignoring deletes.": "Ändrad genom att ignorera borttagningar.", "Altered by ignoring deletes.": "Ändrad genom att ignorera borttagningar.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Alltid på när mapptypen är \"{{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.": "Ett externt kommando hanterar versionen. Det måste ta bort filen från den delade mappen. Om sökvägen till applikationen innehåller mellanslag bör den citeras.", "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.": "Ett externt kommando hanterar versionen. Det måste ta bort filen från den delade mappen. Om sökvägen till applikationen innehåller mellanslag bör den citeras.",
"Anonymous Usage Reporting": "Anonym användarstatistiksrapportering", "Anonymous Usage Reporting": "Anonym användarstatistiksrapportering",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymt användningsrapportformat har ändrats. Vill du flytta till det nya formatet?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Anonymt användningsrapportformat har ändrats. Vill du flytta till det nya formatet?",
@@ -52,6 +53,7 @@
"Body:": "Meddelande:", "Body:": "Meddelande:",
"Bugs": "Felrapporter", "Bugs": "Felrapporter",
"Cancel": "Avbryt", "Cancel": "Avbryt",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Kan inte aktiveras när mapptypen är \"{{foldertype}}\".",
"Changelog": "Ändringslogg", "Changelog": "Ändringslogg",
"Clean out after": "Rensa efteråt", "Clean out after": "Rensa efteråt",
"Cleaning Versions": "Rensningsversioner", "Cleaning Versions": "Rensningsversioner",
@@ -154,7 +156,7 @@
"Failed Items": "Misslyckade objekt", "Failed Items": "Misslyckade objekt",
"Failed to load file versions.": "Det gick inte att läsa in filversioner.", "Failed to load file versions.": "Det gick inte att läsa in filversioner.",
"Failed to load ignore patterns.": "Det gick inte att läsa in ignoreringsmönster.", "Failed to load ignore patterns.": "Det gick inte att läsa in ignoreringsmönster.",
"Failed to setup, retrying": "Det gick inte att ställa in, försöker igen", "Failed to set up, retrying": "Det gick inte att ställa in, försöker igen",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Det går inte att ansluta till IPv6-servrar om det inte finns någon IPv6-anslutning.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "Det går inte att ansluta till IPv6-servrar om det inte finns någon IPv6-anslutning.",
"File Pull Order": "Filhämtningsprioritering", "File Pull Order": "Filhämtningsprioritering",
"File Versioning": "Filversionshantering", "File Versioning": "Filversionshantering",
+3 -1
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "İzin Verilen Ağlar", "Allowed Networks": "İzin Verilen Ağlar",
"Alphabetic": "Alfabetik", "Alphabetic": "Alfabetik",
"Altered by ignoring deletes.": "Silmeler yoksayılarak değiştirildi.", "Altered by ignoring deletes.": "Silmeler yoksayılarak değiştirildi.",
"Always turned on when the folder type is \"{%foldertype%}\".": "Klasör türü \"{{foldertype}}\" olduğunda her zaman açıktır.",
"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.": "Harici bir komut sürümlendirmeyi gerçekleştirir. Dosyayı paylaşılan klasörden kaldırmak zorundadır. Eğer uygulama yolu boşluklar içeriyorsa, tırnak içine alınmalıdır.", "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.": "Harici bir komut sürümlendirmeyi gerçekleştirir. Dosyayı paylaşılan klasörden kaldırmak zorundadır. Eğer uygulama yolu boşluklar içeriyorsa, tırnak içine alınmalıdır.",
"Anonymous Usage Reporting": "İsimsiz Kullanım Bildirme", "Anonymous Usage Reporting": "İsimsiz Kullanım Bildirme",
"Anonymous usage report format has changed. Would you like to move to the new format?": "İsimsiz kullanım raporu biçimi değişti. Yeni biçime geçmek ister misiniz?", "Anonymous usage report format has changed. Would you like to move to the new format?": "İsimsiz kullanım raporu biçimi değişti. Yeni biçime geçmek ister misiniz?",
@@ -52,6 +53,7 @@
"Body:": "Gövde:", "Body:": "Gövde:",
"Bugs": "Hatalar", "Bugs": "Hatalar",
"Cancel": "İptal", "Cancel": "İptal",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Klasör türü \"{{foldertype}}\" olduğunda etkinleştirilemez.",
"Changelog": "Değişiklik Günlüğü", "Changelog": "Değişiklik Günlüğü",
"Clean out after": "Şundan sonra temizle", "Clean out after": "Şundan sonra temizle",
"Cleaning Versions": "Sürümleri Temizleme", "Cleaning Versions": "Sürümleri Temizleme",
@@ -154,7 +156,7 @@
"Failed Items": "Başarısız Olan Öğeler", "Failed Items": "Başarısız Olan Öğeler",
"Failed to load file versions.": "Dosya sürümlerini yükleme başarısız.", "Failed to load file versions.": "Dosya sürümlerini yükleme başarısız.",
"Failed to load ignore patterns.": "Yoksayma şekillerini yükleme başarısız.", "Failed to load ignore patterns.": "Yoksayma şekillerini yükleme başarısız.",
"Failed to setup, retrying": "Ayarlama başarısız, yeniden deneniyor", "Failed to set up, retrying": "Ayarlama başarısız, yeniden deneniyor",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 bağlanabilirliği yoksa IPv6 sunucularına bağlanma hatası beklenmekte.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "IPv6 bağlanabilirliği yoksa IPv6 sunucularına bağlanma hatası beklenmekte.",
"File Pull Order": "Dosya Çekme Sırası", "File Pull Order": "Dosya Çekme Sırası",
"File Versioning": "Dosya Sürümlendirme", "File Versioning": "Dosya Sürümlendirme",
+4 -2
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "Дозволені мережі", "Allowed Networks": "Дозволені мережі",
"Alphabetic": "За абеткою", "Alphabetic": "За абеткою",
"Altered by ignoring deletes.": "Змінено, ігноруючи видалення.", "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.": "Зовнішня команда керує версіями. Вона повинна видалити файл із спільної теки. Якщо шлях до застосунку містить пробіли, його слід взяти в лапки.", "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 Reporting": "Анонімне звітування про використання",
"Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонімного звітування про використання змінився. Бажаєте перейти на новий формат?", "Anonymous usage report format has changed. Would you like to move to the new format?": "Формат анонімного звітування про використання змінився. Бажаєте перейти на новий формат?",
@@ -52,6 +53,7 @@
"Body:": "Повідомлення:", "Body:": "Повідомлення:",
"Bugs": "Помилки", "Bugs": "Помилки",
"Cancel": "Скасувати", "Cancel": "Скасувати",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "Неможливо ввімкнути, якщо тип теки «{{foldertype}}».",
"Changelog": "Журнал змін", "Changelog": "Журнал змін",
"Clean out after": "Очистити після", "Clean out after": "Очистити після",
"Cleaning Versions": "Очищення версій", "Cleaning Versions": "Очищення версій",
@@ -154,7 +156,7 @@
"Failed Items": "Невдалі", "Failed Items": "Невдалі",
"Failed to load file versions.": "Не вдалося завантажити версії файлів.", "Failed to load file versions.": "Не вдалося завантажити версії файлів.",
"Failed to load ignore patterns.": "Не вдалося завантажити шаблони ігнорування.", "Failed to load ignore patterns.": "Не вдалося завантажити шаблони ігнорування.",
"Failed to setup, retrying": "Не вдалося налаштувати, повторна спроба", "Failed to set up, retrying": "Не вдалося налаштувати, повторна спроба",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "За відсутності з'єднання IPv6 очікується неможливість під'єднання до серверів IPv6.", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "За відсутності з'єднання IPv6 очікується неможливість під'єднання до серверів IPv6.",
"File Pull Order": "Порядок витягнення файлів", "File Pull Order": "Порядок витягнення файлів",
"File Versioning": "Версіонування файлів", "File Versioning": "Версіонування файлів",
@@ -550,6 +552,6 @@
}, },
"unknown device": "невідомий пристрій", "unknown device": "невідомий пристрій",
"{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися папкою \"{{folder}}\".", "{%device%} wants to share folder \"{%folder%}\".": "{{device}} хоче поділитися папкою \"{{folder}}\".",
"{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хоче поділитися папкою \"{{folderLabel}}\" ({{folder}}).", "{%device%} wants to share folder \"{%folderlabel%}\" ({%folder%}).": "{{device}} хоче поділитися текою \"{{folderlabel}}\" ({{folder}}).",
"{%reintroducer%} might reintroduce this device.": "{{reintroducer}} може повторно порекомендувати цей пристрій." "{%reintroducer%} might reintroduce this device.": "{{reintroducer}} може повторно порекомендувати цей пристрій."
} }
+9 -7
View File
@@ -27,6 +27,7 @@
"Allowed Networks": "允许的网络", "Allowed Networks": "允许的网络",
"Alphabetic": "字母顺序", "Alphabetic": "字母顺序",
"Altered by ignoring deletes.": "通过忽略删除进行更改。", "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.": "外部命令处理版本控制。必须从共享文件夹中移除文件。如果应用程序的路径包含空格,应用半角引号括起来。", "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 Reporting": "匿名使用报告",
"Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用报告格式已更改。是否要切换到新格式?", "Anonymous usage report format has changed. Would you like to move to the new format?": "匿名使用报告格式已更改。是否要切换到新格式?",
@@ -52,6 +53,7 @@
"Body:": "正文:", "Body:": "正文:",
"Bugs": "问题反馈", "Bugs": "问题反馈",
"Cancel": "取消", "Cancel": "取消",
"Cannot be enabled when the folder type is \"{%foldertype%}\".": "当文件夹类型为“{{foldertype}}”时无法启用。",
"Changelog": "更新日志", "Changelog": "更新日志",
"Clean out after": "在该时间后清除", "Clean out after": "在该时间后清除",
"Cleaning Versions": "清理版本中", "Cleaning Versions": "清理版本中",
@@ -154,7 +156,7 @@
"Failed Items": "失败的项目", "Failed Items": "失败的项目",
"Failed to load file versions.": "加载文件版本失败。", "Failed to load file versions.": "加载文件版本失败。",
"Failed to load ignore patterns.": "加载忽略模式失败。", "Failed to load ignore patterns.": "加载忽略模式失败。",
"Failed to setup, retrying": "设置失败,正在重试", "Failed to set up, retrying": "设置失败,正在重试",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本机没有配置 IPv6,则无法连接 IPv6 服务器是正常的。", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本机没有配置 IPv6,则无法连接 IPv6 服务器是正常的。",
"File Pull Order": "文件拉取顺序", "File Pull Order": "文件拉取顺序",
"File Versioning": "文件版本控制", "File Versioning": "文件版本控制",
@@ -228,12 +230,12 @@
"Listener Failures": "监听程序失败", "Listener Failures": "监听程序失败",
"Listener Status": "监听程序状态", "Listener Status": "监听程序状态",
"Listeners": "监听程序", "Listeners": "监听程序",
"Loading data...": "正在载数据…", "Loading data...": "正在载数据…",
"Loading...": "正在载…", "Loading...": "正在载…",
"Local Additions": "本地添加", "Local Additions": "本地添加",
"Local Discovery": "本地发现", "Local Discovery": "本地发现",
"Local State": "本地状态", "Local State": "本地状态",
"Local State (Total)": "本地状态汇总", "Local State (Total)": "本地状态(总计)",
"Locally Changed Items": "本地更改的项目", "Locally Changed Items": "本地更改的项目",
"Log": "日志", "Log": "日志",
"Log File": "日志文件", "Log File": "日志文件",
@@ -481,11 +483,11 @@
"Untrusted": "不受信任", "Untrusted": "不受信任",
"Up to Date": "最新", "Up to Date": "最新",
"Updated {%file%}": "已更新 {{file}}", "Updated {%file%}": "已更新 {{file}}",
"Upgrade": "更新", "Upgrade": "升级",
"Upgrade To {%version%}": "升级至版本 {{version}}", "Upgrade To {%version%}": "升级 {{version}}",
"Upgrading": "升级中", "Upgrading": "升级中",
"Upload Rate": "上传速率", "Upload Rate": "上传速率",
"Uptime": "启动时间", "Uptime": "运行时间",
"Usage reporting is always enabled for candidate releases.": "发布候选版始终启用使用报告。", "Usage reporting is always enabled for candidate releases.": "发布候选版始终启用使用报告。",
"Use HTTPS for GUI": "使用 HTTPS 连接到 GUI", "Use HTTPS for GUI": "使用 HTTPS 连接到 GUI",
"Use notifications from the filesystem to detect changed items.": "使用来自文件系统的通知来检测更改的项目。", "Use notifications from the filesystem to detect changed items.": "使用来自文件系统的通知来检测更改的项目。",
+1 -1
View File
@@ -145,7 +145,7 @@
"Failed Items": "失敗的項目", "Failed Items": "失敗的項目",
"Failed to load file versions.": "無法加載文件版本。", "Failed to load file versions.": "無法加載文件版本。",
"Failed to load ignore patterns.": "無法加載忽略模式。", "Failed to load ignore patterns.": "無法加載忽略模式。",
"Failed to setup, retrying": "設置失敗,正在重試。", "Failed to set up, retrying": "設置失敗,正在重試。",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本機沒有配置IPv6,則無法連接IPv6服務器是正常的。", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "如果本機沒有配置IPv6,則無法連接IPv6服務器是正常的。",
"File Pull Order": "文件拉取順序", "File Pull Order": "文件拉取順序",
"File Versioning": "版本控制", "File Versioning": "版本控制",
+1 -1
View File
@@ -154,7 +154,7 @@
"Failed Items": "失敗的項目", "Failed Items": "失敗的項目",
"Failed to load file versions.": "無法載入檔案版本。", "Failed to load file versions.": "無法載入檔案版本。",
"Failed to load ignore patterns.": "無法載入忽略模式。", "Failed to load ignore patterns.": "無法載入忽略模式。",
"Failed to setup, retrying": "無法設定,正在重試", "Failed to set up, retrying": "無法設定,正在重試",
"Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "若沒有 IPv6 連線能力,則無法連接 IPv6 伺服器為正常現象。", "Failure to connect to IPv6 servers is expected if there is no IPv6 connectivity.": "若沒有 IPv6 連線能力,則無法連接 IPv6 伺服器為正常現象。",
"File Pull Order": "提取檔案的順序", "File Pull Order": "提取檔案的順序",
"File Versioning": "檔案版本控制", "File Versioning": "檔案版本控制",
+2 -2
View File
@@ -521,7 +521,7 @@
</span> </span>
<span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}"> <span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Periodic scanning at given interval and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}">
<span class="far fa-clock"></span>&nbsp;{{folder.rescanIntervalS | duration}}&ensp; <span class="far fa-clock"></span>&nbsp;{{folder.rescanIntervalS | duration}}&ensp;
<span class="fas fa-eye-slash"></span>&nbsp;<span translate>Failed to setup, retrying</span> <span class="fas fa-eye-slash"></span>&nbsp;<span translate>Failed to set up, retrying</span>
</span> </span>
</div> </div>
<div ng-if="folder.rescanIntervalS <= 0"> <div ng-if="folder.rescanIntervalS <= 0">
@@ -535,7 +535,7 @@
</span> </span>
<span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}"> <span ng-if="folder.fsWatcherEnabled && !folder.paused && folderStatus(folder) !== 'stopped' && model[folder.id].watchError" tooltip data-original-title="{{'Disabled periodic scanning and failed setting up watching for changes, retrying every 1m:' | translate}}<br/>{{model[folder.id].watchError}}">
<span class="far fa-clock"></span>&nbsp;<span translate>Disabled</span>&ensp; <span class="far fa-clock"></span>&nbsp;<span translate>Disabled</span>&ensp;
<span class="fas fa-eye-slash"></span>&nbsp;<span translate>Failed to setup, retrying</span> <span class="fas fa-eye-slash"></span>&nbsp;<span translate>Failed to set up, retrying</span>
</span> </span>
</div> </div>
</td> </td>
+48 -26
View File
@@ -30,7 +30,7 @@
<h4 class="text-center" translate>The Syncthing Authors</h4> <h4 class="text-center" translate>The Syncthing Authors</h4>
<div class="row"> <div class="row">
<div class="col-md-12" id="contributor-list"> <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, 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 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, 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, Ross Smith II, 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, Taylor Khan, Terrance, 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> </div>
</div> </div>
@@ -38,48 +38,70 @@ Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Tomasz Wilczyński, Al
<div id="about-includes" class="tab-pane"> <div id="about-includes" class="tab-pane">
<p translate>Syncthing includes the following software or portions thereof:</p> <p translate>Syncthing includes the following software or portions thereof:</p>
<ul class="list-unstyled two-columns" id="copyright-notices"> <ul class="list-unstyled two-columns" id="copyright-notices">
<li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright &copy; 2011-2016 Twitter, Inc.</li> <li><a href="https://getbootstrap.com/">Bootstrap</a>, Copyright &copy; 2011-2016 Twitter, Inc.</li>
<li><a href="https://angularjs.org/">AngularJS</a>, Copyright &copy; 2010-2014, 2016 Google, Inc.</li> <li><a href="https://angularjs.org/">AngularJS</a>, Copyright &copy; 2010-2014, 2016 Google, Inc.</li>
<li><a href="http://www.daterangepicker.com/">Date Range Picker</a>, Copyright &copy; 2012-2018 Dan Grossman.</li> <li><a href="https://www.daterangepicker.com/">Date Range Picker</a>, Copyright &copy; 2012-2018 Dan Grossman.</li>
<li><a href="https://github.com/mar10/fancytree">JQuery Fancytree Plugin</a>, Copyright &copy; 2008-2018 Martin Wendt.</li> <li><a href="https://github.com/mar10/fancytree">JQuery Fancytree Plugin</a>, Copyright &copy; 2008-2018 Martin Wendt.</li>
<li><a href="https://fontawesome.com/">Font Awesome</a>Copyright &copy; 2024 Fonticons, Inc.</li>
<li><a href="https://forkaweso.me/Fork-Awesome/">Fork Awesome</a>, Copyright &copy; 2018 Dave Gandy &amp; Fork Awesome.</li> <li><a href="https://forkaweso.me/Fork-Awesome/">Fork Awesome</a>, Copyright &copy; 2018 Dave Gandy &amp; Fork Awesome.</li>
<li><a href="http://jquery.com/">jQuery JavaScript Library</a>, Copyright &copy; jQuery Foundation and other contributors.</li> <li><a href="https://evanhahn.github.io/HumanizeDuration.js/">HumanDuration.js</a>, Copyright &copy; 2013-2024 Evan Hahn, portions copyright &copy; 2024 Ross Smith II.</li>
<li><a href="http://momentjs.com/">moment.js</a>, Copyright &copy; JS Foundation and other contributors.</li> <li><a href="https://jquery.com/">jQuery JavaScript Library</a>, Copyright &copy; jQuery Foundation and other contributors.</li>
<li><a href="https://leafletjs.com/">leaflet.js</a>, Copyright &copy; 2010-2025 Volodymyr Agafonkin, Copyright &copy; 2010-2011 CloudMade.</li>
<li><a href="https://momentjs.com/">moment.js</a>, Copyright &copy; JS Foundation and other contributors.</li>
<li><a href="https://golang.org/">The Go Programming Language</a>, Copyright &copy; 2009 The Go Authors.</li>
<li><a href="https://prometheus.io/">Prometheus</a>, Copyright &copy; 2012-2015 The Prometheus Authors.</li> <li><a href="https://prometheus.io/">Prometheus</a>, Copyright &copy; 2012-2015 The Prometheus Authors.</li>
<li><a href="https://github.com/AudriusButkevicius/go-nat-pmp">AudriusButkevicius/go-nat-pmp</a>, Copyright &copy; 2013 John Howard Palevich.</li>
<li><a href="https://github.com/AudriusButkevicius/recli">AudriusButkevicius/recli</a>, Copyright &copy; 2019 Audrius Butkevicius.</li> <li><a href="https://github.com/AudriusButkevicius/recli">AudriusButkevicius/recli</a>, Copyright &copy; 2019 Audrius Butkevicius.</li>
<li><a href="https://github.com/Azure/go-ntlmssp">Azure/go-ntlmssp</a>, Copyright &copy; 2016 Microsoft.</li>
<li><a href="https://github.com/alecthomas/kong">alecthomas/kong</a>, Copyright &copy; 2018 Alec Thomas.</li>
<li><a href="https://github.com/beorn7/perks">beorn7/perks</a>, Copyright &copy; 2013 Blake Mizerany.</li> <li><a href="https://github.com/beorn7/perks">beorn7/perks</a>, Copyright &copy; 2013 Blake Mizerany.</li>
<li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li> <li><a href="https://github.com/calmh/incontainer">calmh/incontainer</a>, Copyright &copy; 2022 calmh.</li>
<li><a href="https://github.com/calmh/du">calmh/du</a>, Public domain.</li>
<li><a href="https://github.com/calmh/xdr">calmh/xdr</a>, Copyright &copy; 2014 Jakob Borg.</li> <li><a href="https://github.com/calmh/xdr">calmh/xdr</a>, Copyright &copy; 2014 Jakob Borg.</li>
<li><a href="https://github.com/ccding/go-stun">ccding/go-stun</a>, Copyright &copy; 2016 Cong Ding.</li>
<li><a href="https://github.com/cespare/xxhash/v2">cespare/xxhash/v2</a>, Copyright &copy; 2016 Caleb Spare.</li>
<li><a href="https://github.com/chmduquesne/rollinghash">chmduquesne/rollinghash</a>, Copyright &copy; 2015 Christophe-Marie Duquesne.</li> <li><a href="https://github.com/chmduquesne/rollinghash">chmduquesne/rollinghash</a>, Copyright &copy; 2015 Christophe-Marie Duquesne.</li>
<li><a href="https://github.com/d4l3k/messagediff">d4l3k/messagediff</a>, Copyright &copy; 2015 Tristan Rice.</li> <li><a href="https://github.com/cpuguy83/go-md2man/v2">cpuguy83/go-md2man/v2</a>, Copyright &copy; 2014 Brian Goff.</li>
<li><a href="https://github.com/davecgh/go-spew">davecgh/go-spew</a>, Copyright &copy; 2012-2016 Dave Collins.</li>
<li><a href="https://github.com/go-asn1-ber/asn1-ber">go-asn1-ber/asn1-ber</a>, Copyright &copy; 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
<li><a href="https://github.com/go-ldap/ldap">go-ldap/ldap</a>, Copyright &copy; 2011-2015 Michael Mitton (mmitton@gmail.com).</li>
<li><a href="https://github.com/uber-go/automaxprocs">go.uber.org/automaxprocs</a>, Copyright &copy; 2017 Uber Technologies, Inc.</li>
<li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright &copy; 2016 Sergey Kamardin.</li> <li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright &copy; 2016 Sergey Kamardin.</li>
<li><a href="https://github.com/golang/groupcache">golang/groupcache</a>, Copyright &copy; 2013 Google Inc.</li> <li><a href="https://github.com/gofrs/flock">gofrs/flock</a>, Copyright &copy; 2018-2025, The Gofrs.</li>
<li><a href="https://github.com/golang/protobuf">golang/protobuf</a>, Copyright &copy; 2010 The Go Authors.</li>
<li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright &copy; 2011 The Snappy-Go Authors.</li> <li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright &copy; 2011 The Snappy-Go Authors.</li>
<li><a href="https://github.com/protocolbuffers/protobuf-go">google.golang.org/protobuf</a>, Copyright &copy; 2018 The Go Authors.</li>
<li><a href="https://github.com/google/uuid">google/uuid</a>, Copyright &copy; 2009,2014 Google Inc.</li>
<li><a href="https://gopkg.in/yaml.v3">gopkg.in/yaml.v3</a>, Copyright &copy; 2025, the gopkg.in/yaml.v3 authors.</li>
<li><a href="https://github.com/greatroar/blobloom">greatroar/blobloom</a>, Copyright &copy; 2020-2024 the Blobloom authors.</li>
<li><a href="https://github.com/hashicorp/errwrap">hashicorp/errwrap</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/hashicorp/go-multierror">hashicorp/go-multierror</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/hashicorp/golang-lru">hashicorp/golang-lru</a>, Copyright &copy; 2014 HashiCorp, Inc.</li>
<li><a href="https://github.com/jackpal/gateway">jackpal/gateway</a>, Copyright &copy; 2010 Jack Palevich.</li> <li><a href="https://github.com/jackpal/gateway">jackpal/gateway</a>, Copyright &copy; 2010 Jack Palevich.</li>
<li><a href="https://github.com/jmoiron/sqlx">jmoiron/sqlx</a>, Copyright &copy; 2013 Jason Moiron.</li> <li><a href="https://github.com/jackpal/go-nat-pmp">jackpal/go-nat-pmp</a>, Copyright 2013 John Howard Palevich.</li>
<li><a href="https://github.com/julienschmidt/httprouter">julienschmidt/httprouter</a>, Copyright &copy; 2013, Julien Schmidt.</li>
<li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li> <li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li>
<li><a href="https://github.com/mattn/go-isatty">mattn/go-isatty</a>, Copyright &copy; Yasuhiro MATSUMOTO.</li> <li><a href="https://github.com/miscreant/miscreant.go">miscreant/miscreant.go</a>, Copyright &copy; 2017-2019 The Miscreant Developers.</li>
<li><a href="https://github.com/mattn/go-sqlite3">mattn/go-sqlite3</a>, Copyright &copy; 2014 Yasuhiro Matsumoto</li> <li><a href="https://github.com/munnerz/goautoneg">munnerz/goautoneg</a>, Copyright &copy; 2011, Open Knowledge Foundation Ltd.</li>
<li><a href="https://github.com/matttproud/golang_protobuf_extensions">matttproud/golang_protobuf_extensions</a>, Copyright &copy; 2012 Matt T. Proud.</li> <li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li>
<li><a href="https://modernc.org/sqlite">modernc.org/sqlite</a>, Copyright &copy; 2017 The Sqlite Authors</li>
<li><a href="https://github.com/oschwald/geoip2-golang">oschwald/geoip2-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
<li><a href="https://github.com/oschwald/maxminddb-golang">oschwald/maxminddb-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
<li><a href="https://github.com/petermattis/goid">petermattis/goid</a>, Copyright &copy; 2015-2016 Peter Mattis.</li>
<li><a href="https://github.com/pkg/errors">pkg/errors</a>, Copyright &copy; 2015, Dave Cheney.</li> <li><a href="https://github.com/pkg/errors">pkg/errors</a>, Copyright &copy; 2015, Dave Cheney.</li>
<li><a href="https://github.com/pmezard/go-difflib">pmezard/go-difflib</a>, Copyright &copy; 2013, Patrick Mezard.</li>
<li><a href="https://github.com/posener/complete">posener/complete</a>, Copyright &copy; 2017 Eyal Posener.</li>
<li><a href="https://github.com/prometheus/client_golang">prometheus/client_golang</a>, Copyright 2012-2015 The Prometheus Authors.</li>
<li><a href="https://github.com/prometheus/client_model">prometheus/client_model</a>, Copyright &copy; 2025, the prometheus/client_model authors.</li>
<li><a href="https://github.com/prometheus/common">prometheus/common</a>, Copyright &copy; 2025, the prometheus/common authors.</li>
<li><a href="https://github.com/prometheus/procfs">prometheus/procfs</a>, Copyright &copy; 2025, the prometheus/procfs authors.</li>
<li><a href="https://github.com/quic-go/quic-go">quic-go/quic-go</a>, Copyright &copy; 2016 the quic-go authors & Google, Inc.</li>
<li><a href="https://github.com/rcrowley/go-metrics">rcrowley/go-metrics</a>, Copyright &copy; 2012 Richard Crowley.</li> <li><a href="https://github.com/rcrowley/go-metrics">rcrowley/go-metrics</a>, Copyright &copy; 2012 Richard Crowley.</li>
<li><a href="https://github.com/sasha-s/go-deadlock">sasha-s/go-deadlock</a>, Copyright &copy; 2016 sasha-s.</li> <li><a href="https://github.com/riywo/loginshell">riywo/loginshell</a>, Copyright &copy; 2019 Ryosuke IWANAGA.</li>
<li><a href="https://github.com/syncthing/notify">syncthing/notify</a>, Copyright &copy; 2014-2015 The Notify Authors.</li> <li><a href="https://github.com/russross/blackfriday/v2">russross/blackfriday/v2</a>, Copyright &copy; 2011 Russ Ross.</li>
<li><a href="https://github.com/shirou/gopsutil">shirou/gopsutil</a>, Copyright &copy; 2014, WAKAYAMA Shirou.</li>
<li><a href="https://github.com/stretchr/objx">stretchr/objx</a>, Copyright &copy; 2014 Stretchr, Inc.</li>
<li><a href="https://github.com/stretchr/testify">stretchr/testify</a>, Copyright &copy; 2012-2020 Mat Ryer, Tyler Bunnell and contributors.</li>
<li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright &copy; 2012 Suryandaru Triandana.</li> <li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright &copy; 2012 Suryandaru Triandana.</li>
<li><a href="https://github.com/thejerf/suture">thejerf/suture</a>, Copyright &copy; 2014-2015 Barracuda Networks, Inc.</li> <li><a href="https://github.com/thejerf/suture">thejerf/suture</a>, Copyright &copy; 2014-2015 Barracuda Networks, Inc.</li>
<li><a href="https://github.com/urfave/cli">urfave/cli</a>, Copyright &copy; 2016 Jeremy Saenz &amp; Contributors.</li> <li><a href="https://github.com/tklauser/go-sysconf">tklauser/go-sysconf</a>, Copyright &copy; 2018-2022, Tobias Klauser.</li>
<li><a href="https://github.com/tklauser/numcpus">tklauser/numcpus</a>, Copyright &copy; 2018-2024 Tobias Klauser.</li>
<li><a href="https://github.com/urfave/cli">urfave/cli</a>, Copyright &copy; 2016 Jeremy Saenz & Contributors.</li>
<li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright &copy; 2010-2011 The Go Authors.</li> <li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright &copy; 2010-2011 The Go Authors.</li>
<li><a href="https://gopkg.in/asn1-ber.v1">gopkg.in/asn1-ber.v1</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-asn1-ber Authors.</li> <li><a href="https://github.com/willabides/kongplete">willabides/kongplete</a>, Copyright &copy; 2020 WillAbides.</li>
<li><a href="https://gopkg.in/ldap.v2">gopkg.in/ldap.v2</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-ldap Authors.</li>
<li><a href="https://golang.org">The Go Programming Language</a>, Copyright &copy; 2009 The Go Authors.</li>
<li>Font Awesome by Dave Gandy - <a href="http://fontawesome.io/">http://fontawesome.io</a></li>
</ul> </ul>
</div> </div>
@@ -16,6 +16,14 @@ angular.module('syncthing.core')
}, },
link: function (scope, element, attrs) { 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 // before modal show animation
$(element).on('show.bs.modal', function () { $(element).on('show.bs.modal', function () {
@@ -1165,7 +1165,7 @@ angular.module('syncthing.core')
} }
// Disconnected // Disconnected
if (!unused && $scope.deviceStats[deviceCfg.deviceID] && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays && $scope.deviceStats[deviceCfg.deviceID].lastSeenDays >= 7) { if (!unused && $scope.deviceStats[deviceCfg.deviceID] && (!$scope.deviceStats[deviceCfg.deviceID].lastSeenDays || $scope.deviceStats[deviceCfg.deviceID].lastSeenDays >= 7)) {
return status + 'disconnected-inactive'; return status + 'disconnected-inactive';
} else { } else {
return status + 'disconnected'; return status + 'disconnected';
+32 -20
View File
@@ -1,27 +1,39 @@
angular.module('syncthing.core') angular.module('syncthing.core')
.filter('uncamel', function () { .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) { return function (input) {
input = input.replace(/(.)([A-Z][a-z]+)/g, '$1 $2').replace(/([a-z0-9])([A-Z])/g, '$1 $2'); if (!input || typeof input !== 'string') return '';
var parts = input.split(' '); const placeholders = {};
var lastPart = parts.splice(-1)[0]; 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) { switch (lastPart) {
case "S": case 'S': parts.push('(seconds)'); break;
parts.push('(seconds)'); case 'M': parts.push('(minutes)'); break;
break; case 'H': parts.push('(hours)'); break;
case "M": case 'Ms': parts.push('(milliseconds)'); break;
parts.push('(minutes)'); default: parts.push(lastPart); break;
break;
case "H":
parts.push('(hours)');
break;
case "Ms":
parts.push('(milliseconds)');
break;
default:
parts.push(lastPart);
break;
} }
input = parts.join(' '); parts = parts.map(part => {
return input.charAt(0).toUpperCase() + input.slice(1); 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();
}; };
}); });
@@ -5,18 +5,12 @@ angular.module('syncthing.core')
link: function (scope, elm, attrs, ctrl) { link: function (scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function (viewValue) { ctrl.$parsers.unshift(function (viewValue) {
$http.get(urlbase + '/svc/deviceid?id=' + viewValue).success(function (resp) { $http.get(urlbase + '/svc/deviceid?id=' + viewValue).success(function (resp) {
if (resp.error) { let isValid = !resp.error;
ctrl.$setValidity('validDeviceid', false); let isUnique = !isValid || !scope.devices.hasOwnProperty(resp.id);
} else {
ctrl.$setValidity('validDeviceid', true); ctrl.$setValidity('validDeviceid', isValid);
} ctrl.$setValidity('unique', isUnique);
}); });
//Prevents user from adding a duplicate ID
if (scope.devices.hasOwnProperty(viewValue)) {
ctrl.$setValidity('unique', false);
} else {
ctrl.$setValidity('unique', true);
}
return viewValue; return viewValue;
}); });
} }
+80
View File
@@ -0,0 +1,80 @@
// 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 azureblob
import (
"context"
"io"
"time"
stblob "github.com/syncthing/syncthing/internal/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/blockblob"
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
)
var _ stblob.Store = (*BlobStore)(nil)
type BlobStore struct {
client *azblob.Client
container string
}
func NewBlobStore(accountName, accountKey, containerName string) (*BlobStore, error) {
credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
return nil, err
}
url := "https://" + accountName + ".blob.core.windows.net/"
sc, err := azblob.NewClientWithSharedKeyCredential(url, credential, &azblob.ClientOptions{})
if err != nil {
return nil, err
}
// This errors when the container already exists, which we ignore.
_, _ = sc.CreateContainer(context.Background(), containerName, &container.CreateOptions{})
return &BlobStore{
client: sc,
container: containerName,
}, nil
}
func (a *BlobStore) Upload(ctx context.Context, key string, data io.Reader) error {
_, err := a.client.UploadStream(ctx, a.container, key, data, &blockblob.UploadStreamOptions{})
return err
}
func (a *BlobStore) Download(ctx context.Context, key string, w stblob.Writer) error {
resp, err := a.client.DownloadStream(ctx, a.container, key, &blob.DownloadStreamOptions{})
if err != nil {
return err
}
defer resp.Body.Close()
_, err = io.Copy(w, resp.Body)
return err
}
func (a *BlobStore) LatestKey(ctx context.Context) (string, error) {
opts := &azblob.ListBlobsFlatOptions{}
pager := a.client.NewListBlobsFlatPager(a.container, opts)
var latest string
var lastModified time.Time
for pager.More() {
page, err := pager.NextPage(ctx)
if err != nil {
return "", err
}
for _, blob := range page.Segment.BlobItems {
if latest == "" || blob.Properties.LastModified.After(lastModified) {
latest = *blob.Name
lastModified = *blob.Properties.LastModified
}
}
}
return latest, nil
}
+23
View File
@@ -0,0 +1,23 @@
// 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 blob
import (
"context"
"io"
)
type Store interface {
Upload(ctx context.Context, key string, r io.Reader) error
Download(ctx context.Context, key string, w Writer) error
LatestKey(ctx context.Context) (string, error)
}
type Writer interface {
io.Writer
io.WriterAt
}
+34 -29
View File
@@ -7,6 +7,7 @@
package s3 package s3
import ( import (
"context"
"io" "io"
"time" "time"
@@ -15,8 +16,11 @@ import (
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/syncthing/syncthing/internal/blob"
) )
var _ blob.Store = (*Session)(nil)
type Session struct { type Session struct {
bucket string bucket string
s3sess *session.Session s3sess *session.Session
@@ -26,9 +30,10 @@ type Object = s3.Object
func NewSession(endpoint, region, bucket, accessKeyID, secretKey string) (*Session, error) { func NewSession(endpoint, region, bucket, accessKeyID, secretKey string) (*Session, error) {
sess, err := session.NewSession(&aws.Config{ sess, err := session.NewSession(&aws.Config{
Region: aws.String(region), Region: aws.String(region),
Endpoint: aws.String(endpoint), Endpoint: aws.String(endpoint),
Credentials: credentials.NewStaticCredentials(accessKeyID, secretKey, ""), Credentials: credentials.NewStaticCredentials(accessKeyID, secretKey, ""),
S3ForcePathStyle: aws.Bool(true),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@@ -39,7 +44,7 @@ func NewSession(endpoint, region, bucket, accessKeyID, secretKey string) (*Sessi
}, nil }, nil
} }
func (s *Session) Upload(r io.Reader, key string) error { func (s *Session) Upload(_ context.Context, key string, r io.Reader) error {
uploader := s3manager.NewUploader(s.s3sess) uploader := s3manager.NewUploader(s.s3sess)
_, err := uploader.Upload(&s3manager.UploadInput{ _, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(s.bucket), Bucket: aws.String(s.bucket),
@@ -49,7 +54,31 @@ func (s *Session) Upload(r io.Reader, key string) error {
return err return err
} }
func (s *Session) List(fn func(*Object) bool) error { func (s *Session) Download(_ context.Context, key string, w blob.Writer) error {
downloader := s3manager.NewDownloader(s.s3sess)
_, err := downloader.Download(w, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
})
return err
}
func (s *Session) LatestKey(_ context.Context) (string, error) {
var latestKey string
var lastModified time.Time
if err := s.list(func(obj *Object) bool {
if latestKey == "" || obj.LastModified.After(lastModified) {
latestKey = *obj.Key
lastModified = *obj.LastModified
}
return true
}); err != nil {
return "", err
}
return latestKey, nil
}
func (s *Session) list(fn func(*Object) bool) error {
svc := s3.New(s.s3sess) svc := s3.New(s.s3sess)
opts := &s3.ListObjectsV2Input{ opts := &s3.ListObjectsV2Input{
@@ -75,27 +104,3 @@ func (s *Session) List(fn func(*Object) bool) error {
return nil return nil
} }
func (s *Session) LatestKey() (string, error) {
var latestKey string
var lastModified time.Time
if err := s.List(func(obj *Object) bool {
if latestKey == "" || obj.LastModified.After(lastModified) {
latestKey = *obj.Key
lastModified = *obj.LastModified
}
return true
}); err != nil {
return "", err
}
return latestKey, nil
}
func (s *Session) Download(w io.WriterAt, key string) error {
downloader := s3manager.NewDownloader(s.s3sess)
_, err := downloader.Download(w, &s3.GetObjectInput{
Bucket: aws.String(s.bucket),
Key: aws.String(key),
})
return err
}
-73
View File
@@ -1,73 +0,0 @@
// Copyright (C) 2014 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 db
import (
"fmt"
"strings"
"github.com/syncthing/syncthing/lib/protocol"
)
type Counts struct {
Files int
Directories int
Symlinks int
Deleted int
Bytes int64
Sequence int64 // zero for the global state
DeviceID protocol.DeviceID // device ID for remote devices, or special values for local/global
LocalFlags uint32 // the local flag for this count bucket
}
func (c Counts) Add(other Counts) Counts {
return Counts{
Files: c.Files + other.Files,
Directories: c.Directories + other.Directories,
Symlinks: c.Symlinks + other.Symlinks,
Deleted: c.Deleted + other.Deleted,
Bytes: c.Bytes + other.Bytes,
Sequence: c.Sequence + other.Sequence,
DeviceID: protocol.EmptyDeviceID,
LocalFlags: c.LocalFlags | other.LocalFlags,
}
}
func (c Counts) TotalItems() int {
return c.Files + c.Directories + c.Symlinks + c.Deleted
}
func (c Counts) String() string {
var flags strings.Builder
if c.LocalFlags&protocol.FlagLocalNeeded != 0 {
flags.WriteString("Need")
}
if c.LocalFlags&protocol.FlagLocalIgnored != 0 {
flags.WriteString("Ignored")
}
if c.LocalFlags&protocol.FlagLocalMustRescan != 0 {
flags.WriteString("Rescan")
}
if c.LocalFlags&protocol.FlagLocalReceiveOnly != 0 {
flags.WriteString("Recvonly")
}
if c.LocalFlags&protocol.FlagLocalUnsupported != 0 {
flags.WriteString("Unsupported")
}
if c.LocalFlags != 0 {
flags.WriteString(fmt.Sprintf("(%x)", c.LocalFlags))
}
if flags.Len() == 0 {
flags.WriteString("---")
}
return fmt.Sprintf("{Device:%v, Files:%d, Dirs:%d, Symlinks:%d, Del:%d, Bytes:%d, Seq:%d, Flags:%s}", c.DeviceID, c.Files, c.Directories, c.Symlinks, c.Deleted, c.Bytes, c.Sequence, flags.String())
}
// Equal compares the numbers only, not sequence/dev/flags.
func (c Counts) Equal(o Counts) bool {
return c.Files == o.Files && c.Directories == o.Directories && c.Symlinks == o.Symlinks && c.Deleted == o.Deleted && c.Bytes == o.Bytes
}
-123
View File
@@ -1,123 +0,0 @@
// 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 db // import "github.com/syncthing/syncthing/internal/db/sqlite"
import (
"iter"
"time"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
"github.com/thejerf/suture/v4"
)
type DB interface {
Service(maintenanceInterval time.Duration) suture.Service
// Basics
Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error
Close() error
// Single files
GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error)
GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error)
GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error)
// File iterators
//
// n.b. there is a slight inconsistency in the return types where some
// return a FileInfo iterator and some a FileMetadata iterator. The
// latter is more lightweight, and the discrepancy depends on how the
// functions tend to be used. We can introduce more variations as
// required.
AllGlobalFiles(folder string) (iter.Seq[FileMetadata], func() error)
AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[FileMetadata], func() error)
AllLocalBlocksWithHash(hash []byte) (iter.Seq[BlockMapEntry], func() error)
AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error)
AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error)
AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error)
AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[FileMetadata], func() error)
AllLocalFilesWithBlocksHashAnyFolder(h []byte) (iter.Seq2[string, FileMetadata], func() error)
AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error)
// Cleanup
DropAllFiles(folder string, device protocol.DeviceID) error
DropDevice(device protocol.DeviceID) error
DropFilesNamed(folder string, device protocol.DeviceID, names []string) error
DropFolder(folder string) error
// Various metadata
GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error)
ListFolders() ([]string, error)
ListDevicesForFolder(folder string) ([]protocol.DeviceID, error)
RemoteSequences(folder string) (map[protocol.DeviceID]int64, error)
// Counts
CountGlobal(folder string) (Counts, error)
CountLocal(folder string, device protocol.DeviceID) (Counts, error)
CountNeed(folder string, device protocol.DeviceID) (Counts, error)
CountReceiveOnlyChanged(folder string) (Counts, error)
// Index IDs
DropAllIndexIDs() error
GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error)
SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error
// MtimeFS
DeleteMtime(folder, name string) error
GetMtime(folder, name string) (ondisk, virtual time.Time)
PutMtime(folder, name string, ondisk, virtual time.Time) error
KV
}
// Generic KV store
type KV interface {
GetKV(key string) ([]byte, error)
PutKV(key string, val []byte) error
DeleteKV(key string) error
PrefixKV(prefix string) (iter.Seq[KeyValue], func() error)
}
type BlockMapEntry struct {
BlocklistHash []byte
Offset int64
BlockIndex int
Size int
}
type KeyValue struct {
Key string
Value []byte
}
type FileMetadata struct {
Name string
Sequence int64
ModNanos int64
Size int64
LocalFlags int64
Type protocol.FileInfoType
Deleted bool
Invalid bool
}
func (f *FileMetadata) ModTime() time.Time {
return time.Unix(0, f.ModNanos)
}
func (f *FileMetadata) IsReceiveOnlyChanged() bool {
return f.LocalFlags&protocol.FlagLocalReceiveOnly != 0
}
func (f *FileMetadata) IsDirectory() bool {
return f.Type == protocol.FileInfoTypeDirectory
}
func (f *FileMetadata) ShouldConflict() bool {
return f.LocalFlags&protocol.LocalConflictFlags != 0
}
-229
View File
@@ -1,229 +0,0 @@
// 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 db
import (
"iter"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/syncthing/syncthing/lib/config"
"github.com/syncthing/syncthing/lib/protocol"
)
var (
metricCurrentOperations = promauto.NewGaugeVec(prometheus.GaugeOpts{
Namespace: "syncthing",
Subsystem: "db",
Name: "operations_current",
}, []string{"folder", "operation"})
metricTotalOperationSeconds = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "db",
Name: "operation_seconds_total",
Help: "Total time spent in database operations, per folder and operation",
}, []string{"folder", "operation"})
metricTotalOperationsCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "db",
Name: "operations_total",
Help: "Total number of database operations, per folder and operation",
}, []string{"folder", "operation"})
metricTotalFilesUpdatedCount = promauto.NewCounterVec(prometheus.CounterOpts{
Namespace: "syncthing",
Subsystem: "db",
Name: "files_updated_total",
Help: "Total number of files updated",
}, []string{"folder"})
)
func MetricsWrap(db DB) DB {
return metricsDB{db}
}
type metricsDB struct {
DB
}
func (m metricsDB) account(folder, op string) func() {
t0 := time.Now()
metricCurrentOperations.WithLabelValues(folder, op).Inc()
return func() {
if dur := time.Since(t0).Seconds(); dur > 0 {
metricTotalOperationSeconds.WithLabelValues(folder, op).Add(dur)
}
metricTotalOperationsCount.WithLabelValues(folder, op).Inc()
metricCurrentOperations.WithLabelValues(folder, op).Dec()
}
}
func (m metricsDB) AllLocalFilesWithBlocksHash(folder string, h []byte) (iter.Seq[FileMetadata], func() error) {
defer m.account(folder, "AllLocalFilesWithBlocksHash")()
return m.DB.AllLocalFilesWithBlocksHash(folder, h)
}
func (m metricsDB) AllLocalFilesWithBlocksHashAnyFolder(h []byte) (iter.Seq2[string, FileMetadata], func() error) {
defer m.account("-", "AllLocalFilesWithBlocksHashAnyFolder")()
return m.DB.AllLocalFilesWithBlocksHashAnyFolder(h)
}
func (m metricsDB) AllGlobalFiles(folder string) (iter.Seq[FileMetadata], func() error) {
defer m.account(folder, "AllGlobalFiles")()
return m.DB.AllGlobalFiles(folder)
}
func (m metricsDB) AllGlobalFilesPrefix(folder string, prefix string) (iter.Seq[FileMetadata], func() error) {
defer m.account(folder, "AllGlobalFilesPrefix")()
return m.DB.AllGlobalFilesPrefix(folder, prefix)
}
func (m metricsDB) AllLocalFiles(folder string, device protocol.DeviceID) (iter.Seq[protocol.FileInfo], func() error) {
defer m.account(folder, "AllLocalFiles")()
return m.DB.AllLocalFiles(folder, device)
}
func (m metricsDB) AllLocalFilesWithPrefix(folder string, device protocol.DeviceID, prefix string) (iter.Seq[protocol.FileInfo], func() error) {
defer m.account(folder, "AllLocalFilesPrefix")()
return m.DB.AllLocalFilesWithPrefix(folder, device, prefix)
}
func (m metricsDB) AllLocalFilesBySequence(folder string, device protocol.DeviceID, startSeq int64, limit int) (iter.Seq[protocol.FileInfo], func() error) {
defer m.account(folder, "AllLocalFilesBySequence")()
return m.DB.AllLocalFilesBySequence(folder, device, startSeq, limit)
}
func (m metricsDB) AllNeededGlobalFiles(folder string, device protocol.DeviceID, order config.PullOrder, limit, offset int) (iter.Seq[protocol.FileInfo], func() error) {
defer m.account(folder, "AllNeededGlobalFiles")()
return m.DB.AllNeededGlobalFiles(folder, device, order, limit, offset)
}
func (m metricsDB) GetGlobalAvailability(folder, file string) ([]protocol.DeviceID, error) {
defer m.account(folder, "GetGlobalAvailability")()
return m.DB.GetGlobalAvailability(folder, file)
}
func (m metricsDB) AllLocalBlocksWithHash(hash []byte) (iter.Seq[BlockMapEntry], func() error) {
defer m.account("-", "AllLocalBlocksWithHash")()
return m.DB.AllLocalBlocksWithHash(hash)
}
func (m metricsDB) Close() error {
defer m.account("-", "Close")()
return m.DB.Close()
}
func (m metricsDB) ListDevicesForFolder(folder string) ([]protocol.DeviceID, error) {
defer m.account(folder, "ListDevicesForFolder")()
return m.DB.ListDevicesForFolder(folder)
}
func (m metricsDB) RemoteSequences(folder string) (map[protocol.DeviceID]int64, error) {
defer m.account(folder, "RemoteSequences")()
return m.DB.RemoteSequences(folder)
}
func (m metricsDB) DropAllFiles(folder string, device protocol.DeviceID) error {
defer m.account(folder, "DropAllFiles")()
return m.DB.DropAllFiles(folder, device)
}
func (m metricsDB) DropDevice(device protocol.DeviceID) error {
defer m.account("-", "DropDevice")()
return m.DB.DropDevice(device)
}
func (m metricsDB) DropFilesNamed(folder string, device protocol.DeviceID, names []string) error {
defer m.account(folder, "DropFilesNamed")()
return m.DB.DropFilesNamed(folder, device, names)
}
func (m metricsDB) DropFolder(folder string) error {
defer m.account(folder, "DropFolder")()
return m.DB.DropFolder(folder)
}
func (m metricsDB) DropAllIndexIDs() error {
defer m.account("-", "IndexIDDropAll")()
return m.DB.DropAllIndexIDs()
}
func (m metricsDB) ListFolders() ([]string, error) {
defer m.account("-", "ListFolders")()
return m.DB.ListFolders()
}
func (m metricsDB) GetGlobalFile(folder string, file string) (protocol.FileInfo, bool, error) {
defer m.account(folder, "GetGlobalFile")()
return m.DB.GetGlobalFile(folder, file)
}
func (m metricsDB) CountGlobal(folder string) (Counts, error) {
defer m.account(folder, "CountGlobal")()
return m.DB.CountGlobal(folder)
}
func (m metricsDB) GetIndexID(folder string, device protocol.DeviceID) (protocol.IndexID, error) {
defer m.account(folder, "IndexIDGet")()
return m.DB.GetIndexID(folder, device)
}
func (m metricsDB) GetDeviceFile(folder string, device protocol.DeviceID, file string) (protocol.FileInfo, bool, error) {
defer m.account(folder, "GetDeviceFile")()
return m.DB.GetDeviceFile(folder, device, file)
}
func (m metricsDB) CountLocal(folder string, device protocol.DeviceID) (Counts, error) {
defer m.account(folder, "CountLocal")()
return m.DB.CountLocal(folder, device)
}
func (m metricsDB) CountNeed(folder string, device protocol.DeviceID) (Counts, error) {
defer m.account(folder, "CountNeed")()
return m.DB.CountNeed(folder, device)
}
func (m metricsDB) CountReceiveOnlyChanged(folder string) (Counts, error) {
defer m.account(folder, "CountReceiveOnlyChanged")()
return m.DB.CountReceiveOnlyChanged(folder)
}
func (m metricsDB) GetDeviceSequence(folder string, device protocol.DeviceID) (int64, error) {
defer m.account(folder, "GetDeviceSequence")()
return m.DB.GetDeviceSequence(folder, device)
}
func (m metricsDB) SetIndexID(folder string, device protocol.DeviceID, id protocol.IndexID) error {
defer m.account(folder, "IndexIDSet")()
return m.DB.SetIndexID(folder, device, id)
}
func (m metricsDB) Update(folder string, device protocol.DeviceID, fs []protocol.FileInfo) error {
defer m.account(folder, "Update")()
defer metricTotalFilesUpdatedCount.WithLabelValues(folder).Add(float64(len(fs)))
return m.DB.Update(folder, device, fs)
}
func (m metricsDB) GetKV(key string) ([]byte, error) {
defer m.account("-", "GetKV")()
return m.DB.GetKV(key)
}
func (m metricsDB) PutKV(key string, val []byte) error {
defer m.account("-", "PutKV")()
return m.DB.PutKV(key, val)
}
func (m metricsDB) DeleteKV(key string) error {
defer m.account("-", "DeleteKV")()
return m.DB.DeleteKV(key)
}
func (m metricsDB) PrefixKV(prefix string) (iter.Seq[KeyValue], func() error) {
defer m.account("-", "PrefixKV")()
return m.DB.PrefixKV(prefix)
}
@@ -1,113 +0,0 @@
// Copyright (C) 2018 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 backend
import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/iterator"
"github.com/syndtr/goleveldb/leveldb/util"
)
// leveldbBackend implements Backend on top of a leveldb
type leveldbBackend struct {
ldb *leveldb.DB
closeWG *closeWaitGroup
location string
}
func newLeveldbBackend(ldb *leveldb.DB, location string) *leveldbBackend {
return &leveldbBackend{
ldb: ldb,
closeWG: &closeWaitGroup{},
location: location,
}
}
func (b *leveldbBackend) NewReadTransaction() (ReadTransaction, error) {
return b.newSnapshot()
}
func (b *leveldbBackend) newSnapshot() (leveldbSnapshot, error) {
rel, err := newReleaser(b.closeWG)
if err != nil {
return leveldbSnapshot{}, err
}
snap, err := b.ldb.GetSnapshot()
if err != nil {
rel.Release()
return leveldbSnapshot{}, wrapLeveldbErr(err)
}
return leveldbSnapshot{
snap: snap,
rel: rel,
}, nil
}
func (b *leveldbBackend) Close() error {
b.closeWG.CloseWait()
return wrapLeveldbErr(b.ldb.Close())
}
func (b *leveldbBackend) Get(key []byte) ([]byte, error) {
val, err := b.ldb.Get(key, nil)
return val, wrapLeveldbErr(err)
}
func (b *leveldbBackend) NewPrefixIterator(prefix []byte) (Iterator, error) {
return &leveldbIterator{b.ldb.NewIterator(util.BytesPrefix(prefix), nil)}, nil
}
func (b *leveldbBackend) NewRangeIterator(first, last []byte) (Iterator, error) {
return &leveldbIterator{b.ldb.NewIterator(&util.Range{Start: first, Limit: last}, nil)}, nil
}
func (b *leveldbBackend) Location() string {
return b.location
}
// leveldbSnapshot implements backend.ReadTransaction
type leveldbSnapshot struct {
snap *leveldb.Snapshot
rel *releaser
}
func (l leveldbSnapshot) Get(key []byte) ([]byte, error) {
val, err := l.snap.Get(key, nil)
return val, wrapLeveldbErr(err)
}
func (l leveldbSnapshot) NewPrefixIterator(prefix []byte) (Iterator, error) {
return l.snap.NewIterator(util.BytesPrefix(prefix), nil), nil
}
func (l leveldbSnapshot) NewRangeIterator(first, last []byte) (Iterator, error) {
return l.snap.NewIterator(&util.Range{Start: first, Limit: last}, nil), nil
}
func (l leveldbSnapshot) Release() {
l.snap.Release()
l.rel.Release()
}
type leveldbIterator struct {
iterator.Iterator
}
func (it *leveldbIterator) Error() error {
return wrapLeveldbErr(it.Iterator.Error())
}
// wrapLeveldbErr wraps errors so that the backend package can recognize them
func wrapLeveldbErr(err error) error {
switch err {
case leveldb.ErrClosed:
return errClosed
case leveldb.ErrNotFound:
return errNotFound
}
return err
}
-32
View File
@@ -1,32 +0,0 @@
// Copyright (C) 2018 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 backend
import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/opt"
)
const dbMaxOpenFiles = 100
// OpenLevelDBRO attempts to open the database at the given location, read
// only.
func OpenLevelDBRO(location string) (Backend, error) {
opts := &opt.Options{
OpenFilesCacheCapacity: dbMaxOpenFiles,
ReadOnly: true,
}
ldb, err := open(location, opts)
if err != nil {
return nil, err
}
return newLeveldbBackend(ldb, location), nil
}
func open(location string, opts *opt.Options) (*leveldb.DB, error) {
return leveldb.OpenFile(location, opts)
}

Some files were not shown because too many files have changed in this diff Show More