github.com/whyrusleeping/gx@v0.14.3/diff.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 gx "github.com/whyrusleeping/gx/gxutil" 12 ) 13 14 func DiffPackages(a, b string) (*Diff, error) { 15 dir, err := ioutil.TempDir("", "gx-diff") 16 if err != nil { 17 return nil, err 18 } 19 20 pa, err := pm.GetPackageTo(a, filepath.Join(dir, "a")) 21 if err != nil { 22 return nil, err 23 } 24 25 pb, err := pm.GetPackageTo(b, filepath.Join(dir, "b")) 26 if err != nil { 27 return nil, err 28 } 29 30 d, err := PkgFileDiff(dir, pa, pb) 31 if err != nil { 32 return nil, err 33 } 34 35 d.Hashes = []string{a, b} 36 return d, nil 37 } 38 39 type Diff struct { 40 Version []string 41 Hashes []string 42 Name string 43 44 Imports map[string]*Diff 45 46 dir string 47 } 48 49 func PkgFileDiff(dir string, a, b *gx.Package) (*Diff, error) { 50 out := Diff{ 51 Version: []string{a.Version, b.Version}, 52 Name: a.Name, 53 Imports: make(map[string]*Diff), 54 dir: dir, 55 } 56 57 current := make(map[string]*gx.Dependency) 58 59 for _, dep := range a.Dependencies { 60 current[dep.Name] = dep 61 } 62 63 for _, dep := range b.Dependencies { 64 old, ok := current[dep.Name] 65 if ok { 66 if old.Hash != dep.Hash { 67 ddiff, err := DiffPackages(old.Hash, dep.Hash) 68 if err != nil { 69 return nil, err 70 } 71 72 out.Imports[dep.Name] = ddiff 73 } 74 } else { 75 ndep := &Diff{ 76 Version: []string{dep.Version}, 77 Hashes: []string{dep.Hash}, 78 Name: dep.Name, 79 } 80 out.Imports[dep.Name] = ndep 81 tdir, err := ioutil.TempDir("", "gx-diff") 82 if err != nil { 83 return nil, err 84 } 85 ndep.dir = tdir 86 87 err = os.MkdirAll(filepath.Join(tdir, "a"), 0775) 88 if err != nil { 89 return nil, err 90 } 91 92 _, err = pm.GetPackageTo(dep.Hash, filepath.Join(tdir, "b")) 93 if err != nil { 94 return nil, err 95 } 96 } 97 } 98 99 return &out, nil 100 } 101 102 func (d *Diff) Print(interactive bool) { 103 d.recPrint(interactive, make(map[string]bool), "") 104 } 105 106 func (d *Diff) recPrint(interactive bool, done map[string]bool, parent string) { 107 change := len(d.Version) == 2 108 if parent != "" { 109 fmt.Printf("IN %s:\n", parent) 110 } 111 if change { 112 fmt.Printf("PACKAGE %s was changed from version\n", d.Name) 113 fmt.Printf(" %s (%s)\n to\n %s (%s)\n", d.Version[0], d.Hashes[0], d.Version[1], d.Hashes[1]) 114 fmt.Printf(" There were %d changes in this packages dependencies.\n", len(d.Imports)) 115 } else { 116 fmt.Printf("PACKAGE %s was imported at version\n %s (%s)\n", d.Name, d.Version[0], d.Hashes[0]) 117 } 118 if d.hasCodeChanges() { 119 prompt := " view new code?" 120 if change { 121 prompt = " view code changes for this package?" 122 } 123 if !interactive || yesNoPrompt(prompt, true) { 124 d.PrintCodeChanges() 125 } 126 fmt.Println() 127 } else { 128 fmt.Printf("Nothing else was changed in this package.\n\n") 129 } 130 131 for _, cdiff := range d.Imports { 132 n := strings.Join(cdiff.Hashes, "-") 133 if !done[n] { 134 cdiff.recPrint(interactive, done, d.Name) 135 done[n] = true 136 } 137 } 138 } 139 140 func (d *Diff) hasCodeChanges() bool { 141 cmd := exec.Command("diff", "-r", "-x", "package.json", "a", "b") 142 cmd.Dir = d.dir 143 cmd.Stdout = ioutil.Discard 144 cmd.Stderr = ioutil.Discard 145 err := cmd.Run() 146 147 return err != nil 148 } 149 150 func (d *Diff) PrintCodeChanges() error { 151 var cmd *exec.Cmd 152 if _, err := exec.LookPath("git"); err == nil { 153 cmd = exec.Command("git", "diff", "--", "a", "b") 154 } else { 155 cmd = exec.Command("diff", "-r", "-x", "package.json", "a", "b") 156 } 157 cmd.Stdout = os.Stdout 158 cmd.Stderr = os.Stderr 159 cmd.Dir = d.dir 160 fmt.Println("RUNNING: ", d.dir, cmd.Dir, cmd.Args) 161 return cmd.Run() 162 } 163 164 func (d *Diff) Cleanup() error { 165 if d.dir != "" { 166 err := os.RemoveAll(d.dir) 167 if err != nil { 168 return err 169 } 170 } 171 172 for _, cdiff := range d.Imports { 173 err := cdiff.Cleanup() 174 if err != nil { 175 return err 176 } 177 } 178 179 return nil 180 }