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:
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 {