update-repos.go 5.6 KB

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