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  }