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 }