github.com/comcast/canticle@v0.0.0-20161108184242-c53cface56e8/canticles/save.go (about)

     1  package canticles
     2  
     3  import (
     4  	"encoding/json"
     5  	"flag"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  	"sort"
    11  )
    12  
    13  type Save struct {
    14  	flags     *flag.FlagSet
    15  	Verbose   bool
    16  	Quite     bool
    17  	DryRun    bool
    18  	OnDisk    bool
    19  	Branches  bool
    20  	NoSources bool
    21  	Excludes  DirFlags
    22  	Resolver  ConflictResolver
    23  }
    24  
    25  func NewSave() *Save {
    26  	f := flag.NewFlagSet("save", flag.ExitOnError)
    27  	s := &Save{
    28  		flags:    f,
    29  		Resolver: &PromptResolution{Printf: fmt.Printf, Scanf: fmt.Scanf},
    30  		Excludes: DirFlags(NewStringSet()),
    31  	}
    32  	f.BoolVar(&s.Verbose, "v", false, "Be verbose when getting stuff.")
    33  	f.BoolVar(&s.Quite, "q", false, "Don't print warnings.")
    34  	f.BoolVar(&s.OnDisk, "ondisk", false, "Save the revisions and sources present on disk ignoring all other Canticle files.")
    35  	f.BoolVar(&s.DryRun, "d", false, "Don't save the deps, just print them.")
    36  	f.BoolVar(&s.Branches, "b", false, "Save branches for the current projects, not revisions.")
    37  	f.BoolVar(&s.NoSources, "no-sources", false, "Don't save a sources for the current projects, not revisions.")
    38  	f.Var(&s.Excludes, "exclude", "Do not recur into these directories when saving unless they are in the dep tree.")
    39  	return s
    40  }
    41  
    42  var save = NewSave()
    43  
    44  var SaveCommand = &Command{
    45  	Name:             "save",
    46  	UsageLine:        "save [-d] [-b] [-v] [-ondisk] [-exclude <dir>] [-no-sources]",
    47  	ShortDescription: "Save the current revision of all dependencies in a Canticle file.",
    48  	LongDescription: `The save command will save the dependencies for a package into a Canticle file.  If at the src level save the current revision of all packages in belows. All dependencies must be present on disk and in the GOROOT. The generated Canticle file will be saved in the packages root directory.
    49  
    50  Specify -v to print out a verbose set of operations instead of just errors.
    51  
    52  Specify -ondisk to use on disk revisions and sources and do no conflict resolution.
    53  
    54  Specify -b to save branches or tags when present instead of revisions`,
    55  	Flags: save.flags,
    56  	Cmd:   save,
    57  }
    58  
    59  // Run the save command, ignores args. Uses its flagset instead.
    60  func (s *Save) Run(args []string) {
    61  	if s.Verbose {
    62  		Verbose = true
    63  	}
    64  	defer func() { Verbose = false }()
    65  	if s.Quite {
    66  		Quite = true
    67  	}
    68  	defer func() { Quite = false }()
    69  
    70  	if s.OnDisk {
    71  		s.Resolver = &PreferLocalResolution{}
    72  	}
    73  
    74  	wd, err := os.Getwd()
    75  	if err != nil {
    76  		log.Fatal(err)
    77  	}
    78  	gopath, err := EnvGoPath()
    79  	if err != nil {
    80  		log.Fatal(err)
    81  	}
    82  	if err := s.SaveProject(gopath, wd); err != nil {
    83  		log.Fatal(err)
    84  	}
    85  }
    86  
    87  // SaveProject does four things:
    88  //   *  It fetches the dep tree of path
    89  //   *  It fetches all possible DependencySources
    90  //   *  It performs conflict resolution
    91  //   *  It saves a Canticle file in path
    92  func (s *Save) SaveProject(gopath, path string) error {
    93  	LogVerbose("Working with gopath %s", gopath)
    94  	deps, err := s.ReadDeps(gopath, path)
    95  	if err != nil {
    96  		return err
    97  	}
    98  	sources, err := s.GetSources(gopath, path, deps)
    99  	if err != nil {
   100  		return err
   101  	}
   102  	LogVerbose("Discovered sources:\n%+v", sources)
   103  	cantdeps, err := s.Resolver.ResolveConflicts(sources)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	if err := s.SaveDeps(path, cantdeps); err != nil {
   109  		return err
   110  	}
   111  	return nil
   112  }
   113  
   114  // GetSources returns the DependencySources (e.g. the possible revisions, vcs sources, and deps)
   115  // for a give path, and set Dependencies.
   116  func (s *Save) GetSources(gopath, path string, deps Dependencies) (*DependencySources, error) {
   117  	LogVerbose("Getting local vcs sources for repos in path %+v", gopath)
   118  	repoResolver := NewMemoizedRepoResolver(&LocalRepoResolver{gopath})
   119  	reader := &DepReader{Gopath: gopath}
   120  	sourceResolver := &SourcesResolver{
   121  		Gopath:     gopath,
   122  		RootPath:   path,
   123  		Resolver:   repoResolver,
   124  		Branches:   s.Branches,
   125  		Sources:    !s.NoSources,
   126  		CDepReader: reader,
   127  	}
   128  	return sourceResolver.ResolveSources(deps)
   129  }
   130  
   131  // ReadDeps reads all dependencies and transitive deps for path.
   132  func (s *Save) ReadDeps(gopath, path string) (Dependencies, error) {
   133  	LogVerbose("Reading deps for repos in path %s", path)
   134  	reader := &DepReader{Gopath: gopath}
   135  	ds := NewDependencySaver(reader.AllDeps, gopath, path)
   136  	ds.NoRecur = StringSet(s.Excludes)
   137  	dw := NewDependencyWalker(ds.PackagePaths, ds.SavePackageDeps)
   138  	if err := dw.TraverseDependencies(path); err != nil {
   139  		return nil, fmt.Errorf("cant read path dep tree %s %s", path, err.Error())
   140  	}
   141  	LogVerbose("Built dep tree: %+v", ds.Dependencies())
   142  	return ds.Dependencies(), nil
   143  }
   144  
   145  // SaveDeps saves a canticle file at path containing deps.
   146  func (s *Save) SaveDeps(path string, deps []*CanticleDependency) error {
   147  	sort.Sort(CanticleDependencies(deps))
   148  	j, err := json.MarshalIndent(deps, "", "    ")
   149  	if err != nil {
   150  		return err
   151  	}
   152  	if s.DryRun {
   153  		fmt.Println(string(j))
   154  		return nil
   155  	}
   156  	return ioutil.WriteFile(DependencyFile(path), j, 0644)
   157  }