github.com/whyrusleeping/gx@v0.14.3/check.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	gx "github.com/whyrusleeping/gx/gxutil"
     8  
     9  	"github.com/blang/semver"
    10  )
    11  
    12  type pkgImport struct {
    13  	parents []string
    14  	version semver.Version
    15  }
    16  
    17  func check(pkg *gx.Package) (bool, error) {
    18  	var failed bool
    19  	// name -> hash -> path+version
    20  	packages := make(map[string]map[string]*pkgImport)
    21  
    22  	var traverse func(*gx.Package) error
    23  	traverse = func(pkg *gx.Package) error {
    24  		return pkg.ForEachDep(func(dep *gx.Dependency, dpkg *gx.Package) error {
    25  			pkgVersions, ok := packages[dpkg.Name]
    26  			if !ok {
    27  				pkgVersions = make(map[string]*pkgImport, 1)
    28  				packages[dpkg.Name] = pkgVersions
    29  			}
    30  			imp, ok := pkgVersions[dep.Hash]
    31  			if !ok {
    32  				version, err := semver.Parse(dpkg.Version)
    33  				if dpkg.Version != "" && err != nil {
    34  					fmt.Printf(
    35  						"package %s (%s) has an invalid version '%s': %s\n",
    36  						dpkg.Name,
    37  						dep.Hash,
    38  						dpkg.Version,
    39  						err,
    40  					)
    41  				}
    42  				pkgVersions[dep.Hash] = &pkgImport{
    43  					version: version,
    44  					parents: []string{pkg.Name},
    45  				}
    46  				return traverse(dpkg)
    47  			}
    48  			imp.parents = append(imp.parents, pkg.Name)
    49  			return nil
    50  		})
    51  	}
    52  
    53  	if err := traverse(pkg); err != nil {
    54  		return !failed, err
    55  	}
    56  
    57  	var dupes []string
    58  	for name, pkgVersions := range packages {
    59  		switch len(pkgVersions) {
    60  		case 0:
    61  			panic("must have at least one package version")
    62  		case 1:
    63  			continue
    64  		}
    65  		dupes = append(dupes, name)
    66  	}
    67  
    68  	if len(dupes) > 0 {
    69  		failed = true
    70  		sort.Strings(dupes)
    71  
    72  		for _, name := range dupes {
    73  			pkgVersions := packages[name]
    74  
    75  			hashes := make([]string, 0, len(pkgVersions))
    76  			for h := range pkgVersions {
    77  				hashes = append(hashes, h)
    78  			}
    79  
    80  			sort.Slice(hashes, func(i, j int) bool {
    81  				ih := hashes[i]
    82  				jh := hashes[j]
    83  				iv := pkgVersions[ih].version
    84  				jv := pkgVersions[jh].version
    85  				if res := iv.Compare(jv); res != 0 {
    86  					return res < 0
    87  				}
    88  				return ih < jh
    89  			})
    90  
    91  			fmt.Printf("package %s imported as:\n", name)
    92  			for _, hash := range hashes {
    93  				imp := pkgVersions[hash]
    94  
    95  				fmt.Printf("  - %s %s\n", imp.version, hash)
    96  				sort.Strings(imp.parents)
    97  				for _, p := range imp.parents {
    98  					fmt.Printf("    - %s\n", p)
    99  				}
   100  			}
   101  		}
   102  	}
   103  
   104  	// Finally, check names and versions.
   105  	if err := pkg.ForEachDep(func(dep *gx.Dependency, dpkg *gx.Package) error {
   106  		if dep.Name != dpkg.Name {
   107  			failed = true
   108  			fmt.Printf(
   109  				"dependency %s references a package with name %s\n",
   110  				dep.Name,
   111  				dpkg.Name,
   112  			)
   113  		}
   114  		if dep.Version != dpkg.Version {
   115  			failed = true
   116  			fmt.Printf(
   117  				"dependency %s has version %s but the referenced package has version %s\n",
   118  				dep.Name,
   119  				dep.Version,
   120  				dpkg.Version,
   121  			)
   122  		}
   123  		return nil
   124  	}); err != nil {
   125  		return !failed, err
   126  	}
   127  	return !failed, nil
   128  }