giffer

Create .gif images from sites like youtube.com
Log | Files | Refs | LICENSE

commit 9c31647f1afb84c2c640c9a952dde7ceccf740d8
parent 8ee99c1e8c615e9c56c5a5bc91c9f53c5a4c3680
Author: Jack Mordaunt <jackmordaunt@gmail.com>
Date:   Mon, 19 Nov 2018 22:57:47 +1300

[+] Hash the output of `yarn build` to speed up builds.

Diffstat:
Acmd/build/hash.go | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcmd/build/main.go | 17+++++++++++++++++
2 files changed, 118 insertions(+), 0 deletions(-)

diff --git a/cmd/build/hash.go b/cmd/build/hash.go @@ -0,0 +1,101 @@ +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "sort" + "sync" + "unicode" + + "github.com/OneOfOne/xxhash" + "github.com/pkg/errors" +) + +// Hash recusively and concurrently hashes a fileEntry or directory. +func Hash(path string) (uint64, error) { + var ( + found = make(chan fileEntry) + wg = &sync.WaitGroup{} + ) + walk := func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + wg.Add(1) + go func() { + defer wg.Done() + by, err := ioutil.ReadFile(path) + found <- fileEntry{ + Path: path, + Content: by, + Err: err, + } + }() + return nil + } + if err := filepath.Walk(path, walk); err != nil { + return 0, err + } + go func() { + wg.Wait() + close(found) + }() + var files []fileEntry + for f := range found { + if f.Err != nil { + return 0, f.Err + } + files = append(files, f) + } + sort.Sort(alphabetical(files)) + hash := xxhash.New64() + for _, f := range files { + if _, err := hash.Write(f.Content); err != nil { + return 0, errors.Wrap(err, "hashing") + } + } + return hash.Sum64(), nil +} + +type fileEntry struct { + Path string + Content []byte + Err error +} + +type alphabetical []fileEntry + +func (a alphabetical) Len() int { + return len(a) +} + +func (a alphabetical) Swap(ii, jj int) { + a[ii], a[jj] = a[jj], a[ii] +} + +func (a alphabetical) Less(ii, jj int) bool { + iRunes := []rune(a[ii].Path) + jRunes := []rune(a[jj].Path) + max := len(iRunes) + if max > len(jRunes) { + max = len(jRunes) + } + for idx := 0; idx < max; idx++ { + ir := iRunes[idx] + jr := jRunes[idx] + lir := unicode.ToLower(ir) + ljr := unicode.ToLower(jr) + if lir != ljr { + return lir < ljr + } + // the lowercase runes are the same, so compare the original + if ir != jr { + return ir < jr + } + } + return false +} diff --git a/cmd/build/main.go b/cmd/build/main.go @@ -4,6 +4,7 @@ package main import ( "archive/zip" "bytes" + "encoding/binary" "flag" "fmt" "image/png" @@ -96,6 +97,22 @@ func main() { Task{ Name: "compile ui", Op: func() error { + var ( + uiFiles = "/Users/jack/dev/personal/giffer/cmd/desktop/ui/src" + checksumFile = "/Users/jack/dev/personal/giffer/cmd/desktop/ui/checksum" + ) + h, err := Hash(uiFiles) + if err != nil { + return errors.Wrap(err, "hashing ui files") + } + checksum, err := ioutil.ReadFile(checksumFile) + if err != nil && !os.IsNotExist(err) { + return errors.Wrap(err, "reading checksum file") + } + if !os.IsNotExist(err) && h == binary.LittleEndian.Uint64(checksum) { + return nil + } + fmt.Printf("compile ui: checksum mismatch: recompiling\n") cmd := exec.Command("yarn", "build") cmd.Dir = "/Users/jack/dev/personal/giffer/cmd/desktop/ui" if out, err := cmd.CombinedOutput(); err != nil {