github.com/Benchkram/bob@v0.0.0-20220321080157-7c8f3876e225/bobtask/target/hash.go (about) 1 package target 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "errors" 7 "fmt" 8 "io/fs" 9 "os" 10 "path/filepath" 11 12 "github.com/Benchkram/bob/pkg/dockermobyutil" 13 "github.com/Benchkram/bob/pkg/usererror" 14 15 "github.com/Benchkram/bob/pkg/file" 16 "github.com/Benchkram/bob/pkg/filehash" 17 ) 18 19 // Hash creates a hash for the entire target 20 func (t *T) Hash() (empty string, _ error) { 21 switch t.Type { 22 case Path: 23 return t.filepathHash() 24 case Docker: 25 return t.dockerImagesHash() 26 default: 27 return t.filepathHash() 28 } 29 } 30 31 func (t *T) filepathHash() (empty string, _ error) { 32 aggregatedHashes := bytes.NewBuffer([]byte{}) 33 for _, f := range t.Paths { 34 target := filepath.Join(t.dir, f) 35 36 if !file.Exists(target) { 37 return empty, usererror.Wrapm(fmt.Errorf("target does not exist %q", f), "failed to hash target") 38 } 39 fi, err := os.Stat(target) 40 if err != nil { 41 return empty, fmt.Errorf("failed to get file info %q: %w", f, err) 42 } 43 44 if fi.IsDir() { 45 if err := filepath.WalkDir(target, func(p string, fi fs.DirEntry, err error) error { 46 if err != nil { 47 return err 48 } 49 if fi.IsDir() { 50 return nil 51 } 52 53 h, err := filehash.Hash(p) 54 if err != nil { 55 return fmt.Errorf("failed to hash target %q: %w", f, err) 56 } 57 58 _, err = aggregatedHashes.Write(h) 59 if err != nil { 60 return fmt.Errorf("failed to write target hash to aggregated hash %q: %w", f, err) 61 } 62 63 return nil 64 }); err != nil { 65 return empty, fmt.Errorf("failed to walk dir %q: %w", target, err) 66 } 67 // TODO: what happens on a empty dir? 68 } else { 69 h, err := filehash.Hash(target) 70 if err != nil { 71 return empty, fmt.Errorf("failed to hash target %q: %w", f, err) 72 } 73 74 _, err = aggregatedHashes.Write(h) 75 if err != nil { 76 return empty, fmt.Errorf("failed to write target hash to aggregated hash %q: %w", f, err) 77 } 78 } 79 } 80 81 h, err := filehash.HashBytes(aggregatedHashes) 82 if err != nil { 83 return empty, fmt.Errorf("failed to write aggregated target hash: %w", err) 84 } 85 86 return hex.EncodeToString(h), nil 87 } 88 89 func (t *T) dockerImagesHash() (string, error) { 90 91 var hash string 92 93 for _, image := range t.Paths { 94 h, err := t.dockerRegistryClient.ImageHash(image) 95 if err != nil { 96 if errors.Is(err, dockermobyutil.ErrImageNotFound) { 97 return "", usererror.Wrapm(err, "failed to fetch docker image hash") 98 } else { 99 return "", fmt.Errorf("failed to get docker image hash info %q: %w", image, err) 100 } 101 } 102 hash = hash + h 103 104 } 105 106 return hash, nil 107 }