github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/builder/remotecontext/lazycontext.go (about) 1 package remotecontext // import "github.com/Prakhar-Agarwal-byte/moby/builder/remotecontext" 2 3 import ( 4 "encoding/hex" 5 "os" 6 "path/filepath" 7 "runtime" 8 "strings" 9 10 "github.com/Prakhar-Agarwal-byte/moby/builder" 11 "github.com/Prakhar-Agarwal-byte/moby/pkg/pools" 12 "github.com/pkg/errors" 13 ) 14 15 // NewLazySource creates a new LazyContext. LazyContext defines a hashed build 16 // context based on a root directory. Individual files are hashed first time 17 // they are asked. It is not safe to call methods of LazyContext concurrently. 18 func NewLazySource(root string) (builder.Source, error) { 19 return &lazySource{ 20 root: root, 21 sums: make(map[string]string), 22 }, nil 23 } 24 25 type lazySource struct { 26 root string 27 sums map[string]string 28 } 29 30 func (c *lazySource) Root() string { 31 return c.root 32 } 33 34 func (c *lazySource) Close() error { 35 return nil 36 } 37 38 func (c *lazySource) Hash(path string) (string, error) { 39 cleanPath, fullPath, err := normalize(path, c.root) 40 if err != nil { 41 return "", err 42 } 43 44 relPath, err := Rel(c.root, fullPath) 45 if err != nil { 46 return "", errors.WithStack(convertPathError(err, cleanPath)) 47 } 48 49 fi, err := os.Lstat(fullPath) 50 if err != nil { 51 // Backwards compatibility: a missing file returns a path as hash. 52 // This is reached in the case of a broken symlink. 53 return relPath, nil 54 } 55 56 sum, ok := c.sums[relPath] 57 if !ok { 58 sum, err = c.prepareHash(relPath, fi) 59 if err != nil { 60 return "", err 61 } 62 } 63 64 return sum, nil 65 } 66 67 func (c *lazySource) prepareHash(relPath string, fi os.FileInfo) (string, error) { 68 p := filepath.Join(c.root, relPath) 69 h, err := NewFileHash(p, relPath, fi) 70 if err != nil { 71 return "", errors.Wrapf(err, "failed to create hash for %s", relPath) 72 } 73 if fi.Mode().IsRegular() && fi.Size() > 0 { 74 f, err := os.Open(p) 75 if err != nil { 76 return "", errors.Wrapf(err, "failed to open %s", relPath) 77 } 78 defer f.Close() 79 if _, err := pools.Copy(h, f); err != nil { 80 return "", errors.Wrapf(err, "failed to copy file data for %s", relPath) 81 } 82 } 83 sum := hex.EncodeToString(h.Sum(nil)) 84 c.sums[relPath] = sum 85 return sum, nil 86 } 87 88 // Rel makes a path relative to base path. Same as `filepath.Rel` but can also 89 // handle UUID paths in windows. 90 func Rel(basepath string, targpath string) (string, error) { 91 // filepath.Rel can't handle UUID paths in windows 92 if runtime.GOOS == "windows" { 93 pfx := basepath + `\` 94 if strings.HasPrefix(targpath, pfx) { 95 p := strings.TrimPrefix(targpath, pfx) 96 if p == "" { 97 p = "." 98 } 99 return p, nil 100 } 101 } 102 return filepath.Rel(basepath, targpath) 103 }