github.com/afking/bazel-gazelle@v0.0.0-20180301150245-c02bc0f529e8/cmd/gazelle/update-repos.go (about) 1 /* Copyright 2017 The Bazel Authors. All rights reserved. 2 3 Licensed under the Apache License, Version 2.0 (the "License"); 4 you may not use this file except in compliance with the License. 5 You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 Unless required by applicable law or agreed to in writing, software 10 distributed under the License is distributed on an "AS IS" BASIS, 11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 See the License for the specific language governing permissions and 13 limitations under the License. 14 */ 15 16 package main 17 18 import ( 19 "errors" 20 "flag" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "sync" 26 27 "github.com/bazelbuild/bazel-gazelle/internal/merger" 28 "github.com/bazelbuild/bazel-gazelle/internal/repos" 29 "github.com/bazelbuild/bazel-gazelle/internal/wspace" 30 bf "github.com/bazelbuild/buildtools/build" 31 ) 32 33 type updateReposFn func(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error) 34 35 type updateReposConfiguration struct { 36 fn updateReposFn 37 repoRoot string 38 lockFilename string 39 importPaths []string 40 } 41 42 func updateRepos(args []string) error { 43 c, err := newUpdateReposConfiguration(args) 44 if err != nil { 45 return err 46 } 47 48 workspacePath := filepath.Join(c.repoRoot, "WORKSPACE") 49 content, err := ioutil.ReadFile(workspacePath) 50 if err != nil { 51 return fmt.Errorf("error reading %q: %v", workspacePath, err) 52 } 53 oldFile, err := bf.Parse(workspacePath, content) 54 if err != nil { 55 return fmt.Errorf("error parsing %q: %v", workspacePath, err) 56 } 57 58 mergedFile, err := c.fn(c, oldFile) 59 if err != nil { 60 return err 61 } 62 mergedFile = merger.FixLoads(mergedFile) 63 if err := ioutil.WriteFile(mergedFile.Path, bf.Format(mergedFile), 0666); err != nil { 64 return fmt.Errorf("error writing %q: %v", mergedFile.Path, err) 65 } 66 return nil 67 } 68 69 func newUpdateReposConfiguration(args []string) (*updateReposConfiguration, error) { 70 c := new(updateReposConfiguration) 71 fs := flag.NewFlagSet("gazelle", flag.ContinueOnError) 72 // Flag will call this on any parse error. Don't print usage unless 73 // -h or -help were passed explicitly. 74 fs.Usage = func() {} 75 76 fromFileFlag := fs.String("from_file", "", "Gazelle will translate repositories listed in this file into repository rules in WORKSPACE. Currently only dep's Gopkg.lock is supported.") 77 repoRootFlag := fs.String("repo_root", "", "path to the root directory of the repository. If unspecified, this is assumed to be the directory containing WORKSPACE.") 78 if err := fs.Parse(args); err != nil { 79 if err == flag.ErrHelp { 80 updateReposUsage(fs) 81 os.Exit(0) 82 } 83 // flag already prints the error; don't print it again. 84 return nil, errors.New("Try -help for more information") 85 } 86 87 // Handle general flags that apply to all subcommands. 88 c.repoRoot = *repoRootFlag 89 if c.repoRoot == "" { 90 if repoRoot, err := wspace.Find("."); err != nil { 91 return nil, err 92 } else { 93 c.repoRoot = repoRoot 94 } 95 } 96 97 // Handle flags specific to each subcommand. 98 switch { 99 case *fromFileFlag != "": 100 if len(fs.Args()) != 0 { 101 return nil, fmt.Errorf("Got %d positional arguments with -from_file; wanted 0.\nTry -help for more information.", len(fs.Args())) 102 } 103 c.fn = importFromLockFile 104 c.lockFilename = *fromFileFlag 105 106 default: 107 if len(fs.Args()) == 0 { 108 return nil, fmt.Errorf("No repositories specified\nTry -help for more information.") 109 } 110 c.fn = updateImportPaths 111 c.importPaths = fs.Args() 112 } 113 114 return c, nil 115 } 116 117 func updateReposUsage(fs *flag.FlagSet) { 118 fmt.Fprintln(os.Stderr, `usage: 119 120 # Add/update repositories by import path 121 gazelle update-repos example.com/repo1 example.com/repo2 122 123 # Import repositories from lock file 124 gazelle update-repos -from_file=file 125 126 The update-repos command updates repository rules in the WORKSPACE file. 127 update-repos can add or update repositories explicitly by import path. 128 update-repos can also import repository rules from a vendoring tool's lock 129 file (currently only deps' Gopkg.lock is supported). 130 131 FLAGS: 132 `) 133 } 134 135 func updateImportPaths(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error) { 136 rs := repos.ListRepositories(oldFile) 137 rc := repos.NewRemoteCache(rs) 138 139 genRules := make([]bf.Expr, len(c.importPaths)) 140 errs := make([]error, len(c.importPaths)) 141 var wg sync.WaitGroup 142 wg.Add(len(c.importPaths)) 143 for i, imp := range c.importPaths { 144 go func(i int) { 145 defer wg.Done() 146 repo, err := repos.UpdateRepo(rc, imp) 147 if err != nil { 148 errs[i] = err 149 return 150 } 151 repo.Remote = "" // don't set these explicitly 152 repo.VCS = "" 153 rule := repos.GenerateRule(repo) 154 genRules[i] = rule 155 }(i) 156 } 157 wg.Wait() 158 159 for _, err := range errs { 160 if err != nil { 161 return nil, err 162 } 163 } 164 mergedFile, _ := merger.MergeFile(genRules, nil, oldFile, merger.RepoAttrs) 165 return mergedFile, nil 166 } 167 168 func importFromLockFile(c *updateReposConfiguration, oldFile *bf.File) (*bf.File, error) { 169 genRules, err := repos.ImportRepoRules(c.lockFilename) 170 if err != nil { 171 return nil, err 172 } 173 174 mergedFile, _ := merger.MergeFile(genRules, nil, oldFile, merger.RepoAttrs) 175 return mergedFile, nil 176 }