diff --git a/go.mod b/go.mod index 9179ec6a..38ea5bce 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/cenkalti/backoff v2.2.1+incompatible github.com/go-git/go-git/v5 v5.6.0 github.com/heroku/heroku-go/v5 v5.5.0 - github.com/otiai10/copy v1.9.0 + github.com/otiai10/copy v1.14.1 github.com/spf13/cobra v1.9.1 ) @@ -26,6 +26,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/otiai10/mint v1.6.3 // indirect github.com/pborman/uuid v1.2.1 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/sergi/go-diff v1.1.0 // indirect @@ -35,6 +36,7 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/net v0.38.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.31.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 3cc9818f..84e6fe16 100644 --- a/go.sum +++ b/go.sum @@ -68,13 +68,10 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/otiai10/copy v1.9.0 h1:7KFNiCgZ91Ru4qW4CWPf/7jqtxLagGRmIxWldPP9VY4= -github.com/otiai10/copy v1.9.0/go.mod h1:hsfX19wcn0UWIHUQ3/4fHuehhk2UyArQ9dVFAn3FczI= -github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= -github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= -github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.4.0 h1:umwcf7gbpEwf7WFzqmWwSv0CzbeMsae2u9ZvpP8j2q4= -github.com/otiai10/mint v1.4.0/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= +github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= +github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= +github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= +github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -126,6 +123,8 @@ golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/otiai10/copy/README.md b/vendor/github.com/otiai10/copy/README.md index 32edfd8f..9f3dce74 100644 --- a/vendor/github.com/otiai10/copy/README.md +++ b/vendor/github.com/otiai10/copy/README.md @@ -10,6 +10,8 @@ [![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/otiai10/copy?sort=semver)](https://pkg.go.dev/github.com/otiai10/copy) [![Docker Test](https://github.com/otiai10/copy/actions/workflows/docker-test.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/docker-test.yml) [![Vagrant Test](https://github.com/otiai10/copy/actions/workflows/vagrant-test.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/vagrant-test.yml) +[![GopherJS](https://github.com/otiai10/copy/actions/workflows/gopherjs.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/gopherjs.yml) +[![Go WASM](https://github.com/otiai10/copy/actions/workflows/wasm.yml/badge.svg)](https://github.com/otiai10/copy/actions/workflows/wasm.yml) `copy` copies directories recursively. @@ -41,9 +43,16 @@ type Options struct { // OnDirExists can specify what to do when there is a directory already existing in destination. OnDirExists func(src, dest string) DirExistsAction + // OnError can let users decide how to handle errors (e.g., you can suppress specific error). + OnError func(src, dest, string, err error) error + // Skip can specify which files should be skipped Skip func(srcinfo os.FileInfo, src, dest string) (bool, error) + // RenameDestination can rename destination. + // If not set, nil, it does nothing. + RenameDestination func(src, dest string) (string, error) + // PermissionControl can control permission of // every entry. // When you want to add permission 0222, do like @@ -74,6 +83,28 @@ type Options struct { // If zero, the internal default buffer of 32KB is used. // See https://golang.org/pkg/io/#CopyBuffer for more information. CopyBufferSize uint + + // If you want to add some limitation on reading src file, + // you can wrap the src and provide new reader, + // such as `RateLimitReader` in the test case. + WrapReader func(src io.Reader) io.Reader + + // If given, copy.Copy refers to this fs.FS instead of the OS filesystem. + // e.g., You can use embed.FS to copy files from embedded filesystem. + FS fs.FS + + // NumOfWorkers represents the number of workers used for + // concurrent copying contents of directories. + // If 0 or 1, it does not use goroutine for copying directories. + // Please refer to https://pkg.go.dev/golang.org/x/sync/semaphore for more details. + NumOfWorkers int64 + + // PreferConcurrent is a function to determine whether or not + // to use goroutine for copying contents of directories. + // If PreferConcurrent is nil, which is default, it does concurrent + // copying for all directories. + // If NumOfWorkers is 0 or 1, this function will be ignored. + PreferConcurrent func(srcdir, destdir string) (bool, error) } ``` @@ -93,4 +124,4 @@ err := Copy("your/directory", "your/directory.copy", opt) ## License -[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fcopy.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fcopy?ref=badge_large) \ No newline at end of file +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fcopy.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fcopy?ref=badge_large) diff --git a/vendor/github.com/otiai10/copy/copy.go b/vendor/github.com/otiai10/copy/copy.go index 60643dd4..f9787cd9 100644 --- a/vendor/github.com/otiai10/copy/copy.go +++ b/vendor/github.com/otiai10/copy/copy.go @@ -1,11 +1,15 @@ package copy import ( + "context" "io" - "io/ioutil" + "io/fs" "os" "path/filepath" "time" + + "golang.org/x/sync/errgroup" + "golang.org/x/sync/semaphore" ) type timespec struct { @@ -15,20 +19,37 @@ type timespec struct { } // Copy copies src to dest, doesn't matter if src is a directory or a file. -func Copy(src, dest string, opt ...Options) error { +func Copy(src, dest string, opts ...Options) error { + opt := assureOptions(src, dest, opts...) + if opt.NumOfWorkers > 1 { + opt.intent.sem = semaphore.NewWeighted(opt.NumOfWorkers) + opt.intent.ctx = context.Background() + } + if opt.FS != nil { + info, err := fs.Stat(opt.FS, src) + if err != nil { + return onError(src, dest, err, opt) + } + return switchboard(src, dest, info, opt) + } info, err := os.Lstat(src) if err != nil { - return err + return onError(src, dest, err, opt) } - return switchboard(src, dest, info, assureOptions(src, dest, opt...)) + return switchboard(src, dest, info, opt) } // switchboard switches proper copy functions regarding file type, etc... // If there would be anything else here, add a case to this switchboard. func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) { - if info.Mode()&os.ModeDevice != 0 && !opt.Specials { - return err + return onError(src, dest, err, opt) + } + + if opt.RenameDestination != nil { + if dest, err = opt.RenameDestination(src, dest); err != nil { + return onError(src, dest, err, opt) + } } switch { @@ -42,7 +63,7 @@ func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) { err = fcopy(src, dest, info, opt) } - return err + return onError(src, dest, err, opt) } // copyNextOrSkip decide if this src should be copied or not. @@ -66,6 +87,20 @@ func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error { // and file permission. func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { + var readcloser io.ReadCloser + if opt.FS != nil { + readcloser, err = opt.FS.Open(src) + } else { + readcloser, err = os.Open(src) + } + if err != nil { + if os.IsNotExist(err) { + return nil + } + return + } + defer fclose(readcloser, &err) + if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil { return } @@ -82,18 +117,12 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { } chmodfunc(&err) - s, err := os.Open(src) - if err != nil { - return - } - defer fclose(s, &err) - var buf []byte = nil var w io.Writer = f - var r io.Reader = s + var r io.Reader = readcloser if opt.WrapReader != nil { - r = opt.WrapReader(s) + r = opt.WrapReader(r) } if opt.CopyBufferSize != 0 { @@ -130,7 +159,6 @@ func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) { // with scanning contents inside the directory // and pass everything to "copy" recursively. func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { - if skip, err := onDirExists(opt, srcdir, destdir); err != nil { return err } else if skip { @@ -144,17 +172,40 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { } defer chmodfunc(&err) - contents, err := ioutil.ReadDir(srcdir) - if err != nil { - return + var entries []fs.DirEntry + if opt.FS != nil { + entries, err = fs.ReadDir(opt.FS, srcdir) + if err != nil { + return err + } + } else { + entries, err = os.ReadDir(srcdir) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } } - for _, content := range contents { - cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name()) + contents := make([]fs.FileInfo, 0, len(entries)) + for _, e := range entries { + info, err := e.Info() + if err != nil { + return err + } + contents = append(contents, info) + } - if err = copyNextOrSkip(cs, cd, content, opt); err != nil { - // If any error, exit immediately - return + if yes, err := shouldCopyDirectoryConcurrent(opt, srcdir, destdir); err != nil { + return err + } else if yes { + if err := dcopyConcurrent(srcdir, destdir, contents, opt); err != nil { + return err + } + } else { + if err := dcopySequential(srcdir, destdir, contents, opt); err != nil { + return err } } @@ -173,6 +224,42 @@ func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) { return } +func dcopySequential(srcdir, destdir string, contents []os.FileInfo, opt Options) error { + for _, content := range contents { + cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name()) + + if err := copyNextOrSkip(cs, cd, content, opt); err != nil { + // If any error, exit immediately + return err + } + } + return nil +} + +// Copy this directory concurrently regarding semaphore of opt.intent +func dcopyConcurrent(srcdir, destdir string, contents []os.FileInfo, opt Options) error { + group, ctx := errgroup.WithContext(opt.intent.ctx) + getRoutine := func(cs, cd string, content os.FileInfo) func() error { + return func() error { + if content.IsDir() { + return copyNextOrSkip(cs, cd, content, opt) + } + if err := opt.intent.sem.Acquire(ctx, 1); err != nil { + return err + } + err := copyNextOrSkip(cs, cd, content, opt) + opt.intent.sem.Release(1) + return err + } + } + for _, content := range contents { + csd := filepath.Join(srcdir, content.Name()) + cdd := filepath.Join(destdir, content.Name()) + group.Go(getRoutine(csd, cdd, content)) + } + return group.Wait() +} + func onDirExists(opt Options, srcdir, destdir string) (bool, error) { _, err := os.Stat(destdir) if err == nil && opt.OnDirExists != nil && destdir != opt.intent.dest { @@ -205,6 +292,10 @@ func onsymlink(src, dest string, opt Options) error { if err != nil { return err } + if !filepath.IsAbs(orig) { + // orig is a relative link: need to add src dir to orig + orig = filepath.Join(filepath.Dir(src), orig) + } info, err := os.Lstat(orig) if err != nil { return err @@ -220,18 +311,42 @@ func onsymlink(src, dest string, opt Options) error { // lcopy is for a symlink, // with just creating a new symlink by replicating src symlink. func lcopy(src, dest string) error { - src, err := os.Readlink(src) + orig, err := os.Readlink(src) + // @See https://github.com/otiai10/copy/issues/111 + // TODO: This might be controlled by Options in the future. if err != nil { + if os.IsNotExist(err) { // Copy symlink even if not existing + return os.Symlink(src, dest) + } return err } - return os.Symlink(src, dest) + + // @See https://github.com/otiai10/copy/issues/132 + // TODO: Control by SymlinkExistsAction + if _, err := os.Lstat(dest); err == nil { + if err := os.Remove(dest); err != nil { + return err + } + } + + return os.Symlink(orig, dest) } // fclose ANYHOW closes file, -// with asiging error raised during Close, +// with assigning error raised during Close, // BUT respecting the error already reported. -func fclose(f *os.File, reported *error) { +func fclose(f io.Closer, reported *error) { if err := f.Close(); *reported == nil { *reported = err } } + +// onError lets caller to handle errors +// occurred when copying a file. +func onError(src, dest string, err error, opt Options) error { + if opt.OnError == nil { + return err + } + + return opt.OnError(src, dest, err) +} diff --git a/vendor/github.com/otiai10/copy/copy_namedpipes.go b/vendor/github.com/otiai10/copy/copy_namedpipes.go index 615ddcd5..657fb381 100644 --- a/vendor/github.com/otiai10/copy/copy_namedpipes.go +++ b/vendor/github.com/otiai10/copy/copy_namedpipes.go @@ -1,5 +1,4 @@ //go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js -// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris,!js package copy diff --git a/vendor/github.com/otiai10/copy/copy_namedpipes_x.go b/vendor/github.com/otiai10/copy/copy_namedpipes_x.go index 38dd9dc7..da3d6f79 100644 --- a/vendor/github.com/otiai10/copy/copy_namedpipes_x.go +++ b/vendor/github.com/otiai10/copy/copy_namedpipes_x.go @@ -1,5 +1,4 @@ //go:build windows || plan9 || netbsd || aix || illumos || solaris || js -// +build windows plan9 netbsd aix illumos solaris js package copy diff --git a/vendor/github.com/otiai10/copy/fileinfo_go1.15.go b/vendor/github.com/otiai10/copy/fileinfo_go1.15.go deleted file mode 100644 index c0708eaf..00000000 --- a/vendor/github.com/otiai10/copy/fileinfo_go1.15.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build !go1.16 -// +build !go1.16 - -package copy - -import "os" - -// This is a cloned definition of os.FileInfo (go1.15) or fs.FileInfo (go1.16~) -// A FileInfo describes a file and is returned by Stat. -type fileInfo interface { - // Name() string // base name of the file - // Size() int64 // length in bytes for regular files; system-dependent for others - Mode() os.FileMode // file mode bits - // ModTime() time.Time // modification time - IsDir() bool // abbreviation for Mode().IsDir() - Sys() interface{} // underlying data source (can return nil) -} diff --git a/vendor/github.com/otiai10/copy/fileinfo_go1.16.go b/vendor/github.com/otiai10/copy/fileinfo_go1.16.go deleted file mode 100644 index 01b3fd24..00000000 --- a/vendor/github.com/otiai10/copy/fileinfo_go1.16.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build go1.16 -// +build go1.16 - -package copy - -import "io/fs" - -// This is a cloned definition of os.FileInfo (go1.15) or fs.FileInfo (go1.16~) -// A FileInfo describes a file and is returned by Stat. -type fileInfo interface { - // Name() string // base name of the file - // Size() int64 // length in bytes for regular files; system-dependent for others - Mode() fs.FileMode // file mode bits - // ModTime() time.Time // modification time - IsDir() bool // abbreviation for Mode().IsDir() - Sys() interface{} // underlying data source (can return nil) -} diff --git a/vendor/github.com/otiai10/copy/options.go b/vendor/github.com/otiai10/copy/options.go index bd18d562..c1db48c8 100644 --- a/vendor/github.com/otiai10/copy/options.go +++ b/vendor/github.com/otiai10/copy/options.go @@ -1,8 +1,12 @@ package copy import ( + "context" "io" + "io/fs" "os" + + "golang.org/x/sync/semaphore" ) // Options specifies optional actions on copying. @@ -14,9 +18,15 @@ type Options struct { // OnDirExists can specify what to do when there is a directory already existing in destination. OnDirExists func(src, dest string) DirExistsAction + // OnErr lets called decide whether or not to continue on particular copy error. + OnError func(src, dest string, err error) error + // Skip can specify which files should be skipped Skip func(srcinfo os.FileInfo, src, dest string) (bool, error) + // RenameDestination can specify the destination file or dir name if needed to rename. + RenameDestination func(src, dest string) (string, error) + // Specials includes special files to be copied. default false. Specials bool @@ -55,12 +65,34 @@ type Options struct { // If you want to add some limitation on reading src file, // you can wrap the src and provide new reader, // such as `RateLimitReader` in the test case. - WrapReader func(src *os.File) io.Reader + WrapReader func(src io.Reader) io.Reader + + // If given, copy.Copy refers to this fs.FS instead of the OS filesystem. + // e.g., You can use embed.FS to copy files from embedded filesystem. + FS fs.FS + + // NumOfWorkers represents the number of workers used for + // concurrent copying contents of directories. + // If 0 or 1, it does not use goroutine for copying directories. + // Please refer to https://pkg.go.dev/golang.org/x/sync/semaphore for more details. + NumOfWorkers int64 + + // PreferConcurrent is a function to determine whether or not + // to use goroutine for copying contents of directories. + // If PreferConcurrent is nil, which is default, it does concurrent + // copying for all directories. + // If NumOfWorkers is 0 or 1, this function will be ignored. + PreferConcurrent func(srcdir, destdir string) (bool, error) + + // Internal use only + intent intent +} - intent struct { - src string - dest string - } +type intent struct { + src string + dest string + sem *semaphore.Weighted + ctx context.Context } // SymlinkAction represents what to do on symlink. @@ -95,6 +127,7 @@ func getDefaultOptions(src, dest string) Options { return Shallow // Do shallow copy }, OnDirExists: nil, // Default behavior is "Merge". + OnError: nil, // Default is "accept error" Skip: nil, // Do not skip anything AddPermission: 0, // Add nothing PermissionControl: PerservePermission, // Just preserve permission @@ -103,10 +136,7 @@ func getDefaultOptions(src, dest string) Options { PreserveTimes: false, // Do not preserve the modification time CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024) WrapReader: nil, // Do not wrap src files, use them as they are. - intent: struct { - src string - dest string - }{src, dest}, + intent: intent{src, dest, nil, nil}, } } @@ -132,3 +162,13 @@ func assureOptions(src, dest string, opts ...Options) Options { opts[0].intent.dest = defopt.intent.dest return opts[0] } + +func shouldCopyDirectoryConcurrent(opt Options, srcdir, destdir string) (bool, error) { + if opt.NumOfWorkers <= 1 { + return false, nil + } + if opt.PreferConcurrent == nil { + return true, nil + } + return opt.PreferConcurrent(srcdir, destdir) +} diff --git a/vendor/github.com/otiai10/copy/permission_control.go b/vendor/github.com/otiai10/copy/permission_control.go index 97ae12d8..901a8451 100644 --- a/vendor/github.com/otiai10/copy/permission_control.go +++ b/vendor/github.com/otiai10/copy/permission_control.go @@ -1,6 +1,7 @@ package copy import ( + "io/fs" "os" ) @@ -11,11 +12,11 @@ const ( tmpPermissionForDirectory = os.FileMode(0755) ) -type PermissionControlFunc func(srcinfo fileInfo, dest string) (chmodfunc func(*error), err error) +type PermissionControlFunc func(srcinfo fs.FileInfo, dest string) (chmodfunc func(*error), err error) var ( AddPermission = func(perm os.FileMode) PermissionControlFunc { - return func(srcinfo fileInfo, dest string) (func(*error), error) { + return func(srcinfo fs.FileInfo, dest string) (func(*error), error) { orig := srcinfo.Mode() if srcinfo.IsDir() { if err := os.MkdirAll(dest, tmpPermissionForDirectory); err != nil { @@ -28,7 +29,7 @@ var ( } } PerservePermission PermissionControlFunc = AddPermission(0) - DoNothing PermissionControlFunc = func(srcinfo fileInfo, dest string) (func(*error), error) { + DoNothing PermissionControlFunc = func(srcinfo fs.FileInfo, dest string) (func(*error), error) { if srcinfo.IsDir() { if err := os.MkdirAll(dest, srcinfo.Mode()); err != nil { return func(*error) {}, err @@ -39,7 +40,7 @@ var ( ) // chmod ANYHOW changes file mode, -// with asiging error raised during Chmod, +// with assigning error raised during Chmod, // BUT respecting the error already reported. func chmod(dir string, mode os.FileMode, reported *error) { if err := os.Chmod(dir, mode); *reported == nil { diff --git a/vendor/github.com/otiai10/copy/preserve_ltimes.go b/vendor/github.com/otiai10/copy/preserve_ltimes.go index cc006d37..6b6787b2 100644 --- a/vendor/github.com/otiai10/copy/preserve_ltimes.go +++ b/vendor/github.com/otiai10/copy/preserve_ltimes.go @@ -1,5 +1,4 @@ //go:build !windows && !plan9 && !js -// +build !windows,!plan9,!js package copy diff --git a/vendor/github.com/otiai10/copy/preserve_ltimes_x.go b/vendor/github.com/otiai10/copy/preserve_ltimes_x.go index 02aec40b..5ef234d5 100644 --- a/vendor/github.com/otiai10/copy/preserve_ltimes_x.go +++ b/vendor/github.com/otiai10/copy/preserve_ltimes_x.go @@ -1,5 +1,4 @@ //go:build windows || js || plan9 -// +build windows js plan9 package copy diff --git a/vendor/github.com/otiai10/copy/preserve_owner.go b/vendor/github.com/otiai10/copy/preserve_owner.go index 13ec4f57..bd129644 100644 --- a/vendor/github.com/otiai10/copy/preserve_owner.go +++ b/vendor/github.com/otiai10/copy/preserve_owner.go @@ -1,14 +1,14 @@ //go:build !windows && !plan9 -// +build !windows,!plan9 package copy import ( + "io/fs" "os" "syscall" ) -func preserveOwner(src, dest string, info fileInfo) (err error) { +func preserveOwner(src, dest string, info fs.FileInfo) (err error) { if info == nil { if info, err = os.Stat(src); err != nil { return err diff --git a/vendor/github.com/otiai10/copy/preserve_owner_x.go b/vendor/github.com/otiai10/copy/preserve_owner_x.go index 9d825740..1e8f1251 100644 --- a/vendor/github.com/otiai10/copy/preserve_owner_x.go +++ b/vendor/github.com/otiai10/copy/preserve_owner_x.go @@ -1,8 +1,9 @@ //go:build windows || plan9 -// +build windows plan9 package copy -func preserveOwner(src, dest string, info fileInfo) (err error) { +import "io/fs" + +func preserveOwner(src, dest string, info fs.FileInfo) (err error) { return nil } diff --git a/vendor/github.com/otiai10/copy/stat_times.go b/vendor/github.com/otiai10/copy/stat_times.go index 75f45f6e..49ea67c2 100644 --- a/vendor/github.com/otiai10/copy/stat_times.go +++ b/vendor/github.com/otiai10/copy/stat_times.go @@ -1,5 +1,4 @@ //go:build !windows && !darwin && !freebsd && !plan9 && !netbsd && !js -// +build !windows,!darwin,!freebsd,!plan9,!netbsd,!js // TODO: add more runtimes diff --git a/vendor/github.com/otiai10/copy/stat_times_darwin.go b/vendor/github.com/otiai10/copy/stat_times_darwin.go index d4c23d8e..935ce1d7 100644 --- a/vendor/github.com/otiai10/copy/stat_times_darwin.go +++ b/vendor/github.com/otiai10/copy/stat_times_darwin.go @@ -1,5 +1,4 @@ //go:build darwin -// +build darwin package copy diff --git a/vendor/github.com/otiai10/copy/stat_times_freebsd.go b/vendor/github.com/otiai10/copy/stat_times_freebsd.go index 5309334e..1deb1cc4 100644 --- a/vendor/github.com/otiai10/copy/stat_times_freebsd.go +++ b/vendor/github.com/otiai10/copy/stat_times_freebsd.go @@ -1,5 +1,4 @@ //go:build freebsd -// +build freebsd package copy diff --git a/vendor/github.com/otiai10/copy/stat_times_js.go b/vendor/github.com/otiai10/copy/stat_times_js.go index c645771c..a4b1e288 100644 --- a/vendor/github.com/otiai10/copy/stat_times_js.go +++ b/vendor/github.com/otiai10/copy/stat_times_js.go @@ -1,5 +1,4 @@ //go:build js -// +build js package copy diff --git a/vendor/github.com/otiai10/copy/stat_times_windows.go b/vendor/github.com/otiai10/copy/stat_times_windows.go index d6a84a76..babfe7d9 100644 --- a/vendor/github.com/otiai10/copy/stat_times_windows.go +++ b/vendor/github.com/otiai10/copy/stat_times_windows.go @@ -1,5 +1,4 @@ //go:build windows -// +build windows package copy diff --git a/vendor/github.com/otiai10/copy/stat_times_x.go b/vendor/github.com/otiai10/copy/stat_times_x.go index 886ddd3f..53da32e2 100644 --- a/vendor/github.com/otiai10/copy/stat_times_x.go +++ b/vendor/github.com/otiai10/copy/stat_times_x.go @@ -1,5 +1,4 @@ //go:build plan9 || netbsd -// +build plan9 netbsd package copy diff --git a/vendor/github.com/otiai10/copy/symlink_test_x.go b/vendor/github.com/otiai10/copy/symlink_test_x.go new file mode 100644 index 00000000..1f6bb1f4 --- /dev/null +++ b/vendor/github.com/otiai10/copy/symlink_test_x.go @@ -0,0 +1,45 @@ +//go:build windows || plan9 || netbsd || aix || illumos || solaris || js + +package copy + +import ( + "os" + "testing" + + . "github.com/otiai10/mint" +) + +func TestOptions_OnSymlink(t *testing.T) { + opt := Options{OnSymlink: func(string) SymlinkAction { return Deep }} + err := Copy("test/data/case03", "test/data.copy/case03.deep", opt) + Expect(t, err).ToBe(nil) + info, err := os.Lstat("test/data.copy/case03.deep/case01") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).ToBe(os.FileMode(0)) + + opt = Options{OnSymlink: func(string) SymlinkAction { return Shallow }} + err = Copy("test/data/case03", "test/data.copy/case03.shallow", opt) + Expect(t, err).ToBe(nil) + info, err = os.Lstat("test/data.copy/case03.shallow/case01") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) + + opt = Options{OnSymlink: func(string) SymlinkAction { return Skip }} + err = Copy("test/data/case03", "test/data.copy/case03.skip", opt) + Expect(t, err).ToBe(nil) + _, err = os.Stat("test/data.copy/case03.skip/case01") + Expect(t, os.IsNotExist(err)).ToBe(true) + + err = Copy("test/data/case03", "test/data.copy/case03.default") + Expect(t, err).ToBe(nil) + info, err = os.Lstat("test/data.copy/case03.default/case01") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) + + opt = Options{OnSymlink: nil} + err = Copy("test/data/case03", "test/data.copy/case03.not-specified", opt) + Expect(t, err).ToBe(nil) + info, err = os.Lstat("test/data.copy/case03.not-specified/case01") + Expect(t, err).ToBe(nil) + Expect(t, info.Mode()&os.ModeSymlink).Not().ToBe(os.FileMode(0)) +} diff --git a/vendor/github.com/otiai10/copy/test_setup.go b/vendor/github.com/otiai10/copy/test_setup.go deleted file mode 100644 index f3c2d83c..00000000 --- a/vendor/github.com/otiai10/copy/test_setup.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js -// +build !windows,!plan9,!netbsd,!aix,!illumos,!solaris,!js - -package copy - -import ( - "os" - "syscall" - "testing" -) - -func setup(m *testing.M) { - os.RemoveAll("test/data.copy") - os.MkdirAll("test/data.copy", os.ModePerm) - os.Symlink("test/data/case01", "test/data/case03/case01") - os.Chmod("test/data/case07/dir_0555", 0o555) - os.Chmod("test/data/case07/file_0444", 0o444) - syscall.Mkfifo("test/data/case11/foo/bar", 0o555) -} diff --git a/vendor/github.com/otiai10/copy/test_setup_x.go b/vendor/github.com/otiai10/copy/test_setup_x.go deleted file mode 100644 index fc56b732..00000000 --- a/vendor/github.com/otiai10/copy/test_setup_x.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build windows || plan9 || netbsd || aix || illumos || solaris || js -// +build windows plan9 netbsd aix illumos solaris js - -package copy - -import ( - "os" - "testing" -) - -func setup(m *testing.M) { - os.MkdirAll("test/data.copy", os.ModePerm) - os.Symlink("test/data/case01", "test/data/case03/case01") - os.Chmod("test/data/case07/dir_0555", 0555) - os.Chmod("test/data/case07/file_0444", 0444) -} diff --git a/vendor/github.com/otiai10/mint/.gitignore b/vendor/github.com/otiai10/mint/.gitignore new file mode 100644 index 00000000..6ae51791 --- /dev/null +++ b/vendor/github.com/otiai10/mint/.gitignore @@ -0,0 +1,2 @@ +coverage.txt +vendor diff --git a/vendor/github.com/otiai10/mint/LICENSE b/vendor/github.com/otiai10/mint/LICENSE new file mode 100644 index 00000000..a5bad7fc --- /dev/null +++ b/vendor/github.com/otiai10/mint/LICENSE @@ -0,0 +1,7 @@ +Copyright 2017 otiai10 (Hiromu OCHIAI) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/otiai10/mint/README.md b/vendor/github.com/otiai10/mint/README.md new file mode 100644 index 00000000..06caae03 --- /dev/null +++ b/vendor/github.com/otiai10/mint/README.md @@ -0,0 +1,62 @@ +# mint + +[![Go](https://github.com/otiai10/mint/actions/workflows/go.yml/badge.svg)](https://github.com/otiai10/mint/actions/workflows/go.yml) +[![codecov](https://codecov.io/gh/otiai10/mint/branch/master/graph/badge.svg)](https://codecov.io/gh/otiai10/mint) +[![Go Report Card](https://goreportcard.com/badge/github.com/otiai10/mint)](https://goreportcard.com/report/github.com/otiai10/mint) +[![GoDoc](https://godoc.org/github.com/otiai10/mint?status.png)](https://godoc.org/github.com/otiai10/mint) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/otiai10/mint?sort=semver)](https://pkg.go.dev/github.com/otiai10/mint) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fmint.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fmint?ref=badge_shield) + +The very minimum assertion for Go. + +```go +package your_test + +import ( + "testing" + "pkg/your" + . "github.com/otiai10/mint" +) + +func TestFoo(t *testing.T) { + + foo := your.Foo() + Expect(t, foo).ToBe(1234) + Expect(t, foo).TypeOf("int") + Expect(t, foo).Not().ToBe(nil) + Expect(t, func() { yourFunc() }).Exit(1) + + // If assertion failed, exit 1 with message. + Expect(t, foo).ToBe("foobarbuz") + + // You can run assertions without os.Exit + res := Expect(t, foo).Dry().ToBe("bar") + // res.OK() == false + + // You can omit repeated `t`. + m := mint.Blend(t) + m.Expect(foo).ToBe(1234) +} +``` + +# features + +- Simple syntax +- Loosely coupled +- Plain implementation + +# tests +``` +go test ./... +``` + +# use cases + +Projects bellow use `mint` + +- [github.com/otiai10/gosseract](https://github.com/otiai10/gosseract/blob/master/all_test.go) +- [github.com/otiai10/marmoset](https://github.com/otiai10/marmoset/blob/master/all_test.go#L168-L190) + + +## License +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fotiai10%2Fmint.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fotiai10%2Fmint?ref=badge_large) \ No newline at end of file diff --git a/vendor/github.com/otiai10/mint/because.go b/vendor/github.com/otiai10/mint/because.go new file mode 100644 index 00000000..6d496cee --- /dev/null +++ b/vendor/github.com/otiai10/mint/because.go @@ -0,0 +1,15 @@ +package mint + +import "testing" + +// Because is context printer. +func Because(t *testing.T, context string, wrapper func(*testing.T)) { + Log(" Because ", context, "\n") + wrapper(t) +} + +// When is an alternative of `Because` +func When(t *testing.T, context string, wrapper func(*testing.T)) { + Log(" When ", context, "\n") + wrapper(t) +} diff --git a/vendor/github.com/otiai10/mint/comparer.go b/vendor/github.com/otiai10/mint/comparer.go new file mode 100644 index 00000000..d543eb6d --- /dev/null +++ b/vendor/github.com/otiai10/mint/comparer.go @@ -0,0 +1,53 @@ +package mint + +import ( + "fmt" + "reflect" +) + +func getComparer(a, b interface{}, deeply bool) Comparer { + if deeply { + return deepComparer{} + } + switch reflect.ValueOf(a).Kind() { + case reflect.Slice: + return sliceComparer{} + case reflect.Map: + return mapComparer{} + } + if b == nil { + return nilComparer{} + } + return defaultComparer{} +} + +type Comparer interface { + Compare(a, b interface{}) bool +} + +type defaultComparer struct{} + +func (c defaultComparer) Compare(a, b interface{}) bool { + return a == b +} + +type deepComparer struct{} + +func (c deepComparer) Compare(a, b interface{}) bool { + return reflect.DeepEqual(a, b) +} + +type mapComparer struct { + deepComparer +} + +type sliceComparer struct { + deepComparer +} + +type nilComparer struct { +} + +func (c nilComparer) Compare(a, _ interface{}) bool { + return fmt.Sprintf("%v", a) == fmt.Sprintf("%v", nil) +} diff --git a/vendor/github.com/otiai10/mint/exit.go b/vendor/github.com/otiai10/mint/exit.go new file mode 100644 index 00000000..fc64ac96 --- /dev/null +++ b/vendor/github.com/otiai10/mint/exit.go @@ -0,0 +1,41 @@ +//go:build !freebsd +// +build !freebsd + +package mint + +// On "freebsd/FreeBSD-10.4-STABLE" OS image, +// Go installed by `pkg install` might NOT have `syscall.Mprotect` +// causing such error: "bou.ke/monkey/replace_unix.go:13:10: undefined: syscall.Mprotect". +// See https://www.freebsd.org/cgi/man.cgi?sektion=2&query=mprotect +// TODO: Fix the image for https://github.com/otiai10/gosseract/blob/master/test/runtimes/freebsd.Vagrantfile#L4 +/* + * "bou.ke/monkey" + */ // FIXME: Now I remove this library because of LICENSE problem +// See https://github.com/otiai10/copy/issues/12 as well + +// Exit ... +func (testee *Testee) Exit(expectedCode int) MintResult { + + panic("`mint.Testee.Exit` method is temporarily deprecated.") + + /* + fun, ok := testee.actual.(func()) + if !ok { + panic("mint error: Exit only can be called for func type value") + } + + var actualCode int + patch := monkey.Patch(os.Exit, func(code int) { + actualCode = code + }) + fun() + patch.Unpatch() + + testee.actual = actualCode + if judge(actualCode, expectedCode, testee.not, testee.deeply) { + return testee.result + } + testee.expected = expectedCode + return testee.failed(failExitCode) + */ +} diff --git a/vendor/github.com/otiai10/mint/exit_freebsd.go b/vendor/github.com/otiai10/mint/exit_freebsd.go new file mode 100644 index 00000000..d5eed6cf --- /dev/null +++ b/vendor/github.com/otiai10/mint/exit_freebsd.go @@ -0,0 +1,10 @@ +//go:build freebsd +// +build freebsd + +package mint + +// Exit ... +func (testee *Testee) Exit(expectedCode int) MintResult { + panic("Exit method can NOT be used on FreeBSD, for now.") + return MintResult{ok: false} +} diff --git a/vendor/github.com/otiai10/mint/log.go b/vendor/github.com/otiai10/mint/log.go new file mode 100644 index 00000000..6aa8f8dc --- /dev/null +++ b/vendor/github.com/otiai10/mint/log.go @@ -0,0 +1,15 @@ +package mint + +import ( + "fmt" + "os" +) + +// Log only output if -v flag is given. +// This is because the standard "t.Testing.Log" method decorates +// its caller: runtime.Caller(3) automatically. +func Log(args ...interface{}) { + if isVerbose(os.Args) { + fmt.Print(args...) + } +} diff --git a/vendor/github.com/otiai10/mint/mint.go b/vendor/github.com/otiai10/mint/mint.go new file mode 100644 index 00000000..a37e3c1d --- /dev/null +++ b/vendor/github.com/otiai10/mint/mint.go @@ -0,0 +1,86 @@ +package mint + +import ( + "os" + "testing" +) + +// Mint (mint.Mint) is wrapper for *testing.T +// blending testing type to omit repeated `t`. +type Mint struct { + t *testing.T +} + +var ( + failToBe = 0 + failType = 1 + failIn = 2 + failToMatch = 3 + failExitCode = 4 + scolds = map[int]string{ + failToBe: "%s:%d\n\tExpected %sto be\t`%+v`\n\tBut actual\t`%+v`", + failType: "%s:%d\n\tExpected %stype\t`%+v`\n\tBut actual\t`%T`", + failIn: "%s:%d\n\tExpected %sis in\t`%v`\n\tbut it's not", + failToMatch: "%s:%d\n\tExpected %v to match\t`%s`\n\tBut actual\t`%+v`", + failExitCode: "%s:%d\n\tExpected %sto exit with code `%d`\n\tBut actual\t`%d`", + } +) +var ( + redB = "\033[1;31m" + reset = "\033[0m" + colorize = map[string]func(string) string{ + "red": func(v string) string { + return redB + v + reset + }, + } +) + +// Blend provides (blended) *mint.Mint. +// You can save writing "t" repeatedly. +func Blend(t *testing.T) *Mint { + return &Mint{ + t, + } +} + +// Expect provides "*Testee". +// The blended mint is merely a proxy to instantiate testee. +func (m *Mint) Expect(actual interface{}) *Testee { + return expect(m.t, actual) +} + +// Expect provides "*mint.Testee". +// It has assertion methods such as "ToBe". +func Expect(t *testing.T, actual interface{}) *Testee { + return expect(t, actual) +} + +func expect(t *testing.T, actual interface{}) *Testee { + return &Testee{t: t, actual: actual, verbose: isVerbose(os.Args), result: MintResult{ok: true}} +} + +// Require provides "*mint.Testee", +// which stops execution of goroutine when the assertion failed. +func Require(t *testing.T, actual interface{}) *Testee { + return require(t, actual) +} + +func require(t *testing.T, actual interface{}) *Testee { + return &Testee{t: t, actual: actual, verbose: isVerbose(os.Args), required: true, result: MintResult{ok: true}} +} + +func isVerbose(flags []string) bool { + for _, f := range flags { + if f == "-test.v=true" { + return true + } + } + return false +} +func judge(a, b interface{}, not, deeply bool) bool { + comparer := getComparer(a, b, deeply) + if not { + return !comparer.Compare(a, b) + } + return comparer.Compare(a, b) +} diff --git a/vendor/github.com/otiai10/mint/mocks.go b/vendor/github.com/otiai10/mint/mocks.go new file mode 100644 index 00000000..87feab49 --- /dev/null +++ b/vendor/github.com/otiai10/mint/mocks.go @@ -0,0 +1,30 @@ +package mint + +import ( + "bytes" + "io/ioutil" + "net/http" +) + +type HTTPClientMock struct { + HTTPError error + ResponseStatusCode int + ResponseBody string +} + +func (hcm *HTTPClientMock) Handle() (res *http.Response, err error, ok bool) { + if hcm.HTTPError != nil { + err = hcm.HTTPError + ok = true + } + res = new(http.Response) + if hcm.ResponseBody != "" { + res.Body = ioutil.NopCloser(bytes.NewBufferString(hcm.ResponseBody)) + ok = true + } + if hcm.ResponseStatusCode != 0 { + res.StatusCode = hcm.ResponseStatusCode + ok = true + } + return res, err, ok +} diff --git a/vendor/github.com/otiai10/mint/mquery/README.md b/vendor/github.com/otiai10/mint/mquery/README.md new file mode 100644 index 00000000..4992930d --- /dev/null +++ b/vendor/github.com/otiai10/mint/mquery/README.md @@ -0,0 +1,31 @@ +mquery +=== + +```go +import mquery + +var m = map[string]interface{}{ + "foo": "bar", + "hoge": map[string]interface{}{ + "name": "otiai10", + }, + "fuga": map[int]map[string]interface{}{ + 0: {"greet": "Hello"}, + 1: {"greet": "こんにちは"}, + }, + "langs": []string{"Go", "JavaScript", "English"}, + "baz": nil, + "required": false, +} + +func main() { + fmt.Println( + Query(m, "foo"), // "bar" + Query(m, "hoge.name"), // "otiai10" + Query(m, "fuga.0.greet"), // "Hello" + Query(m, "langs.2"), // "English" + Query(m, "required"), // false + Query(m, "baz.biz"), // nil + ) +} +``` \ No newline at end of file diff --git a/vendor/github.com/otiai10/mint/mquery/mquery.go b/vendor/github.com/otiai10/mint/mquery/mquery.go new file mode 100644 index 00000000..2a7ddbac --- /dev/null +++ b/vendor/github.com/otiai10/mint/mquery/mquery.go @@ -0,0 +1,72 @@ +package mquery + +import ( + "fmt" + "reflect" + "strconv" + "strings" +) + +func Query(m interface{}, q string) interface{} { + return query(m, strings.Split(q, ".")) +} + +func query(m interface{}, qs []string) interface{} { + t := reflect.TypeOf(m) + switch t.Kind() { + case reflect.Map: + return queryMap(m, t, qs) + case reflect.Slice: + return querySlice(m, t, qs) + default: + return m + } +} + +func queryMap(m interface{}, t reflect.Type, qs []string) interface{} { + if len(qs) == 0 { + return m + } + val := reflect.ValueOf(m) + if val.IsZero() { + return nil + } + switch t.Key().Kind() { + case reflect.String: + val := reflect.ValueOf(m).MapIndex(reflect.ValueOf(qs[0])) + if !val.IsValid() { + return nil + } + return query(val.Interface(), qs[1:]) + case reflect.Int: + i, err := strconv.Atoi(qs[0]) + if err != nil { + return fmt.Errorf("cannot access map with keyword: %s: %v", qs[0], err) + } + val := reflect.ValueOf(m).MapIndex(reflect.ValueOf(i)) + if !val.IsValid() { + return nil + } + return query(val.Interface(), qs[1:]) + } + return nil +} + +func querySlice(m interface{}, t reflect.Type, qs []string) interface{} { + if len(qs) == 0 { + return m + } + v := reflect.ValueOf(m) + if v.Len() == 0 { + return nil + } + i, err := strconv.Atoi(qs[0]) + if err != nil { + return fmt.Errorf("cannot access slice with keyword: %s: %v", qs[0], err) + } + if v.Len() <= i { + return nil + } + next := v.Index(i).Interface() + return query(next, qs[1:]) +} diff --git a/vendor/github.com/otiai10/mint/result.go b/vendor/github.com/otiai10/mint/result.go new file mode 100644 index 00000000..2ce38c05 --- /dev/null +++ b/vendor/github.com/otiai10/mint/result.go @@ -0,0 +1,23 @@ +package mint + +// MintResult provide the results of assertion +// for `Dry` option. +type MintResult struct { + ok bool + message string +} + +// OK returns whether result is ok or not. +func (r MintResult) OK() bool { + return r.ok +} + +// NG is the opposite alias for OK(). +func (r MintResult) NG() bool { + return !r.ok +} + +// Message returns failure message. +func (r MintResult) Message() string { + return r.message +} diff --git a/vendor/github.com/otiai10/mint/testee.go b/vendor/github.com/otiai10/mint/testee.go new file mode 100644 index 00000000..90537fc3 --- /dev/null +++ b/vendor/github.com/otiai10/mint/testee.go @@ -0,0 +1,145 @@ +package mint + +import ( + "fmt" + "path/filepath" + "reflect" + "regexp" + "runtime" + "testing" + + "github.com/otiai10/mint/mquery" +) + +// Testee is holder of interfaces which user want to assert +// and also has its result. +type Testee struct { + t *testing.T + actual interface{} + expected interface{} + dry bool + not bool + deeply bool + result MintResult + required bool + verbose bool + + // origin string // Only used when querying +} + +// Query queries the actual value with given query string. +func (testee *Testee) Query(query string) *Testee { + // testee.origin = fmt.Sprintf("%T", testee.actual) + testee.actual = mquery.Query(testee.actual, query) + return testee +} + +// ToBe can assert the testee to equal the parameter of this func. +// OS will exit with code 1, when the assertion fail. +// If you don't want to exit, see "Dry()". +func (testee *Testee) ToBe(expected interface{}) MintResult { + if judge(testee.actual, expected, testee.not, testee.deeply) { + return testee.result + } + testee.expected = expected + return testee.failed(failToBe) +} + +// Match can assert the testee to match with specified regular expression. +// It uses `regexp.MustCompile`, it's due to caller to make sure it's valid regexp. +// OS will exit with code 1, when the assertion fail. +// If you don't want to exit, see "Dry()". +func (testee *Testee) Match(expression string) MintResult { + exp := regexp.MustCompile(expression) + matched := exp.MatchString(fmt.Sprintf("%v", testee.actual)) + if judge(matched, true, testee.not, testee.deeply) { + return testee.result + } + testee.expected = expression + return testee.failed(failToMatch) +} + +// In can assert the testee is in given array. +func (testee *Testee) In(expecteds ...interface{}) MintResult { + for _, expected := range expecteds { + if judge(testee.actual, expected, testee.not, testee.deeply) { + return testee.result + } + } + testee.expected = expecteds + return testee.failed(failIn) +} + +// TypeOf can assert the type of testee to equal the parameter of this func. +// OS will exit with code 1, when the assertion fail. +// If you don't want to exit, see "Dry()". +func (testee *Testee) TypeOf(typeName string) MintResult { + if judge(reflect.TypeOf(testee.actual).String(), typeName, testee.not, testee.deeply) { + return testee.result + } + testee.expected = typeName + return testee.failed(failType) +} + +// Not makes following assertion conversed. +func (testee *Testee) Not() *Testee { + testee.not = true + return testee +} + +// Dry makes the testee NOT to call "Fail()". +// Use this if you want to fail test in a purpose. +func (testee *Testee) Dry() *Testee { + testee.dry = true + return testee +} + +// Deeply makes following assertions use `reflect.DeepEqual`. +// You had better use this to compare reference type objects. +func (testee *Testee) Deeply() *Testee { + testee.deeply = true + return testee +} + +func (testee *Testee) failed(failure int) MintResult { + message := testee.toText(failure) + testee.result.ok = false + testee.result.message = message + if !testee.dry { + fmt.Println(colorize["red"](message)) + if testee.required { + testee.t.FailNow() + } else { + testee.t.Fail() + } + } + return testee.result +} + +func (testee *Testee) toText(fail int) string { + not := "" + if testee.not { + not = "NOT " + } + _, file, line, _ := runtime.Caller(3) + // if testee.origin != "" { + // testee.origin = fmt.Sprintf("(queried from %s)", testee.origin) + // } + return fmt.Sprintf( + scolds[fail], + filepath.Base(file), line, + not, + testee.expected, + testee.actual, + ) +} + +// Log only output if -v flag is given. +// This is because the standard "t.Testing.Log" method decorates +// its caller: runtime.Caller(3) automatically. +func (testee *Testee) Log(args ...interface{}) { + if !testee.verbose { + return + } + fmt.Print(args...) +} diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 00000000..2a7cf70d --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright 2009 The Go Authors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 00000000..73309904 --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 00000000..948a3ee6 --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,135 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +// +// [errgroup.Group] is related to [sync.WaitGroup] but adds handling of tasks +// returning errors. +package errgroup + +import ( + "context" + "fmt" + "sync" +) + +type token struct{} + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid, has no limit on the number of active goroutines, +// and does not cancel on error. +type Group struct { + cancel func(error) + + wg sync.WaitGroup + + sem chan token + + errOnce sync.Once + err error +} + +func (g *Group) done() { + if g.sem != nil { + <-g.sem + } + g.wg.Done() +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := withCancelCause(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel(g.err) + } + return g.err +} + +// Go calls the given function in a new goroutine. +// It blocks until the new goroutine can be added without the number of +// active goroutines in the group exceeding the configured limit. +// +// The first call to return a non-nil error cancels the group's context, if the +// group was created by calling WithContext. The error will be returned by Wait. +func (g *Group) Go(f func() error) { + if g.sem != nil { + g.sem <- token{} + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel(g.err) + } + }) + } + }() +} + +// TryGo calls the given function in a new goroutine only if the number of +// active goroutines in the group is currently below the configured limit. +// +// The return value reports whether the goroutine was started. +func (g *Group) TryGo(f func() error) bool { + if g.sem != nil { + select { + case g.sem <- token{}: + // Note: this allows barging iff channels in general allow barging. + default: + return false + } + } + + g.wg.Add(1) + go func() { + defer g.done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel(g.err) + } + }) + } + }() + return true +} + +// SetLimit limits the number of active goroutines in this group to at most n. +// A negative value indicates no limit. +// +// Any subsequent call to the Go method will block until it can add an active +// goroutine without exceeding the configured limit. +// +// The limit must not be modified while any goroutines in the group are active. +func (g *Group) SetLimit(n int) { + if n < 0 { + g.sem = nil + return + } + if len(g.sem) != 0 { + panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem))) + } + g.sem = make(chan token, n) +} diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go new file mode 100644 index 00000000..f93c740b --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/go120.go @@ -0,0 +1,13 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build go1.20 + +package errgroup + +import "context" + +func withCancelCause(parent context.Context) (context.Context, func(error)) { + return context.WithCancelCause(parent) +} diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go new file mode 100644 index 00000000..88ce3343 --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/pre_go120.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !go1.20 + +package errgroup + +import "context" + +func withCancelCause(parent context.Context) (context.Context, func(error)) { + ctx, cancel := context.WithCancel(parent) + return ctx, func(error) { cancel() } +} diff --git a/vendor/golang.org/x/sync/semaphore/semaphore.go b/vendor/golang.org/x/sync/semaphore/semaphore.go new file mode 100644 index 00000000..b618162a --- /dev/null +++ b/vendor/golang.org/x/sync/semaphore/semaphore.go @@ -0,0 +1,160 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package semaphore provides a weighted semaphore implementation. +package semaphore // import "golang.org/x/sync/semaphore" + +import ( + "container/list" + "context" + "sync" +) + +type waiter struct { + n int64 + ready chan<- struct{} // Closed when semaphore acquired. +} + +// NewWeighted creates a new weighted semaphore with the given +// maximum combined weight for concurrent access. +func NewWeighted(n int64) *Weighted { + w := &Weighted{size: n} + return w +} + +// Weighted provides a way to bound concurrent access to a resource. +// The callers can request access with a given weight. +type Weighted struct { + size int64 + cur int64 + mu sync.Mutex + waiters list.List +} + +// Acquire acquires the semaphore with a weight of n, blocking until resources +// are available or ctx is done. On success, returns nil. On failure, returns +// ctx.Err() and leaves the semaphore unchanged. +func (s *Weighted) Acquire(ctx context.Context, n int64) error { + done := ctx.Done() + + s.mu.Lock() + select { + case <-done: + // ctx becoming done has "happened before" acquiring the semaphore, + // whether it became done before the call began or while we were + // waiting for the mutex. We prefer to fail even if we could acquire + // the mutex without blocking. + s.mu.Unlock() + return ctx.Err() + default: + } + if s.size-s.cur >= n && s.waiters.Len() == 0 { + // Since we hold s.mu and haven't synchronized since checking done, if + // ctx becomes done before we return here, it becoming done must have + // "happened concurrently" with this call - it cannot "happen before" + // we return in this branch. So, we're ok to always acquire here. + s.cur += n + s.mu.Unlock() + return nil + } + + if n > s.size { + // Don't make other Acquire calls block on one that's doomed to fail. + s.mu.Unlock() + <-done + return ctx.Err() + } + + ready := make(chan struct{}) + w := waiter{n: n, ready: ready} + elem := s.waiters.PushBack(w) + s.mu.Unlock() + + select { + case <-done: + s.mu.Lock() + select { + case <-ready: + // Acquired the semaphore after we were canceled. + // Pretend we didn't and put the tokens back. + s.cur -= n + s.notifyWaiters() + default: + isFront := s.waiters.Front() == elem + s.waiters.Remove(elem) + // If we're at the front and there're extra tokens left, notify other waiters. + if isFront && s.size > s.cur { + s.notifyWaiters() + } + } + s.mu.Unlock() + return ctx.Err() + + case <-ready: + // Acquired the semaphore. Check that ctx isn't already done. + // We check the done channel instead of calling ctx.Err because we + // already have the channel, and ctx.Err is O(n) with the nesting + // depth of ctx. + select { + case <-done: + s.Release(n) + return ctx.Err() + default: + } + return nil + } +} + +// TryAcquire acquires the semaphore with a weight of n without blocking. +// On success, returns true. On failure, returns false and leaves the semaphore unchanged. +func (s *Weighted) TryAcquire(n int64) bool { + s.mu.Lock() + success := s.size-s.cur >= n && s.waiters.Len() == 0 + if success { + s.cur += n + } + s.mu.Unlock() + return success +} + +// Release releases the semaphore with a weight of n. +func (s *Weighted) Release(n int64) { + s.mu.Lock() + s.cur -= n + if s.cur < 0 { + s.mu.Unlock() + panic("semaphore: released more than held") + } + s.notifyWaiters() + s.mu.Unlock() +} + +func (s *Weighted) notifyWaiters() { + for { + next := s.waiters.Front() + if next == nil { + break // No more waiters blocked. + } + + w := next.Value.(waiter) + if s.size-s.cur < w.n { + // Not enough tokens for the next waiter. We could keep going (to try to + // find a waiter with a smaller request), but under load that could cause + // starvation for large requests; instead, we leave all remaining waiters + // blocked. + // + // Consider a semaphore used as a read-write lock, with N tokens, N + // readers, and one writer. Each reader can Acquire(1) to obtain a read + // lock. The writer can Acquire(N) to obtain a write lock, excluding all + // of the readers. If we allow the readers to jump ahead in the queue, + // the writer will starve — there is always one token available for every + // reader. + break + } + + s.cur += w.n + s.waiters.Remove(next) + close(w.ready) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d58fdb3e..bad479fc 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -137,9 +137,13 @@ github.com/jbenet/go-context/io # github.com/kevinburke/ssh_config v1.2.0 ## explicit github.com/kevinburke/ssh_config -# github.com/otiai10/copy v1.9.0 -## explicit; go 1.14 +# github.com/otiai10/copy v1.14.1 +## explicit; go 1.18 github.com/otiai10/copy +# github.com/otiai10/mint v1.6.3 +## explicit; go 1.18 +github.com/otiai10/mint +github.com/otiai10/mint/mquery # github.com/pborman/uuid v1.2.1 ## explicit github.com/pborman/uuid @@ -182,6 +186,10 @@ golang.org/x/crypto/ssh/knownhosts golang.org/x/net/context golang.org/x/net/internal/socks golang.org/x/net/proxy +# golang.org/x/sync v0.8.0 +## explicit; go 1.18 +golang.org/x/sync/errgroup +golang.org/x/sync/semaphore # golang.org/x/sys v0.31.0 ## explicit; go 1.23.0 golang.org/x/sys/cpu