mirror of
https://github.com/syncthing/syncthing.git
synced 2026-01-04 03:49:12 -05:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d475ad7ce1 | ||
|
|
7c8418f493 | ||
|
|
200a7fc844 | ||
|
|
5a38e0ba3f | ||
|
|
c77490c32d | ||
|
|
b75c9f2bbb | ||
|
|
322bedbb04 | ||
|
|
487655b365 | ||
|
|
03c678a810 | ||
|
|
b79f8aceb8 | ||
|
|
92e8c4303a | ||
|
|
e735a3a25c |
5
AUTHORS
5
AUTHORS
@@ -67,10 +67,11 @@ Kelong Cong (kc1212) <kc04bc@gmx.com> <kc1212@users.noreply.github.com>
|
||||
Ken'ichi Kamada (kamadak) <kamada@nanohz.org>
|
||||
Kevin Allen (ironmig) <kma1660@gmail.com>
|
||||
Kevin White, Jr. (kwhite17) <kevinwhite1710@gmail.com>
|
||||
Kurt Fitzner (Kudalufi) <kurt@va1der.ca>
|
||||
Kurt Fitzner (Kudalufi) <kurt@va1der.ca> <kurt.fitzner@gmail.com>
|
||||
Lars K.W. Gohlke (lkwg82) <lkwg82@gmx.de>
|
||||
Laurent Etiemble (letiemble) <laurent.etiemble@gmail.com> <laurent.etiemble@monobjc.net>
|
||||
Leo Arias (elopio) <yo@elopio.net>
|
||||
Liu Siyuan (liusy182) <liusy182@gmail.com> <liusy182@hotmail.com>
|
||||
Lode Hoste (Zillode) <zillode@zillode.be>
|
||||
Lord Landon Agahnim (LordLandon) <lordlandon@gmail.com>
|
||||
Majed Abdulaziz (majedev) <majed.alhajry@gmail.com>
|
||||
@@ -112,6 +113,6 @@ Veeti Paananen (veeti) <veeti.paananen@rojekti.fi>
|
||||
Victor Buinsky (buinsky) <vix_booja@tut.by>
|
||||
Vil Brekin (Vilbrekin) <vilbrekin@gmail.com>
|
||||
William A. Kennington III (wkennington) <william@wkennington.com>
|
||||
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de>
|
||||
Wulf Weich (wweich) <wweich@users.noreply.github.com> <wweich@gmx.de> <wulf@weich-kr.de>
|
||||
Xavier O. (damajor) <damajor@gmail.com>
|
||||
Yannic A. (eipiminus1) <eipiminusone+github@gmail.com> <eipiminus1@users.noreply.github.com>
|
||||
|
||||
4
NICKS
4
NICKS
@@ -70,9 +70,12 @@ kralo <max.schulze@online.de>
|
||||
kralo <kralo@users.noreply.github.com>
|
||||
krozycki <rozycki.karol@gmail.com>
|
||||
Kudalufi <kurt@va1der.ca>
|
||||
Kudalufi <kurt.fitzner@gmail.com>
|
||||
kwhite17 <kevinwhite1710@gmail.com>
|
||||
letiemble <laurent.etiemble@gmail.com>
|
||||
letiemble <laurent.etiemble@monobjc.net>
|
||||
liusy182 <liusy182@gmail.com>
|
||||
liusy182 <liusy182@hotmail.com>
|
||||
lkwg82 <lkwg82@gmx.de>
|
||||
LordLandon <lordlandon@gmail.com>
|
||||
majedev <majed.alhajry@gmail.com>
|
||||
@@ -136,6 +139,7 @@ wkennington <william@wkennington.com>
|
||||
WSGCSysadmin <e.meitner@willystreet.coop>
|
||||
wweich <wweich@users.noreply.github.com>
|
||||
wweich <wweich@gmx.de>
|
||||
wweich <wulf@weich-kr.de>
|
||||
xduugu <cedric@gmx.ca>
|
||||
zaynetro <romanznet@gmail.com>
|
||||
Zillode <zillode@zillode.be>
|
||||
|
||||
281
build.go
281
build.go
@@ -13,6 +13,7 @@ import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
@@ -32,14 +33,16 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
|
||||
goarch string
|
||||
goos string
|
||||
noupgrade bool
|
||||
version string
|
||||
goVersion float64
|
||||
race bool
|
||||
debug = os.Getenv("BUILDDEBUG") != ""
|
||||
versionRe = regexp.MustCompile(`-[0-9]{1,3}-g[0-9a-f]{5,10}`)
|
||||
goarch string
|
||||
goos string
|
||||
noupgrade bool
|
||||
version string
|
||||
goVersion float64
|
||||
race bool
|
||||
debug = os.Getenv("BUILDDEBUG") != ""
|
||||
noBuildGopath bool
|
||||
extraTags string
|
||||
)
|
||||
|
||||
type target struct {
|
||||
@@ -65,7 +68,7 @@ var targets = map[string]target{
|
||||
"all": {
|
||||
// Only valid for the "build" and "install" commands as it lacks all
|
||||
// the archive creation stuff.
|
||||
buildPkg: "./cmd/...",
|
||||
buildPkg: "github.com/syncthing/syncthing/cmd/...",
|
||||
tags: []string{"purego"},
|
||||
},
|
||||
"syncthing": {
|
||||
@@ -75,7 +78,7 @@ var targets = map[string]target{
|
||||
debdeps: []string{"libc6", "procps"},
|
||||
debpost: "script/post-upgrade",
|
||||
description: "Open Source Continuous File Synchronization",
|
||||
buildPkg: "./cmd/syncthing",
|
||||
buildPkg: "github.com/syncthing/syncthing/cmd/syncthing",
|
||||
binaryName: "syncthing", // .exe will be added automatically for Windows builds
|
||||
archiveFiles: []archiveFile{
|
||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
||||
@@ -110,7 +113,7 @@ var targets = map[string]target{
|
||||
debname: "syncthing-discosrv",
|
||||
debdeps: []string{"libc6"},
|
||||
description: "Syncthing Discovery Server",
|
||||
buildPkg: "./cmd/stdiscosrv",
|
||||
buildPkg: "github.com/syncthing/syncthing/cmd/stdiscosrv",
|
||||
binaryName: "stdiscosrv", // .exe will be added automatically for Windows builds
|
||||
archiveFiles: []archiveFile{
|
||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
||||
@@ -132,7 +135,7 @@ var targets = map[string]target{
|
||||
debname: "syncthing-relaysrv",
|
||||
debdeps: []string{"libc6"},
|
||||
description: "Syncthing Relay Server",
|
||||
buildPkg: "./cmd/strelaysrv",
|
||||
buildPkg: "github.com/syncthing/syncthing/cmd/strelaysrv",
|
||||
binaryName: "strelaysrv", // .exe will be added automatically for Windows builds
|
||||
archiveFiles: []archiveFile{
|
||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
||||
@@ -153,7 +156,7 @@ var targets = map[string]target{
|
||||
debname: "syncthing-relaypoolsrv",
|
||||
debdeps: []string{"libc6"},
|
||||
description: "Syncthing Relay Pool Server",
|
||||
buildPkg: "./cmd/strelaypoolsrv",
|
||||
buildPkg: "github.com/syncthing/syncthing/cmd/strelaypoolsrv",
|
||||
binaryName: "strelaypoolsrv", // .exe will be added automatically for Windows builds
|
||||
archiveFiles: []archiveFile{
|
||||
{src: "{{binary}}", dst: "{{binary}}", perm: 0755},
|
||||
@@ -170,41 +173,6 @@ var targets = map[string]target{
|
||||
},
|
||||
}
|
||||
|
||||
var (
|
||||
// fast linters complete in a fraction of a second and might as well be
|
||||
// run always as part of the build
|
||||
fastLinters = []string{
|
||||
"deadcode",
|
||||
"golint",
|
||||
"ineffassign",
|
||||
"vet",
|
||||
}
|
||||
|
||||
// slow linters take several seconds and are run only as part of the
|
||||
// "metalint" command.
|
||||
slowLinters = []string{
|
||||
"gosimple",
|
||||
"staticcheck",
|
||||
"structcheck",
|
||||
"unused",
|
||||
"varcheck",
|
||||
}
|
||||
|
||||
// Which parts of the tree to lint
|
||||
lintDirs = []string{".", "./lib/...", "./cmd/..."}
|
||||
|
||||
// Messages to ignore
|
||||
lintExcludes = []string{
|
||||
".pb.go",
|
||||
"should have comment",
|
||||
"protocol.Vector composite literal uses unkeyed fields",
|
||||
"cli.Requires composite literal uses unkeyed fields",
|
||||
"Use DialContext instead", // Go 1.7
|
||||
"os.SEEK_SET is deprecated", // Go 1.7
|
||||
"SA4017", // staticcheck "is a pure function but its return value is ignored"
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// The "syncthing" target includes a few more files found in the "etc"
|
||||
// and "extra" dirs.
|
||||
@@ -222,9 +190,10 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(0)
|
||||
|
||||
parseFlags()
|
||||
|
||||
if debug {
|
||||
t0 := time.Now()
|
||||
defer func() {
|
||||
@@ -233,15 +202,24 @@ func main() {
|
||||
}
|
||||
|
||||
if os.Getenv("GOPATH") == "" {
|
||||
setGoPath()
|
||||
gopath, err := temporaryBuildDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !noBuildGopath {
|
||||
lazyRebuildAssets()
|
||||
if err := buildGOPATH(gopath); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
os.Setenv("GOPATH", gopath)
|
||||
log.Println("GOPATH is", gopath)
|
||||
}
|
||||
|
||||
// Set path to $GOPATH/bin:$PATH so that we can for sure find tools we
|
||||
// might have installed during "build.go setup".
|
||||
os.Setenv("PATH", fmt.Sprintf("%s%cbin%c%s", os.Getenv("GOPATH"), os.PathSeparator, os.PathListSeparator, os.Getenv("PATH")))
|
||||
|
||||
parseFlags()
|
||||
|
||||
checkArchitecture()
|
||||
|
||||
// Invoking build.go with no parameters at all builds everything (incrementally),
|
||||
@@ -284,22 +262,23 @@ func runCommand(cmd string, target target) {
|
||||
if noupgrade {
|
||||
tags = []string{"noupgrade"}
|
||||
}
|
||||
tags = append(tags, strings.Fields(extraTags)...)
|
||||
install(target, tags)
|
||||
metalint(fastLinters, lintDirs)
|
||||
metalintShort()
|
||||
|
||||
case "build":
|
||||
var tags []string
|
||||
if noupgrade {
|
||||
tags = []string{"noupgrade"}
|
||||
}
|
||||
tags = append(tags, strings.Fields(extraTags)...)
|
||||
build(target, tags)
|
||||
metalint(fastLinters, lintDirs)
|
||||
|
||||
case "test":
|
||||
test("./lib/...", "./cmd/...")
|
||||
test("github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
|
||||
case "bench":
|
||||
bench("./lib/...", "./cmd/...")
|
||||
bench("github.com/syncthing/syncthing/lib/...", "github.com/syncthing/syncthing/cmd/...")
|
||||
|
||||
case "assets":
|
||||
rebuildAssets()
|
||||
@@ -329,41 +308,37 @@ func runCommand(cmd string, target target) {
|
||||
clean()
|
||||
|
||||
case "vet":
|
||||
metalint(fastLinters, lintDirs)
|
||||
metalintShort()
|
||||
|
||||
case "lint":
|
||||
metalint(fastLinters, lintDirs)
|
||||
metalintShort()
|
||||
|
||||
case "metalint":
|
||||
metalint(fastLinters, lintDirs)
|
||||
metalint(slowLinters, lintDirs)
|
||||
metalint()
|
||||
|
||||
case "version":
|
||||
fmt.Println(getVersion())
|
||||
|
||||
case "gopath":
|
||||
gopath, err := temporaryBuildDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(gopath)
|
||||
|
||||
default:
|
||||
log.Fatalf("Unknown command %q", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// setGoPath sets GOPATH correctly with the assumption that we are
|
||||
// in $GOPATH/src/github.com/syncthing/syncthing.
|
||||
func setGoPath() {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
gopath := filepath.Clean(filepath.Join(cwd, "../../../../"))
|
||||
log.Println("GOPATH is", gopath)
|
||||
os.Setenv("GOPATH", gopath)
|
||||
}
|
||||
|
||||
func parseFlags() {
|
||||
flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
|
||||
flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
|
||||
flag.BoolVar(&noupgrade, "no-upgrade", noupgrade, "Disable upgrade functionality")
|
||||
flag.StringVar(&version, "version", getVersion(), "Set compiled in version string")
|
||||
flag.BoolVar(&race, "race", race, "Use race detector")
|
||||
flag.BoolVar(&noBuildGopath, "no-build-gopath", noBuildGopath, "Don't build GOPATH, assume it's OK")
|
||||
flag.StringVar(&extraTags, "tags", extraTags, "Extra tags, space separated")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
@@ -390,7 +365,7 @@ func setup() {
|
||||
runPrint("go", "get", "-u", pkg)
|
||||
}
|
||||
|
||||
runPrint("go", "install", "-v", "./vendor/github.com/gogo/protobuf/protoc-gen-gogofast")
|
||||
runPrint("go", "install", "-v", "github.com/syncthing/syncthing/vendor/github.com/gogo/protobuf/protoc-gen-gogofast")
|
||||
}
|
||||
|
||||
func test(pkgs ...string) {
|
||||
@@ -428,6 +403,7 @@ func install(target target, tags []string) {
|
||||
args := []string{"install", "-v", "-ldflags", ldflags()}
|
||||
if len(tags) > 0 {
|
||||
args = append(args, "-tags", strings.Join(tags, " "))
|
||||
args = append(args, "-installsuffix", strings.Join(tags, "-"))
|
||||
}
|
||||
if race {
|
||||
args = append(args, "-race")
|
||||
@@ -448,6 +424,7 @@ func build(target target, tags []string) {
|
||||
args := []string{"build", "-i", "-v", "-ldflags", ldflags()}
|
||||
if len(tags) > 0 {
|
||||
args = append(args, "-tags", strings.Join(tags, " "))
|
||||
args = append(args, "-installsuffix", strings.Join(tags, "-"))
|
||||
}
|
||||
if race {
|
||||
args = append(args, "-race")
|
||||
@@ -482,7 +459,7 @@ func buildTar(target target) {
|
||||
}
|
||||
|
||||
tarGz(filename, target.archiveFiles)
|
||||
log.Println(filename)
|
||||
fmt.Println(filename)
|
||||
}
|
||||
|
||||
func buildZip(target target) {
|
||||
@@ -506,7 +483,7 @@ func buildZip(target target) {
|
||||
}
|
||||
|
||||
zipFile(filename, target.archiveFiles)
|
||||
log.Println(filename)
|
||||
fmt.Println(filename)
|
||||
}
|
||||
|
||||
func buildDeb(target target) {
|
||||
@@ -605,21 +582,36 @@ func buildSnap(target target) {
|
||||
runPrint("snapcraft")
|
||||
}
|
||||
|
||||
// copyFile copies a file from src to dst, ensuring the containing directory
|
||||
// exists. The permission bits are copied as well. If dst already exists and
|
||||
// the contents are identical to src the modification time is not updated.
|
||||
func copyFile(src, dst string, perm os.FileMode) error {
|
||||
dstDir := filepath.Dir(dst)
|
||||
os.MkdirAll(dstDir, 0755) // ignore error
|
||||
srcFd, err := os.Open(src)
|
||||
in, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer srcFd.Close()
|
||||
dstFd, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm)
|
||||
|
||||
out, err := ioutil.ReadFile(dst)
|
||||
if err != nil {
|
||||
// The destination probably doesn't exist, we should create
|
||||
// it.
|
||||
goto copy
|
||||
}
|
||||
|
||||
if bytes.Equal(in, out) {
|
||||
// The permission bits may have changed without the contents
|
||||
// changing so we always mirror them.
|
||||
os.Chmod(dst, perm)
|
||||
return nil
|
||||
}
|
||||
|
||||
copy:
|
||||
os.MkdirAll(filepath.Dir(dst), 0777)
|
||||
if err := ioutil.WriteFile(dst, in, perm); err != nil {
|
||||
return err
|
||||
}
|
||||
defer dstFd.Close()
|
||||
_, err = io.Copy(dstFd, srcFd)
|
||||
return err
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func listFiles(dir string) []string {
|
||||
@@ -674,7 +666,7 @@ func shouldRebuildAssets(target, srcdir string) bool {
|
||||
}
|
||||
|
||||
func proto() {
|
||||
runPrint("go", "generate", "./lib/...")
|
||||
runPrint("go", "generate", "github.com/syncthing/syncthing/lib/...")
|
||||
}
|
||||
|
||||
func translate() {
|
||||
@@ -801,8 +793,9 @@ func getBranchSuffix() string {
|
||||
}
|
||||
|
||||
branch = parts[len(parts)-1]
|
||||
if branch == "master" {
|
||||
// master builds are the default.
|
||||
switch branch {
|
||||
case "master", "release":
|
||||
// these are not special
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -1054,59 +1047,99 @@ func macosCodesign(file string) {
|
||||
}
|
||||
}
|
||||
|
||||
func metalint(linters []string, dirs []string) {
|
||||
ok := true
|
||||
if isGometalinterInstalled() {
|
||||
if !gometalinter(linters, dirs, lintExcludes...) {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
log.Fatal("Build succeeded, but there were lint warnings")
|
||||
}
|
||||
func metalint() {
|
||||
lazyRebuildAssets()
|
||||
runPrint("go", "test", "-run", "Metalint", "./meta")
|
||||
}
|
||||
|
||||
func isGometalinterInstalled() bool {
|
||||
if _, err := runError("gometalinter", "--disable-all"); err != nil {
|
||||
log.Println("gometalinter is not installed")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
func metalintShort() {
|
||||
lazyRebuildAssets()
|
||||
runPrint("go", "test", "-short", "-run", "Metalint", "./meta")
|
||||
}
|
||||
|
||||
func gometalinter(linters []string, dirs []string, excludes ...string) bool {
|
||||
params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
|
||||
func temporaryBuildDir() (string, error) {
|
||||
// The base of our temp dir is "syncthing-xxxxxxxx" where the x:es
|
||||
// are eight bytes from the sha256 of our working directory. We do
|
||||
// this because we want a name in the global temp dir that doesn't
|
||||
// conflict with someone else building syncthing on the same
|
||||
// machine, yet is persistent between runs from the same source
|
||||
// directory.
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := sha256.Sum256([]byte(wd))
|
||||
base := fmt.Sprintf("syncthing-%x", hash[:4])
|
||||
|
||||
for _, linter := range linters {
|
||||
params = append(params, "--enable="+linter)
|
||||
// The temp dir is taken from $STTMPDIR if set, otherwise the system
|
||||
// default (potentially infrluenced by $TMPDIR on unixes).
|
||||
var tmpDir string
|
||||
if t := os.Getenv("STTMPDIR"); t != "" {
|
||||
tmpDir = t
|
||||
} else {
|
||||
tmpDir = os.TempDir()
|
||||
}
|
||||
|
||||
for _, exclude := range excludes {
|
||||
params = append(params, "--exclude="+exclude)
|
||||
return filepath.Join(tmpDir, base), nil
|
||||
}
|
||||
|
||||
func buildGOPATH(gopath string) error {
|
||||
pkg := filepath.Join(gopath, "src/github.com/syncthing/syncthing")
|
||||
dirs := []string{"cmd", "lib", "meta", "script", "test", "vendor"}
|
||||
|
||||
if debug {
|
||||
t0 := time.Now()
|
||||
log.Println("build temporary GOPATH in", gopath)
|
||||
defer func() {
|
||||
log.Println("... in", time.Since(t0))
|
||||
}()
|
||||
}
|
||||
|
||||
// Walk the sources and copy the files into the temporary GOPATH.
|
||||
// Remember which files are supposed to be present so we can clean
|
||||
// out everything else in the next step. The copyFile() step will
|
||||
// only actually copy the file if it doesn't exist or the contents
|
||||
// differ.
|
||||
|
||||
exists := map[string]struct{}{}
|
||||
for _, dir := range dirs {
|
||||
params = append(params, dir)
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
dst := filepath.Join(pkg, path)
|
||||
exists[dst] = struct{}{}
|
||||
|
||||
if err := copyFile(path, dst, info.Mode()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bs, _ := runError("gometalinter", params...)
|
||||
// Walk the temporary GOPATH and remove any files that we wouldn't
|
||||
// have copied there in the previous step.
|
||||
|
||||
nerr := 0
|
||||
lines := make(map[string]struct{})
|
||||
for _, line := range strings.Split(string(bs), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
filepath.Walk(pkg, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := lines[line]; ok {
|
||||
continue
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
log.Println(line)
|
||||
if strings.Contains(line, "executable file not found") {
|
||||
log.Println(` - Try "go run build.go setup" to install missing tools`)
|
||||
if _, ok := exists[path]; !ok {
|
||||
os.Remove(path)
|
||||
}
|
||||
lines[line] = struct{}{}
|
||||
nerr++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nerr == 0
|
||||
return nil
|
||||
}
|
||||
|
||||
1
build.sh
1
build.sh
@@ -17,6 +17,7 @@ build() {
|
||||
case "${1:-default}" in
|
||||
default)
|
||||
build
|
||||
build lint
|
||||
;;
|
||||
|
||||
clean)
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когато добавяш ново устройство помни, че твоето устройство също трябва да бъде добавено от другата страна.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когато добавяш нов идентификатор на папка помни, че той се използва за свързване на папките на различни устройства. Главни/малки букви са от значение и трябва да са еднакви на всички устройства.",
|
||||
"Yes": "Да",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can also select one of these nearby devices:": "Също така може да изберете едно от следните устройтва намиращи се наблизо:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Може да промените решението си по всяко време в прозореца Настройки.",
|
||||
"You can read more about the two release channels at the link below.": "Може да научите допълнително за двата канала на версии, следвайки връзката по-долу.",
|
||||
"You must keep at least one version.": "Трябва да пазиш поне една версия.",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"A device with that ID is already added.": "L'appareil portant cette ID est déjà présent.",
|
||||
"A device with that ID is already added.": "L'appareil portant cet ID est déjà présent.",
|
||||
"A negative number of days doesn't make sense.": "Ce champ n'accepte qu'un entier positif ou nul.",
|
||||
"A new major version may not be compatible with previous versions.": "Une nouvelle version majeure peut présenter des incompatibilités avec les versions antérieures.",
|
||||
"API Key": "Clé API",
|
||||
@@ -48,7 +48,7 @@
|
||||
"Danger!": "Attention !",
|
||||
"Deleted": "Supprimé",
|
||||
"Device": "Appareil",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "L'appareil \"{{name}}\" ({{device}} à {{address}}) veut se connecter. L'acceptez-vous ?",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "\"{{name}}\" ({{device}}), actuellement à {{address}}, demande à se connecter.\nAcceptez-vous de l'ajouter à votre liste d'appareils connus ?",
|
||||
"Device ID": "ID de l'appareil",
|
||||
"Device Identification": "Identifiant de l'appareil",
|
||||
"Device Name": "Nom de l'appareil",
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Quando si aggiunge un nuovo dispositivo, tenere presente che il dispositivo deve essere aggiunto anche dall'altra parte.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Quando aggiungi una nuova cartella, ricordati che gli ID vengono utilizzati per collegare le cartelle nei dispositivi. Distinguono maiuscole e minuscole e devono corrispondere esattamente su tutti i dispositivi.",
|
||||
"Yes": "Sì",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can also select one of these nearby devices:": "È anche possibile selezionare uno di questi dispositivi nelle vicinanze:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Puoi sempre cambiare la tua scelta nel dialogo Impostazioni.",
|
||||
"You can read more about the two release channels at the link below.": "Puoi ottenere piu informazioni riguarda i due canali di rilascio nel collegamento sottostante.",
|
||||
"You must keep at least one version.": "È necessario mantenere almeno una versione.",
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"Add Device": "デバイスを追加",
|
||||
"Add Folder": "フォルダーを追加",
|
||||
"Add Remote Device": "接続先デバイスを追加",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "紹介者デバイスから紹介されたデバイスは、相互に共有しているフォルダーがある場合、このデバイス上でも登録されます。",
|
||||
"Add new folder?": "新しいフォルダーとして追加しますか?",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "紹介者デバイスから紹介されたデバイスは、相互に共有しているフォルダーがある場合、このデバイス上にも追加されます。",
|
||||
"Add new folder?": "新しいフォルダーとして追加しますか?",
|
||||
"Address": "アドレス",
|
||||
"Addresses": "アドレス",
|
||||
"Advanced": "高度な設定",
|
||||
@@ -50,7 +50,7 @@
|
||||
"Device": "デバイス",
|
||||
"Device \"{%name%}\" ({%device%} at {%address%}) wants to connect. Add new device?": "デバイス「{{name}}」 ({{address}} の {{device}}) が接続を求めています。新しいデバイスとして追加しますか?",
|
||||
"Device ID": "デバイスID",
|
||||
"Device Identification": "デバイス識別情報",
|
||||
"Device Identification": "デバイスID",
|
||||
"Device Name": "デバイス名",
|
||||
"Devices": "デバイス",
|
||||
"Disconnected": "切断中",
|
||||
@@ -94,7 +94,7 @@
|
||||
"GUI Authentication Password": "GUI認証パスワード",
|
||||
"GUI Authentication User": "GUI認証ユーザー名",
|
||||
"GUI Listen Address": "GUI待ち受けアドレス",
|
||||
"GUI Listen Addresses": "GUI待ち受けアドレスリスト",
|
||||
"GUI Listen Addresses": "GUI待ち受けアドレス",
|
||||
"GUI Theme": "GUIテーマ",
|
||||
"Generate": "生成",
|
||||
"Global Changes": "全変更点",
|
||||
@@ -111,7 +111,7 @@
|
||||
"Introduced By": "紹介元",
|
||||
"Introducer": "紹介者デバイス",
|
||||
"Inversion of the given condition (i.e. do not exclude)": "条件の否定 (つまり、無視しないという意味になります)",
|
||||
"Keep Versions": "保持するバージョンの数",
|
||||
"Keep Versions": "保持するバージョン数",
|
||||
"Largest First": "大きい順",
|
||||
"Last File Received": "最後に受信したファイル",
|
||||
"Last Scan": "最終スキャン日時",
|
||||
@@ -125,7 +125,7 @@
|
||||
"Local State (Total)": "ローカル状態 (合計)",
|
||||
"Major Upgrade": "メジャーアップグレード",
|
||||
"Master": "マスター",
|
||||
"Maximum Age": "最長保存日数",
|
||||
"Maximum Age": "最大保存日数",
|
||||
"Metadata Only": "メタデータのみ",
|
||||
"Minimum Free Disk Space": "同期を停止する最小空きディスク容量",
|
||||
"Move to top of queue": "最優先にする",
|
||||
@@ -212,7 +212,7 @@
|
||||
"Statistics": "統計情報",
|
||||
"Stopped": "停止中",
|
||||
"Support": "サポート",
|
||||
"Sync Protocol Listen Addresses": "Syncプロトコルの待ち受けアドレスリスト",
|
||||
"Sync Protocol Listen Addresses": "同期プロトコルの待ち受けアドレス",
|
||||
"Syncing": "同期中",
|
||||
"Syncthing has been shut down.": "Syncthingをシャットダウンしました。",
|
||||
"Syncthing includes the following software or portions thereof:": "Syncthingは以下のソフトウェアまたはその一部を内包しています:",
|
||||
@@ -231,15 +231,15 @@
|
||||
"The folder ID cannot be blank.": "フォルダーIDは空欄にできません。",
|
||||
"The folder ID must be unique.": "フォルダーIDが重複しています。",
|
||||
"The folder path cannot be blank.": "フォルダーパスは空欄にできません。",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保存間隔は次の通りです。初めの1時間は30秒ごとに古いバージョンを保存します。同様に、初めの1日間は1時間ごと、初めの30日間は1日ごと、その後最長保存日数までは1週間ごとに、古いバージョンを保存します。",
|
||||
"The following intervals are used: for the first hour a version is kept every 30 seconds, for the first day a version is kept every hour, for the first 30 days a version is kept every day, until the maximum age a version is kept every week.": "保存間隔は次の通りです。初めの1時間は30秒ごとに古いバージョンを保存します。同様に、初めの1日間は1時間ごと、初めの30日間は1日ごと、その後最大保存日数までは1週間ごとに、古いバージョンを保存します。",
|
||||
"The following items could not be synchronized.": "以下の項目は同期できませんでした。",
|
||||
"The maximum age must be a number and cannot be blank.": "最長保存日数には数値を指定してください。空欄にはできません。",
|
||||
"The maximum age must be a number and cannot be blank.": "最大保存日数には数値を指定してください。空欄にはできません。",
|
||||
"The maximum time to keep a version (in days, set to 0 to keep versions forever).": "古いバージョンを保持する最大日数 (0で無期限)",
|
||||
"The minimum free disk space percentage must be a non-negative number between 0 and 100 (inclusive).": "最小空きディスク容量はパーセントで、0から100の値を入力してください。",
|
||||
"The number of days must be a number and cannot be blank.": "日数は数値を指定してください。空欄にはできません。",
|
||||
"The number of days to keep files in the trash can. Zero means forever.": "ゴミ箱でファイルを保持する日数 (0で無期限)",
|
||||
"The number of old versions to keep, per file.": "ファイルごとに古いバージョンをいくつ保持するかを指定します。",
|
||||
"The number of versions must be a number and cannot be blank.": "保持するバージョンの数は数値を指定してください。空欄にはできません。",
|
||||
"The number of versions must be a number and cannot be blank.": "保持するバージョン数は数値を指定してください。空欄にはできません。",
|
||||
"The path cannot be blank.": "パスを入力してください。",
|
||||
"The rate limit must be a non-negative number (0: no limit)": "帯域制限値は0以上で指定して下さい。 (0で無制限)",
|
||||
"The rescan interval must be a non-negative number of seconds.": "再スキャン間隔は0秒以上で指定してください。",
|
||||
@@ -265,7 +265,7 @@
|
||||
"Use HTTPS for GUI": "GUIにHTTPSを使用する",
|
||||
"Version": "バージョン",
|
||||
"Versions Path": "古いバージョンを保存するパス",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "古いバージョンは、最長保存日数もしくは期間ごとの最大保存数を超えた場合、自動的に削除されます。",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "古いバージョンは、最大保存日数もしくは期間ごとの最大保存数を超えた場合、自動的に削除されます。",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolder}}」の親ディレクトリです。",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolderLabel}}」 ({{otherFolder}}) の親ディレクトリです。",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "警告: 入力されたパスは、設定済みのフォルダー「{{otherFolder}}」のサブディレクトリです。",
|
||||
@@ -273,8 +273,8 @@
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "新しいデバイスを追加する際は、相手側デバイスにもこのデバイスを追加してください。",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "新しいフォルダーを追加する際、フォルダーIDはデバイス間でフォルダーの対応づけに使われることに注意してください。フォルダーIDは大文字と小文字が区別され、共有するすべてのデバイスの間で完全に一致しなくてはなりません。",
|
||||
"Yes": "はい",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can change your choice at any time in the Settings dialog.": "どちらを選ぶかは設定ダイアログからいつでも変更できます。",
|
||||
"You can also select one of these nearby devices:": "近くに検出された以下のデバイスの一つを選択できます。",
|
||||
"You can change your choice at any time in the Settings dialog.": "この設定はいつでも変更できます。",
|
||||
"You can read more about the two release channels at the link below.": "2種類のリリースチャネルについての詳細は、以下のリンク先を参照してください。",
|
||||
"You must keep at least one version.": "少なくとも一つのバージョンを保持する必要があります。",
|
||||
"days": "日",
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "새 장치를 추가할 시 추가한 기기 쪽에서도 이 장치를 추가해야 합니다.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "새 폴더를 추가할 시 폴더 ID는 장치간에 폴더를 묶을 때 사용됩니다. 대소문자를 구분하며 모든 장치에서 같은 ID를 사용해야 합니다.",
|
||||
"Yes": "예",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can also select one of these nearby devices:": "주변 기기 중 하나를 선택할 수 있습니다:",
|
||||
"You can change your choice at any time in the Settings dialog.": "설정창 에서 언제든지 원하시는 때에 설정을 변경하는 것이 가능합니다.",
|
||||
"You can read more about the two release channels at the link below.": "이 두 개의 출시 채널에 대해 아래 링크에서 자세하게 읽어 보실 수 있습니다.",
|
||||
"You must keep at least one version.": "최소 한 개의 버전은 유지해야 합니다.",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"Add": "Toevoegen",
|
||||
"Add Device": "Apparaat toevoegen",
|
||||
"Add Folder": "Map toevoegen",
|
||||
"Add Remote Device": "Voeg extern apparaat toe",
|
||||
"Add Remote Device": "Extern apparaat toevoegen",
|
||||
"Add devices from the introducer to our device list, for mutually shared folders.": "Voeg apparaten van het introductieapparaat toe aan de lijst met apparaten voor gemeenschappelijk gedeelde mappen.",
|
||||
"Add new folder?": "Nieuwe map toevoegen?",
|
||||
"Address": "Adres",
|
||||
@@ -63,11 +63,11 @@
|
||||
"Downloading": "Bezig met downloaden",
|
||||
"Edit": "Bewerk",
|
||||
"Edit Device": "Bewerk apparaat",
|
||||
"Edit Folder": "Bewerk map",
|
||||
"Edit Folder": "Map bewerken",
|
||||
"Editing": "Bezig met bewerken",
|
||||
"Editing {%path%}.": "Bezig met bewerken van {{path}}.",
|
||||
"Enable NAT traversal": "Activeer NAT traversal",
|
||||
"Enable Relaying": "Activeer doorsturen",
|
||||
"Enable NAT traversal": "NAT traversal inschakelen",
|
||||
"Enable Relaying": "Doorsturen inschakelen",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Voer door komma's gescheiden (\"tcp://ip:port\", \"tcp://host:port\") adressen in of voer \"dynamisch\" in om automatische ontdekking van het adres uit te voeren.",
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
"Advanced settings": "Дополнительные настройки",
|
||||
"All Data": "Все данные",
|
||||
"Allow Anonymous Usage Reporting?": "Разрешить анонимный отчет об использовании?",
|
||||
"Allowed Networks": "Allowed Networks",
|
||||
"Allowed Networks": "Разрешённые сети",
|
||||
"Alphabetic": "По алфавиту",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder.": "An external command handles the versioning. It has to remove the file from the shared folder.",
|
||||
"An external command handles the versioning. It has to remove the file from the shared folder.": "Для версионирования используется внешняя программа. Ей нужно удалить файл из общей папки.",
|
||||
"An external command handles the versioning. It has to remove the file from the synced folder.": "Внешний процесс управляет версиями файлов. Процесс удалит файл из синхронизируемой папки.",
|
||||
"Anonymous Usage Reporting": "Анонимный отчет об использовании",
|
||||
"Any devices configured on an introducer device will be added to this device as well.": "Все устройства, подключённые к устройству-рекомендателю, будут добавлены к текущему устройству.",
|
||||
@@ -43,7 +43,7 @@
|
||||
"Copied from elsewhere": "Скопировано из другого места",
|
||||
"Copied from original": "Скопировано с оригинала",
|
||||
"Copyright © 2014-2016 the following Contributors:": "Авторские права © 2014–2016 принадлежат:",
|
||||
"Copyright © 2014-2017 the following Contributors:": "Авторское право © 2014-2017 следующие участники:",
|
||||
"Copyright © 2014-2017 the following Contributors:": "Авторские права © 2014—2017 следующие участники:",
|
||||
"Creating ignore patterns, overwriting an existing file at {%path%}.": "Creating ignore patterns, overwriting an existing file at {{path}}.",
|
||||
"Danger!": "Опасно!",
|
||||
"Deleted": "Удалено",
|
||||
@@ -65,23 +65,23 @@
|
||||
"Edit Device": "Редактирование устройства",
|
||||
"Edit Folder": "Редактирование папки",
|
||||
"Editing": "Редактирование",
|
||||
"Editing {%path%}.": "Editing {{path}}.",
|
||||
"Editing {%path%}.": "Правка {{path}}.",
|
||||
"Enable NAT traversal": "Включить NAT traversal",
|
||||
"Enable Relaying": "Включить релеи",
|
||||
"Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.": "Enter a non-negative number (e.g., \"2.35\") and select a unit. Percentages are as part of the total disk size.",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Enter a non-privileged port number (1024 - 65535).",
|
||||
"Enter a non-privileged port number (1024 - 65535).": "Введите непривилегированный порт (1024—65535).",
|
||||
"Enter comma separated (\"tcp://ip:port\", \"tcp://host:port\") addresses or \"dynamic\" to perform automatic discovery of the address.": "Введите через запятую («tcp://ip:port», «tcp://host:port») адреса, либо «dynamic», чтобы выполнить автоматическое обнаружение адреса.",
|
||||
"Enter ignore patterns, one per line.": "Введите шаблоны игнорирования, по одному на строку.",
|
||||
"Error": "Ошибка",
|
||||
"External File Versioning": "Внешний контроль версий файлов",
|
||||
"Failed Items": "Сбои",
|
||||
"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.": "Если нет IPv6-соединений, при подключении к IPv6-серверам произойдёт ошибка.",
|
||||
"File Pull Order": "Порядок получения файлов",
|
||||
"File Versioning": "Управление версиями",
|
||||
"File permission bits are ignored when looking for changes. Use on FAT file systems.": "Права на файлы игнорируются при поиске изменений. Используется на файловой системе FAT.",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Files are moved to .stversions directory when replaced or deleted by Syncthing.",
|
||||
"Files are moved to .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, они помещаются в папку .stversions",
|
||||
"Files are moved to .stversions folder when replaced or deleted by Syncthing.": "Файлы перемещаются в папку .stversions после их замены или удаления системой Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.",
|
||||
"Files are moved to date stamped versions in a .stversions directory when replaced or deleted by Syncthing.": "Когда Syncthing изменяет или удаляет файлы, их версии с таймштампами помещаются в папку .stversions",
|
||||
"Files are moved to date stamped versions in a .stversions folder when replaced or deleted by Syncthing.": "Файлы с временнОй меткой версии помещаются в папку .stversions при их замене или удалении Syncthing.",
|
||||
"Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.": "Файлы защищены от изменений сделанных на других устройствах, но изменения сделанные на этом устройстве будут отправлены всему кластеру.",
|
||||
"Folder": "Папка",
|
||||
@@ -97,7 +97,7 @@
|
||||
"GUI Listen Addresses": "Адрес панели управления",
|
||||
"GUI Theme": "Тема оформления",
|
||||
"Generate": "Сгенерировать",
|
||||
"Global Changes": "Global Changes",
|
||||
"Global Changes": "Глобальные изменения",
|
||||
"Global Discovery": "Глобальное обнаружение",
|
||||
"Global Discovery Servers": "Серверы глобального обнаружения",
|
||||
"Global State": "Глобальное состояние",
|
||||
@@ -150,7 +150,7 @@
|
||||
"Override Changes": "Перезаписать изменения",
|
||||
"Path": "Путь",
|
||||
"Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for": "Путь к папке на локальном компьютере. Если её не существует, то она будет создана. Тильда (~) может использоваться как сокращение для",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions directory in the shared folder).": "Путь, в котором нужно хранить версии (оставьте пустым для папки по умолчанию .stversions внутри общей папки).",
|
||||
"Path where versions should be stored (leave empty for the default .stversions folder in the folder).": "Путь, где должны храниться версии (оставьте пустым, чтобы использовать папку по умолчанию .stversions внутри папки).",
|
||||
"Pause": "Пауза",
|
||||
"Pause All": "Приостановить все",
|
||||
@@ -247,7 +247,7 @@
|
||||
"This Device": "Это устройство",
|
||||
"This can easily give hackers access to read and change any files on your computer.": "Это может дать доступ хакерам для чтения и изменения любых файлов на вашем компьютере.",
|
||||
"This is a major version upgrade.": "Это обновление основной версии продукта.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "This setting controls the free space required on the home (i.e., index database) disk.",
|
||||
"This setting controls the free space required on the home (i.e., index database) disk.": "Эта настройка управляет свободным местом, необходимым на домашнем диске (например, для базы индексов).",
|
||||
"Time": "Время",
|
||||
"Trash Can File Versioning": "Использовать версионность для файлов в Корзине",
|
||||
"Type": "Тип",
|
||||
@@ -266,14 +266,14 @@
|
||||
"Version": "Версия",
|
||||
"Versions Path": "Путь к версиям",
|
||||
"Versions are automatically deleted if they are older than the maximum age or exceed the number of files allowed in an interval.": "Версии удаляются автоматически, если они существуют дольше максимального срока или превышают разрешённое количество файлов за интервал.",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Warning, this path is a parent directory of an existing folder \"{{otherFolder}}\".",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a parent directory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolder%}\".": "Внимание! Этот путь — родительская директория уже существующей папки «{{otherFolder}}».",
|
||||
"Warning, this path is a parent directory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Внимание! Этот путь — родительская директория уже существующей папки «{{otherFolderLabel}}» ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolder%}\".": "Осторожно, этот путь является подкаталогом существующей папки «{{otherFolder}}».",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Warning, this path is a subdirectory of an existing folder \"{{otherFolderLabel}}\" ({{otherFolder}}).",
|
||||
"Warning, this path is a subdirectory of an existing folder \"{%otherFolderLabel%}\" ({%otherFolder%}).": "Внимание! Этот путь — поддиректория уже существующей папки «{{otherFolderLabel}}» ({{otherFolder}}).",
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "Когда добавляете устройство, помните о том, что это же устройство должно быть добавлено и другой стороной.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "Когда добавляете новую папку, помните, что ID папок используются для того, чтобы связывать папки между всеми устройствами. Они чувствительны к регистру и должны совпадать на всех используемых устройствах.",
|
||||
"Yes": "Да",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can also select one of these nearby devices:": "Вы можете выбрать из этих устройств рядом:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Выбор можно изменить в любой момент в диалоге настроек.",
|
||||
"You can read more about the two release channels at the link below.": "О двух каналах выпусков можно почитать подробнее по нижеприведённой ссылке.",
|
||||
"You must keep at least one version.": "Вы должны хранить как минимум одну версию.",
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
"When adding a new device, keep in mind that this device must be added on the other side too.": "När du lägger till en ny enhet, kom ihåg att den här enheten måste läggas till på den andra enheten också.",
|
||||
"When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.": "När du lägger till ny mapp, tänk på att mapp-ID knyter ihop mappar mellan olika enheter. De skiftlägeskänsliga och måste matcha precis mellan alla enheter.",
|
||||
"Yes": "Ja",
|
||||
"You can also select one of these nearby devices:": "You can also select one of these nearby devices:",
|
||||
"You can also select one of these nearby devices:": "Du kan också välja en av dessa närliggande enheter:",
|
||||
"You can change your choice at any time in the Settings dialog.": "Du kan ändra ditt val när som helst i inställningsdialogrutan.",
|
||||
"You can read more about the two release channels at the link below.": "Du kan läsa mer om de två publiceringsskanalerna på länken nedan.",
|
||||
"You must keep at least one version.": "Du måste behålla åtminstone en version.",
|
||||
|
||||
@@ -564,7 +564,7 @@
|
||||
<span ng-switch="deviceStatus(deviceCfg)" class="pull-right text-{{deviceClass(deviceCfg)}}">
|
||||
<span ng-switch-when="insync"><span class="hidden-xs" translate>Up to Date</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="syncing">
|
||||
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | number:0}}%)
|
||||
<span class="hidden-xs" translate>Syncing</span> ({{completion[deviceCfg.deviceID]._total | number:0}}%, {{completion[deviceCfg.deviceID]._needBytes | binary}}B)
|
||||
</span>
|
||||
<span ng-switch-when="paused"><span class="hidden-xs" translate>Paused</span><span class="visible-xs">◼</span></span>
|
||||
<span ng-switch-when="disconnected"><span class="hidden-xs" translate>Disconnected</span><span class="visible-xs">◼</span></span>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<p translate>Copyright © 2014-2017 the following Contributors:</p>
|
||||
<div class="row">
|
||||
<div class="col-md-12" id="contributor-list">
|
||||
Jakob Borg, Audrius Butkevicius, Alexander Graf, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Simon Frei, Stefan Tatschner, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Alexandre Viau, Andrew Dunham, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Heiko Zuerker, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jochen Voss, Johan Vromans, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Niels Peter Roest, Pascal Jungblut, Peter Hoeg, Phill Luby, Piotr Bejda, Robert Carosi, Roman Zaynetdinov, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Tim Abell, Tim Howes, Tobias Nygren, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Wulf Weich, Xavier O., Yannic A.
|
||||
Jakob Borg, Audrius Butkevicius, Alexander Graf, Anderson Mesquita, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Simon Frei, Stefan Tatschner, Aaron Bieber, Adam Piggott, Adel Qalieh, Alessandro G., Alexandre Viau, Andrew Dunham, Andrey D, Antoine Lamielle, Arthur Axel fREW Schmidt, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benny Ng, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chris Howie, Chris Joel, Colin Kennedy, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Dominik Heidler, Elias Jarlebring, Emil Hessman, Erik Meitner, Federico Castagnini, Felix Ableitner, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gilli Sigurdsson, Heiko Zuerker, Jaakko Hannikainen, Jacek Szafarkiewicz, Jake Peterson, James Patterson, Jaroslav Malec, Jaya Chithra, Jens Diemer, Jochen Voss, Johan Vromans, Karol Różycki, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin White, Jr., Kurt Fitzner, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Mark Pulford, Mateusz Naściszewski, Matt Burke, Max Schulze, Michael Jephcote, Michael Tilli, Niels Peter Roest, Pascal Jungblut, Peter Hoeg, Phill Luby, Piotr Bejda, Robert Carosi, Roman Zaynetdinov, Sacheendra Talluri, Scott Klupfel, Stefan Kuntz, Suhas Gundimeda, Tim Abell, Tim Howes, Tobias Nygren, Tomas Cerveny, Tully Robinson, Tyler Brazier, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, William A. Kennington III, Wulf Weich, Xavier O., Yannic A.
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
|
||||
@@ -232,7 +232,8 @@ angular.module('syncthing.core')
|
||||
address: arg.data.addr
|
||||
};
|
||||
$scope.completion[arg.data.id] = {
|
||||
_total: 100
|
||||
_total: 100,
|
||||
_needBytes: 0
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -378,7 +379,8 @@ angular.module('syncthing.core')
|
||||
$scope.devices = $scope.config.devices;
|
||||
$scope.devices.forEach(function (deviceCfg) {
|
||||
$scope.completion[deviceCfg.deviceID] = {
|
||||
_total: 100
|
||||
_total: 100,
|
||||
_needBytes: 0
|
||||
};
|
||||
});
|
||||
$scope.devices.sort(deviceCompare);
|
||||
@@ -463,7 +465,7 @@ angular.module('syncthing.core')
|
||||
function recalcCompletion(device) {
|
||||
var total = 0, needed = 0, deletes = 0;
|
||||
for (var folder in $scope.completion[device]) {
|
||||
if (folder === "_total") {
|
||||
if (folder === "_total" || folder === '_needBytes') {
|
||||
continue;
|
||||
}
|
||||
total += $scope.completion[device][folder].globalBytes;
|
||||
@@ -472,8 +474,10 @@ angular.module('syncthing.core')
|
||||
}
|
||||
if (total == 0) {
|
||||
$scope.completion[device]._total = 100;
|
||||
$scope.completion[device]._needBytes = 0;
|
||||
} else {
|
||||
$scope.completion[device]._total = 100 * (1 - needed / total);
|
||||
$scope.completion[device]._needBytes = needed
|
||||
}
|
||||
|
||||
if (needed == 0 && deletes > 0) {
|
||||
@@ -481,6 +485,7 @@ angular.module('syncthing.core')
|
||||
// to do. Drop down the completion percentage to indicate
|
||||
// that we have stuff to do.
|
||||
$scope.completion[device]._total = 95;
|
||||
$scope.completion[device]._needBytes = 0;
|
||||
}
|
||||
|
||||
console.log("recalcCompletion", device, $scope.completion[device]);
|
||||
|
||||
@@ -77,8 +77,8 @@ type ChangeDetector interface {
|
||||
}
|
||||
|
||||
type Matcher struct {
|
||||
lines []string
|
||||
patterns []Pattern
|
||||
lines []string // exact lines read from .stignore
|
||||
patterns []Pattern // patterns including those from included files
|
||||
withCache bool
|
||||
matches *cache
|
||||
curHash string
|
||||
@@ -386,11 +386,10 @@ func parseIgnoreFile(fd io.Reader, currentFile string, cd ChangeDetector) ([]str
|
||||
} else if strings.HasPrefix(line, "#include ") {
|
||||
includeRel := line[len("#include "):]
|
||||
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
|
||||
includeLines, includePatterns, err := loadIgnoreFile(includeFile, cd)
|
||||
_, includePatterns, err := loadIgnoreFile(includeFile, cd)
|
||||
if err != nil {
|
||||
return fmt.Errorf("include of %q: %v", includeRel, err)
|
||||
}
|
||||
lines = append(lines, includeLines...)
|
||||
patterns = append(patterns, includePatterns...)
|
||||
} else {
|
||||
// Path name or pattern, add it so it matches files both in
|
||||
|
||||
@@ -872,3 +872,38 @@ func TestRoot(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLines(t *testing.T) {
|
||||
stignore := `
|
||||
#include testdata/excludes
|
||||
|
||||
!/a
|
||||
/*
|
||||
`
|
||||
|
||||
pats := New(WithCache(true))
|
||||
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedLines := []string{
|
||||
"",
|
||||
"#include testdata/excludes",
|
||||
"",
|
||||
"!/a",
|
||||
"/*",
|
||||
"",
|
||||
}
|
||||
|
||||
lines := pats.Lines()
|
||||
if len(lines) != len(expectedLines) {
|
||||
t.Fatalf("len(Lines()) == %d, expected %d", len(lines), len(expectedLines))
|
||||
}
|
||||
for i := range lines {
|
||||
if lines[i] != expectedLines[i] {
|
||||
t.Fatalf("Lines()[%d] == %s, expected %s", i, lines[i], expectedLines[i])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -51,11 +51,14 @@ func NewStaggered(folderID, folderPath string, params map[string]string) Version
|
||||
// Use custom path if set, otherwise .stversions in folderPath
|
||||
var versionsDir string
|
||||
if params["versionsPath"] == "" {
|
||||
l.Debugln("using default dir .stversions")
|
||||
versionsDir = filepath.Join(folderPath, ".stversions")
|
||||
} else {
|
||||
l.Debugln("using default dir .stversions")
|
||||
} else if filepath.IsAbs(params["versionsPath"]) {
|
||||
l.Debugln("using dir", params["versionsPath"])
|
||||
versionsDir = params["versionsPath"]
|
||||
} else {
|
||||
versionsDir = filepath.Join(folderPath, params["versionsPath"])
|
||||
l.Debugln("using dir", versionsDir)
|
||||
}
|
||||
|
||||
s := &Staggered{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STDISCOSRV" "1" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "STDISCOSRV" "1" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
stdiscosrv \- Syncthing Discovery Server
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "STRELAYSRV" "1" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "STRELAYSRV" "1" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
strelaysrv \- Syncthing Relay Server
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-BEP" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-BEP" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-bep \- Block Exchange Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-CONFIG" "5" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-CONFIG" "5" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-config \- Syncthing Configuration
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-DEVICE-IDS" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-device-ids \- Understanding Device IDs
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-EVENT-API" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-EVENT-API" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-event-api \- Event API
|
||||
.
|
||||
@@ -37,7 +37,7 @@ core utility towards a GUI.
|
||||
.sp
|
||||
To receive events, perform a HTTP GET of \fB/rest/events\fP or
|
||||
\fB/rest/events/disk\fP\&. The latter returns only local\-change\-detected and
|
||||
local\-change\-detected events, the former all other events.
|
||||
remote\-change\-detected events, the former all other events.
|
||||
.sp
|
||||
The optional parameter \fBsince=<lastSeenID>\fP sets the ID of the last event
|
||||
you\(aqve already seen. Syncthing returns a JSON encoded array of event objects,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-FAQ" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-FAQ" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-faq \- Frequently Asked Questions
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-GLOBALDISCO" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-globaldisco \- Global Discovery Protocol v3
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-LOCALDISCO" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-localdisco \- Local Discovery Protocol v4
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-NETWORKING" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-NETWORKING" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-networking \- Firewall Setup
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-RELAY" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-RELAY" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-relay \- Relay Protocol v1
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-REST-API" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-REST-API" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-rest-api \- REST API
|
||||
.
|
||||
@@ -885,7 +885,7 @@ core utility towards a GUI.
|
||||
.sp
|
||||
To receive events, perform a HTTP GET of \fB/rest/events\fP or
|
||||
\fB/rest/events/disk\fP\&. The latter returns only local\-change\-detected and
|
||||
local\-change\-detected events, the former all other events.
|
||||
remote\-change\-detected events, the former all other events.
|
||||
.sp
|
||||
The optional parameter \fBsince=<lastSeenID>\fP sets the ID of the last event
|
||||
you\(aqve already seen. Syncthing returns a JSON encoded array of event objects,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-SECURITY" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-SECURITY" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-security \- Security Principles
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-STIGNORE" "5" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-STIGNORE" "5" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-stignore \- Prevent files from being synchronized to other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING-VERSIONING" "7" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING-VERSIONING" "7" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
|
||||
.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Man page generated from reStructuredText.
|
||||
.
|
||||
.TH "SYNCTHING" "1" "June 23, 2017" "v0.14" "Syncthing"
|
||||
.TH "SYNCTHING" "1" "July 03, 2017" "v0.14" "Syncthing"
|
||||
.SH NAME
|
||||
syncthing \- Syncthing
|
||||
.
|
||||
|
||||
3
meta/README.txt
Normal file
3
meta/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
The files in this directory contain metadata tests - that is, tests on the
|
||||
shape and colour of the code in the rest of the repository. This code is not
|
||||
compiled into the final product.
|
||||
@@ -4,19 +4,16 @@
|
||||
// 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/.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Checks for authors that are not mentioned in AUTHORS
|
||||
package main
|
||||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// list of commits that we don't include in our checks; because they are
|
||||
@@ -37,34 +34,36 @@ var excludeCommits = stringSetFromStrings([]string{
|
||||
"bcc5d7c00f52552303b463d43a636f27b7f7e19b",
|
||||
})
|
||||
|
||||
func init() {
|
||||
log.SetOutput(os.Stdout)
|
||||
log.SetFlags(0)
|
||||
}
|
||||
func TestCheckAuthors(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping slow test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
actual := actualAuthorEmails("cmd/", "lib/", "gui/", "test/", "script/")
|
||||
listed := listedAuthorEmails()
|
||||
actual, hashes := actualAuthorEmails(t, ".", "../cmd/", "../lib/", "../gui/", "../test/", "../script/")
|
||||
listed := listedAuthorEmails(t)
|
||||
missing := actual.except(listed)
|
||||
if len(missing) > 0 {
|
||||
log.Println("Missing authors:")
|
||||
for author := range missing {
|
||||
log.Println(" ", author)
|
||||
for author := range missing {
|
||||
t.Logf("Missing author: %s", author)
|
||||
for _, hash := range hashes[author] {
|
||||
t.Logf(" in hash: %s", hash)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
if len(missing) > 0 {
|
||||
t.Errorf("Missing %d author(s)", len(missing))
|
||||
}
|
||||
}
|
||||
|
||||
// actualAuthorEmails returns the set of author emails found in the actual git
|
||||
// commit log, except those in excluded commits.
|
||||
func actualAuthorEmails(paths ...string) stringSet {
|
||||
func actualAuthorEmails(t *testing.T, paths ...string) (stringSet, map[string][]string) {
|
||||
args := append([]string{"log", "--format=%H %ae"}, paths...)
|
||||
cmd := exec.Command("git", args...)
|
||||
bs, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatal("authorEmails:", err)
|
||||
t.Fatal("authorEmails:", err)
|
||||
}
|
||||
|
||||
hashes := make(map[string][]string)
|
||||
authors := newStringSet()
|
||||
for _, line := range bytes.Split(bs, []byte{'\n'}) {
|
||||
fields := strings.Fields(string(line))
|
||||
@@ -77,21 +76,22 @@ func actualAuthorEmails(paths ...string) stringSet {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(body(hash)), "skip-check: authors") {
|
||||
if strings.Contains(strings.ToLower(body(t, hash)), "skip-check: authors") {
|
||||
continue
|
||||
}
|
||||
|
||||
authors.add(author)
|
||||
hashes[author] = append(hashes[author], hash)
|
||||
}
|
||||
|
||||
return authors
|
||||
return authors, hashes
|
||||
}
|
||||
|
||||
// listedAuthorEmails returns the set of author emails mentioned in AUTHORS
|
||||
func listedAuthorEmails() stringSet {
|
||||
bs, err := ioutil.ReadFile("AUTHORS")
|
||||
func listedAuthorEmails(t *testing.T) stringSet {
|
||||
bs, err := ioutil.ReadFile("../AUTHORS")
|
||||
if err != nil {
|
||||
log.Fatal("listedAuthorEmails:", err)
|
||||
t.Fatal("listedAuthorEmails:", err)
|
||||
}
|
||||
|
||||
emailRe := regexp.MustCompile(`<([^>]+)>`)
|
||||
@@ -104,11 +104,11 @@ func listedAuthorEmails() stringSet {
|
||||
return authors
|
||||
}
|
||||
|
||||
func body(hash string) string {
|
||||
func body(t *testing.T, hash string) string {
|
||||
cmd := exec.Command("git", "show", "--pretty=format:%b", "-s", hash)
|
||||
bs, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatal("body:", err)
|
||||
t.Fatal("body:", err)
|
||||
}
|
||||
return string(bs)
|
||||
}
|
||||
@@ -4,26 +4,27 @@
|
||||
// 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/.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Checks for files missing copyright notice
|
||||
package main
|
||||
package meta
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// File extensions to check
|
||||
var checkExts = map[string]bool{
|
||||
var copyrightCheckExts = map[string]bool{
|
||||
".go": true,
|
||||
}
|
||||
|
||||
// Directories to search
|
||||
var copyrightCheckDirs = []string{".", "../cmd", "../lib", "../test", "../script"}
|
||||
|
||||
// Valid copyright headers, searched for in the top five lines in each file.
|
||||
var copyrightRegexps = []string{
|
||||
`Copyright`,
|
||||
@@ -34,13 +35,11 @@ var copyrightRegexps = []string{
|
||||
|
||||
var copyrightRe = regexp.MustCompile(strings.Join(copyrightRegexps, "|"))
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
for _, dir := range flag.Args() {
|
||||
func TestCheckCopyright(t *testing.T) {
|
||||
for _, dir := range copyrightCheckDirs {
|
||||
err := filepath.Walk(dir, checkCopyright)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +51,7 @@ func checkCopyright(path string, info os.FileInfo, err error) error {
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
if !checkExts[filepath.Ext(path)] {
|
||||
if !copyrightCheckExts[filepath.Ext(path)] {
|
||||
return nil
|
||||
}
|
||||
|
||||
45
meta/gofmt_test.go
Normal file
45
meta/gofmt_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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/.
|
||||
|
||||
// Checks for authors that are not mentioned in AUTHORS
|
||||
package meta
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var gofmtCheckDirs = []string{".", "../cmd", "../lib", "../test", "../script"}
|
||||
|
||||
func TestCheckGoFmt(t *testing.T) {
|
||||
for _, dir := range gofmtCheckDirs {
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
if filepath.Ext(path) != ".go" {
|
||||
return nil
|
||||
}
|
||||
cmd := exec.Command("gofmt", "-s", "-d", path)
|
||||
bs, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(bs) != 0 {
|
||||
t.Errorf("File %s is not formatted correctly:\n\n%s", path, string(bs))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
118
meta/metalint_test.go
Normal file
118
meta/metalint_test.go
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (C) 2017 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
package meta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
// fast linters complete in a fraction of a second and might as well be
|
||||
// run always as part of the build
|
||||
fastLinters = []string{
|
||||
"deadcode",
|
||||
"golint",
|
||||
"ineffassign",
|
||||
"vet",
|
||||
}
|
||||
|
||||
// slow linters take several seconds and are run only as part of the
|
||||
// "metalint" command.
|
||||
slowLinters = []string{
|
||||
"gosimple",
|
||||
"staticcheck",
|
||||
"structcheck",
|
||||
"unused",
|
||||
"varcheck",
|
||||
}
|
||||
|
||||
// Which parts of the tree to lint
|
||||
lintDirs = []string{
|
||||
".",
|
||||
"../cmd/...",
|
||||
"../lib/...",
|
||||
"../script/...",
|
||||
}
|
||||
|
||||
// Messages to ignore
|
||||
lintExcludes = []string{
|
||||
".pb.go",
|
||||
"should have comment",
|
||||
"protocol.Vector composite literal uses unkeyed fields",
|
||||
"cli.Requires composite literal uses unkeyed fields",
|
||||
"Use DialContext instead", // Go 1.7
|
||||
"os.SEEK_SET is deprecated", // Go 1.7
|
||||
"SA4017", // staticcheck "is a pure function but its return value is ignored"
|
||||
}
|
||||
)
|
||||
|
||||
func TestCheckMetalint(t *testing.T) {
|
||||
if !isGometalinterInstalled() {
|
||||
return
|
||||
}
|
||||
|
||||
gometalinter(t, lintDirs, lintExcludes...)
|
||||
}
|
||||
|
||||
func isGometalinterInstalled() bool {
|
||||
if _, err := runError("gometalinter", "--disable-all"); err != nil {
|
||||
log.Println("gometalinter is not installed")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func gometalinter(t *testing.T, dirs []string, excludes ...string) bool {
|
||||
params := []string{"--disable-all", "--concurrency=2", "--deadline=300s"}
|
||||
|
||||
for _, linter := range fastLinters {
|
||||
params = append(params, "--enable="+linter)
|
||||
}
|
||||
|
||||
if !testing.Short() {
|
||||
for _, linter := range slowLinters {
|
||||
params = append(params, "--enable="+linter)
|
||||
}
|
||||
}
|
||||
|
||||
for _, exclude := range excludes {
|
||||
params = append(params, "--exclude="+exclude)
|
||||
}
|
||||
|
||||
params = append(params, dirs...)
|
||||
|
||||
bs, _ := runError("gometalinter", params...)
|
||||
|
||||
nerr := 0
|
||||
lines := make(map[string]struct{})
|
||||
for _, line := range strings.Split(string(bs), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := lines[line]; ok {
|
||||
continue
|
||||
}
|
||||
log.Println(line)
|
||||
if strings.Contains(line, "executable file not found") {
|
||||
log.Println(` - Try "go run build.go setup" to install missing tools`)
|
||||
}
|
||||
lines[line] = struct{}{}
|
||||
nerr++
|
||||
}
|
||||
|
||||
return nerr == 0
|
||||
}
|
||||
|
||||
func runError(cmd string, args ...string) ([]byte, error) {
|
||||
ecmd := exec.Command(cmd, args...)
|
||||
bs, err := ecmd.CombinedOutput()
|
||||
return bytes.TrimSpace(bs), err
|
||||
}
|
||||
@@ -108,6 +108,9 @@ func runError(cmd string, args ...string) ([]byte, error) {
|
||||
|
||||
func githubIssueTitle(n int) (string, error) {
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.github.com/repos/syncthing/syncthing/issues/%d", n), nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
user, token := os.Getenv("GITHUB_USERNAME"), os.Getenv("GITHUB_TOKEN")
|
||||
if user != "" && token != "" {
|
||||
|
||||
Reference in New Issue
Block a user