1
2
3
4
5 package modload
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "io/fs"
13 "os"
14 pathpkg "path"
15 "slices"
16 "sort"
17 "strings"
18 "sync"
19 "time"
20
21 "cmd/go/internal/cfg"
22 "cmd/go/internal/gover"
23 "cmd/go/internal/imports"
24 "cmd/go/internal/modfetch"
25 "cmd/go/internal/modfetch/codehost"
26 "cmd/go/internal/modinfo"
27 "cmd/go/internal/search"
28 "cmd/go/internal/str"
29 "cmd/go/internal/trace"
30 "cmd/internal/pkgpattern"
31
32 "golang.org/x/mod/module"
33 "golang.org/x/mod/semver"
34 )
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 func Query(ctx context.Context, path, query, current string, allowed AllowedFunc) (*modfetch.RevInfo, error) {
84 ctx, span := trace.StartSpan(ctx, "modload.Query "+path)
85 defer span.Done()
86
87 return queryReuse(ctx, path, query, current, allowed, nil)
88 }
89
90
91
92 func queryReuse(ctx context.Context, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) {
93 var info *modfetch.RevInfo
94 err := modfetch.TryProxies(func(proxy string) (err error) {
95 info, err = queryProxy(ctx, proxy, path, query, current, allowed, reuse)
96 return err
97 })
98 return info, err
99 }
100
101
102
103 func checkReuse(ctx context.Context, m module.Version, old *codehost.Origin) error {
104 return modfetch.TryProxies(func(proxy string) error {
105 repo, err := lookupRepo(ctx, proxy, m.Path)
106 if err != nil {
107 return err
108 }
109 return checkReuseRepo(ctx, repo, m.Path, m.Version, old)
110 })
111 }
112
113 func checkReuseRepo(ctx context.Context, repo versionRepo, path, query string, origin *codehost.Origin) error {
114 if origin == nil {
115 return errors.New("nil Origin")
116 }
117
118
119
120
121 switch {
122 case origin.RepoSum != "":
123
124
125
126 case query == module.CanonicalVersion(query):
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 if origin.Hash == "" && origin.Ref == "" && origin.TagSum == "" {
142 return errors.New("no Origin information to check")
143 }
144
145 case IsRevisionQuery(path, query):
146
147
148
149
150
151
152 if origin.Hash == "" && origin.Ref == "" {
153 return fmt.Errorf("query %q requires a Hash or Ref", query)
154 }
155
156
157
158
159
160
161
162 if origin.TagSum == "" {
163 return fmt.Errorf("query %q requires a TagSum", query)
164 }
165
166 default:
167
168
169
170 if origin.TagSum == "" {
171 return fmt.Errorf("query %q requires a TagSum", query)
172 }
173 }
174
175 return repo.CheckReuse(ctx, origin)
176 }
177
178
179
180
181
182
183
184
185
186
187 type AllowedFunc func(context.Context, module.Version) error
188
189 var errQueryDisabled error = queryDisabledError{}
190
191 type queryDisabledError struct{}
192
193 func (queryDisabledError) Error() string {
194 if cfg.BuildModReason == "" {
195 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod)
196 }
197 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason)
198 }
199
200 func queryProxy(ctx context.Context, proxy, path, query, current string, allowed AllowedFunc, reuse map[module.Version]*modinfo.ModulePublic) (*modfetch.RevInfo, error) {
201 ctx, span := trace.StartSpan(ctx, "modload.queryProxy "+path+" "+query)
202 defer span.Done()
203
204 if current != "" && current != "none" && !gover.ModIsValid(path, current) {
205 return nil, fmt.Errorf("invalid previous version %v@%v", path, current)
206 }
207 if cfg.BuildMod == "vendor" {
208 return nil, errQueryDisabled
209 }
210 if allowed == nil {
211 allowed = func(context.Context, module.Version) error { return nil }
212 }
213
214 if MainModules.Contains(path) && (query == "upgrade" || query == "patch") {
215 m := module.Version{Path: path}
216 if err := allowed(ctx, m); err != nil {
217 return nil, fmt.Errorf("internal error: main module version is not allowed: %w", err)
218 }
219 return &modfetch.RevInfo{Version: m.Version}, nil
220 }
221
222 if path == "std" || path == "cmd" {
223 return nil, fmt.Errorf("can't query specific version (%q) of standard-library module %q", query, path)
224 }
225
226 repo, err := lookupRepo(ctx, proxy, path)
227 if err != nil {
228 return nil, err
229 }
230
231 if old := reuse[module.Version{Path: path, Version: query}]; old != nil {
232 if err := checkReuseRepo(ctx, repo, path, query, old.Origin); err == nil {
233 info := &modfetch.RevInfo{
234 Version: old.Version,
235 Origin: old.Origin,
236 }
237 if old.Time != nil {
238 info.Time = *old.Time
239 }
240 return info, nil
241 }
242 }
243
244
245
246 qm, err := newQueryMatcher(path, query, current, allowed)
247 if (err == nil && qm.canStat) || err == errRevQuery {
248
249
250
251
252
253
254 info, err := repo.Stat(ctx, query)
255 if err != nil {
256 queryErr := err
257
258
259
260 canonicalQuery := module.CanonicalVersion(query)
261 if canonicalQuery != "" && query != canonicalQuery {
262 info, err = repo.Stat(ctx, canonicalQuery)
263 if err != nil && !errors.Is(err, fs.ErrNotExist) {
264 return info, err
265 }
266 }
267 if err != nil {
268 return info, queryErr
269 }
270 }
271 if err := allowed(ctx, module.Version{Path: path, Version: info.Version}); errors.Is(err, ErrDisallowed) {
272 return nil, err
273 }
274 return info, nil
275 } else if err != nil {
276 return nil, err
277 }
278
279
280 versions, err := repo.Versions(ctx, qm.prefix)
281 if err != nil {
282 return nil, err
283 }
284 origin := versions.Origin
285
286 revWithOrigin := func(rev *modfetch.RevInfo) *modfetch.RevInfo {
287 if rev == nil {
288 if origin == nil {
289 return nil
290 }
291 return &modfetch.RevInfo{Origin: origin}
292 }
293
294 clone := *rev
295 clone.Origin = origin
296 return &clone
297 }
298
299 releases, prereleases, err := qm.filterVersions(ctx, versions.List)
300 if err != nil {
301 return revWithOrigin(nil), err
302 }
303
304 lookup := func(v string) (*modfetch.RevInfo, error) {
305 rev, err := repo.Stat(ctx, v)
306 if rev != nil {
307
308
309 origin = mergeOrigin(origin, rev.Origin)
310 }
311 if err != nil {
312 return revWithOrigin(nil), err
313 }
314
315 if (query == "upgrade" || query == "patch") && module.IsPseudoVersion(current) && !rev.Time.IsZero() {
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334 currentTime, err := module.PseudoVersionTime(current)
335 if err == nil && rev.Time.Before(currentTime) {
336 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
337 return revWithOrigin(nil), err
338 }
339 rev, err = repo.Stat(ctx, current)
340 if rev != nil {
341 origin = mergeOrigin(origin, rev.Origin)
342 }
343 if err != nil {
344 return revWithOrigin(nil), err
345 }
346 return revWithOrigin(rev), nil
347 }
348 }
349
350 return revWithOrigin(rev), nil
351 }
352
353 if qm.preferLower {
354 if len(releases) > 0 {
355 return lookup(releases[0])
356 }
357 if len(prereleases) > 0 {
358 return lookup(prereleases[0])
359 }
360 } else {
361 if len(releases) > 0 {
362 return lookup(releases[len(releases)-1])
363 }
364 if len(prereleases) > 0 {
365 return lookup(prereleases[len(prereleases)-1])
366 }
367 }
368
369 if qm.mayUseLatest {
370 latest, err := repo.Latest(ctx)
371 if latest != nil {
372 origin = mergeOrigin(origin, latest.Origin)
373 }
374 if err == nil {
375 if qm.allowsVersion(ctx, latest.Version) {
376 return lookup(latest.Version)
377 }
378 } else if !errors.Is(err, fs.ErrNotExist) {
379 return revWithOrigin(nil), err
380 }
381 }
382
383 if (query == "upgrade" || query == "patch") && current != "" && current != "none" {
384
385 if err := allowed(ctx, module.Version{Path: path, Version: current}); errors.Is(err, ErrDisallowed) {
386 return revWithOrigin(nil), err
387 }
388 return lookup(current)
389 }
390
391 return revWithOrigin(nil), &NoMatchingVersionError{query: query, current: current}
392 }
393
394
395
396
397
398 func IsRevisionQuery(path, vers string) bool {
399 if vers == "latest" ||
400 vers == "upgrade" ||
401 vers == "patch" ||
402 strings.HasPrefix(vers, "<") ||
403 strings.HasPrefix(vers, ">") ||
404 (gover.ModIsValid(path, vers) && gover.ModIsPrefix(path, vers)) {
405 return false
406 }
407 return true
408 }
409
410 type queryMatcher struct {
411 path string
412 prefix string
413 filter func(version string) bool
414 allowed AllowedFunc
415 canStat bool
416 preferLower bool
417 mayUseLatest bool
418 preferIncompatible bool
419 }
420
421 var errRevQuery = errors.New("query refers to a non-semver revision")
422
423
424
425
426
427
428 func newQueryMatcher(path string, query, current string, allowed AllowedFunc) (*queryMatcher, error) {
429 badVersion := func(v string) (*queryMatcher, error) {
430 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query)
431 }
432
433 matchesMajor := func(v string) bool {
434 _, pathMajor, ok := module.SplitPathVersion(path)
435 if !ok {
436 return false
437 }
438 return module.CheckPathMajor(v, pathMajor) == nil
439 }
440
441 qm := &queryMatcher{
442 path: path,
443 allowed: allowed,
444 preferIncompatible: strings.HasSuffix(current, "+incompatible"),
445 }
446
447 switch {
448 case query == "latest":
449 qm.mayUseLatest = true
450
451 case query == "upgrade":
452 if current == "" || current == "none" {
453 qm.mayUseLatest = true
454 } else {
455 qm.mayUseLatest = module.IsPseudoVersion(current)
456 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
457 }
458
459 case query == "patch":
460 if current == "" || current == "none" {
461 return nil, &NoPatchBaseError{path}
462 }
463 if current == "" {
464 qm.mayUseLatest = true
465 } else {
466 qm.mayUseLatest = module.IsPseudoVersion(current)
467 qm.prefix = gover.ModMajorMinor(qm.path, current) + "."
468 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, current) >= 0 }
469 }
470
471 case strings.HasPrefix(query, "<="):
472 v := query[len("<="):]
473 if !gover.ModIsValid(path, v) {
474 return badVersion(v)
475 }
476 if gover.ModIsPrefix(path, v) {
477
478 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
479 }
480 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) <= 0 }
481 if !matchesMajor(v) {
482 qm.preferIncompatible = true
483 }
484
485 case strings.HasPrefix(query, "<"):
486 v := query[len("<"):]
487 if !gover.ModIsValid(path, v) {
488 return badVersion(v)
489 }
490 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) < 0 }
491 if !matchesMajor(v) {
492 qm.preferIncompatible = true
493 }
494
495 case strings.HasPrefix(query, ">="):
496 v := query[len(">="):]
497 if !gover.ModIsValid(path, v) {
498 return badVersion(v)
499 }
500 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) >= 0 }
501 qm.preferLower = true
502 if !matchesMajor(v) {
503 qm.preferIncompatible = true
504 }
505
506 case strings.HasPrefix(query, ">"):
507 v := query[len(">"):]
508 if !gover.ModIsValid(path, v) {
509 return badVersion(v)
510 }
511 if gover.ModIsPrefix(path, v) {
512
513 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query)
514 }
515 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, v) > 0 }
516 qm.preferLower = true
517 if !matchesMajor(v) {
518 qm.preferIncompatible = true
519 }
520
521 case gover.ModIsValid(path, query):
522 if gover.ModIsPrefix(path, query) {
523 qm.prefix = query + "."
524
525
526 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) >= 0 }
527 } else {
528 qm.canStat = true
529 qm.filter = func(mv string) bool { return gover.ModCompare(qm.path, mv, query) == 0 }
530 qm.prefix = semver.Canonical(query)
531 }
532 if !matchesMajor(query) {
533 qm.preferIncompatible = true
534 }
535
536 default:
537 return nil, errRevQuery
538 }
539
540 return qm, nil
541 }
542
543
544
545 func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
546 if qm.prefix != "" && !strings.HasPrefix(v, qm.prefix) {
547 if gover.IsToolchain(qm.path) && strings.TrimSuffix(qm.prefix, ".") == v {
548
549 } else {
550 return false
551 }
552 }
553 if qm.filter != nil && !qm.filter(v) {
554 return false
555 }
556 if qm.allowed != nil {
557 if err := qm.allowed(ctx, module.Version{Path: qm.path, Version: v}); errors.Is(err, ErrDisallowed) {
558 return false
559 }
560 }
561 return true
562 }
563
564
565
566
567
568
569
570
571
572 func (qm *queryMatcher) filterVersions(ctx context.Context, versions []string) (releases, prereleases []string, err error) {
573 needIncompatible := qm.preferIncompatible
574
575 var lastCompatible string
576 for _, v := range versions {
577 if !qm.allowsVersion(ctx, v) {
578 continue
579 }
580
581 if !needIncompatible {
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598 if !strings.HasSuffix(v, "+incompatible") {
599 lastCompatible = v
600 } else if lastCompatible != "" {
601
602
603
604
605 ok, err := versionHasGoMod(ctx, module.Version{Path: qm.path, Version: lastCompatible})
606 if err != nil {
607 return nil, nil, err
608 }
609 if ok {
610
611
612
613
614 break
615 }
616
617
618
619
620 needIncompatible = true
621 }
622 }
623
624 if gover.ModIsPrerelease(qm.path, v) {
625 prereleases = append(prereleases, v)
626 } else {
627 releases = append(releases, v)
628 }
629 }
630
631 return releases, prereleases, nil
632 }
633
634 type QueryResult struct {
635 Mod module.Version
636 Rev *modfetch.RevInfo
637 Packages []string
638 }
639
640
641
642 func QueryPackages(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) ([]QueryResult, error) {
643 pkgMods, modOnly, err := QueryPattern(ctx, pattern, query, current, allowed)
644
645 if len(pkgMods) == 0 && err == nil {
646 replacement := Replacement(modOnly.Mod)
647 return nil, &PackageNotInModuleError{
648 Mod: modOnly.Mod,
649 Replacement: replacement,
650 Query: query,
651 Pattern: pattern,
652 }
653 }
654
655 return pkgMods, err
656 }
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673 func QueryPattern(ctx context.Context, pattern, query string, current func(string) string, allowed AllowedFunc) (pkgMods []QueryResult, modOnly *QueryResult, err error) {
674 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern "+pattern+" "+query)
675 defer span.Done()
676
677 base := pattern
678
679 firstError := func(m *search.Match) error {
680 if len(m.Errs) == 0 {
681 return nil
682 }
683 return m.Errs[0]
684 }
685
686 var match func(mod module.Version, roots []string, isLocal bool) *search.Match
687 matchPattern := pkgpattern.MatchPattern(pattern)
688
689 if i := strings.Index(pattern, "..."); i >= 0 {
690 base = pathpkg.Dir(pattern[:i+3])
691 if base == "." {
692 return nil, nil, &WildcardInFirstElementError{Pattern: pattern, Query: query}
693 }
694 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
695 m := search.NewMatch(pattern)
696 matchPackages(ctx, m, imports.AnyTags(), omitStd, []module.Version{mod})
697 return m
698 }
699 } else {
700 match = func(mod module.Version, roots []string, isLocal bool) *search.Match {
701 m := search.NewMatch(pattern)
702 prefix := mod.Path
703 if MainModules.Contains(mod.Path) {
704 prefix = MainModules.PathPrefix(module.Version{Path: mod.Path})
705 }
706 for _, root := range roots {
707 if _, ok, err := dirInModule(pattern, prefix, root, isLocal); err != nil {
708 m.AddError(err)
709 } else if ok {
710 m.Pkgs = []string{pattern}
711 }
712 }
713 return m
714 }
715 }
716
717 var mainModuleMatches []module.Version
718 for _, mainModule := range MainModules.Versions() {
719 m := match(mainModule, modRoots, true)
720 if len(m.Pkgs) > 0 {
721 if query != "upgrade" && query != "patch" {
722 return nil, nil, &QueryMatchesPackagesInMainModuleError{
723 Pattern: pattern,
724 Query: query,
725 Packages: m.Pkgs,
726 }
727 }
728 if err := allowed(ctx, mainModule); err != nil {
729 return nil, nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed: %w", pattern, mainModule.Path, err)
730 }
731 return []QueryResult{{
732 Mod: mainModule,
733 Rev: &modfetch.RevInfo{Version: mainModule.Version},
734 Packages: m.Pkgs,
735 }}, nil, nil
736 }
737 if err := firstError(m); err != nil {
738 return nil, nil, err
739 }
740
741 var matchesMainModule bool
742 if matchPattern(mainModule.Path) {
743 mainModuleMatches = append(mainModuleMatches, mainModule)
744 matchesMainModule = true
745 }
746
747 if (query == "upgrade" || query == "patch") && matchesMainModule {
748 if err := allowed(ctx, mainModule); err == nil {
749 modOnly = &QueryResult{
750 Mod: mainModule,
751 Rev: &modfetch.RevInfo{Version: mainModule.Version},
752 }
753 }
754 }
755 }
756
757 var (
758 results []QueryResult
759 candidateModules = modulePrefixesExcludingTarget(base)
760 )
761 if len(candidateModules) == 0 {
762 if modOnly != nil {
763 return nil, modOnly, nil
764 } else if len(mainModuleMatches) != 0 {
765 return nil, nil, &QueryMatchesMainModulesError{
766 MainModules: mainModuleMatches,
767 Pattern: pattern,
768 Query: query,
769 }
770 } else {
771 return nil, nil, &PackageNotInModuleError{
772 MainModules: mainModuleMatches,
773 Query: query,
774 Pattern: pattern,
775 }
776 }
777 }
778
779 err = modfetch.TryProxies(func(proxy string) error {
780 queryModule := func(ctx context.Context, path string) (r QueryResult, err error) {
781 ctx, span := trace.StartSpan(ctx, "modload.QueryPattern.queryModule ["+proxy+"] "+path)
782 defer span.Done()
783
784 pathCurrent := current(path)
785 r.Mod.Path = path
786 r.Rev, err = queryProxy(ctx, proxy, path, query, pathCurrent, allowed, nil)
787 if err != nil {
788 return r, err
789 }
790 r.Mod.Version = r.Rev.Version
791 if gover.IsToolchain(r.Mod.Path) {
792 return r, nil
793 }
794 root, isLocal, err := fetch(ctx, r.Mod)
795 if err != nil {
796 return r, err
797 }
798 m := match(r.Mod, []string{root}, isLocal)
799 r.Packages = m.Pkgs
800 if len(r.Packages) == 0 && !matchPattern(path) {
801 if err := firstError(m); err != nil {
802 return r, err
803 }
804 replacement := Replacement(r.Mod)
805 return r, &PackageNotInModuleError{
806 Mod: r.Mod,
807 Replacement: replacement,
808 Query: query,
809 Pattern: pattern,
810 }
811 }
812 return r, nil
813 }
814
815 allResults, err := queryPrefixModules(ctx, candidateModules, queryModule)
816 results = allResults[:0]
817 for _, r := range allResults {
818 if len(r.Packages) == 0 {
819 modOnly = &r
820 } else {
821 results = append(results, r)
822 }
823 }
824 return err
825 })
826
827 if len(mainModuleMatches) > 0 && len(results) == 0 && modOnly == nil && errors.Is(err, fs.ErrNotExist) {
828 return nil, nil, &QueryMatchesMainModulesError{
829 Pattern: pattern,
830 Query: query,
831 }
832 }
833 return slices.Clip(results), modOnly, err
834 }
835
836
837
838
839
840
841 func modulePrefixesExcludingTarget(path string) []string {
842 prefixes := make([]string, 0, strings.Count(path, "/")+1)
843
844 mainModulePrefixes := make(map[string]bool)
845 for _, m := range MainModules.Versions() {
846 mainModulePrefixes[m.Path] = true
847 }
848
849 for {
850 if !mainModulePrefixes[path] {
851 if _, _, ok := module.SplitPathVersion(path); ok {
852 prefixes = append(prefixes, path)
853 }
854 }
855
856 j := strings.LastIndexByte(path, '/')
857 if j < 0 {
858 break
859 }
860 path = path[:j]
861 }
862
863 return prefixes
864 }
865
866 func queryPrefixModules(ctx context.Context, candidateModules []string, queryModule func(ctx context.Context, path string) (QueryResult, error)) (found []QueryResult, err error) {
867 ctx, span := trace.StartSpan(ctx, "modload.queryPrefixModules")
868 defer span.Done()
869
870
871
872
873
874 type result struct {
875 QueryResult
876 err error
877 }
878 results := make([]result, len(candidateModules))
879 var wg sync.WaitGroup
880 wg.Add(len(candidateModules))
881 for i, p := range candidateModules {
882 ctx := trace.StartGoroutine(ctx)
883 go func(p string, r *result) {
884 r.QueryResult, r.err = queryModule(ctx, p)
885 wg.Done()
886 }(p, &results[i])
887 }
888 wg.Wait()
889
890
891
892
893 var (
894 noPackage *PackageNotInModuleError
895 noVersion *NoMatchingVersionError
896 noPatchBase *NoPatchBaseError
897 invalidPath *module.InvalidPathError
898 invalidVersion error
899 notExistErr error
900 )
901 for _, r := range results {
902 switch rErr := r.err.(type) {
903 case nil:
904 found = append(found, r.QueryResult)
905 case *PackageNotInModuleError:
906
907
908 if noPackage == nil || MainModules.Contains(noPackage.Mod.Path) {
909 noPackage = rErr
910 }
911 case *NoMatchingVersionError:
912 if noVersion == nil {
913 noVersion = rErr
914 }
915 case *NoPatchBaseError:
916 if noPatchBase == nil {
917 noPatchBase = rErr
918 }
919 case *module.InvalidPathError:
920
921
922
923
924
925
926
927 if invalidPath == nil {
928 invalidPath = rErr
929 }
930 default:
931 if errors.Is(rErr, fs.ErrNotExist) {
932 if notExistErr == nil {
933 notExistErr = rErr
934 }
935 } else if iv := (*module.InvalidVersionError)(nil); errors.As(rErr, &iv) {
936 if invalidVersion == nil {
937 invalidVersion = rErr
938 }
939 } else if err == nil {
940 if len(found) > 0 || noPackage != nil {
941
942
943
944
945
946
947
948 } else {
949 err = r.err
950 }
951 }
952 }
953 }
954
955
956
957
958
959 if len(found) == 0 && err == nil {
960 switch {
961 case noPackage != nil:
962 err = noPackage
963 case noVersion != nil:
964 err = noVersion
965 case noPatchBase != nil:
966 err = noPatchBase
967 case invalidPath != nil:
968 err = invalidPath
969 case invalidVersion != nil:
970 err = invalidVersion
971 case notExistErr != nil:
972 err = notExistErr
973 default:
974 panic("queryPrefixModules: no modules found, but no error detected")
975 }
976 }
977
978 return found, err
979 }
980
981
982
983
984
985
986
987
988
989 type NoMatchingVersionError struct {
990 query, current string
991 }
992
993 func (e *NoMatchingVersionError) Error() string {
994 currentSuffix := ""
995 if (e.query == "upgrade" || e.query == "patch") && e.current != "" && e.current != "none" {
996 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current)
997 }
998 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix
999 }
1000
1001
1002
1003 type NoPatchBaseError struct {
1004 path string
1005 }
1006
1007 func (e *NoPatchBaseError) Error() string {
1008 return fmt.Sprintf(`can't query version "patch" of module %s: no existing version is required`, e.path)
1009 }
1010
1011
1012
1013
1014 type WildcardInFirstElementError struct {
1015 Pattern string
1016 Query string
1017 }
1018
1019 func (e *WildcardInFirstElementError) Error() string {
1020 return fmt.Sprintf("no modules to query for %s@%s because first path element contains a wildcard", e.Pattern, e.Query)
1021 }
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032 type PackageNotInModuleError struct {
1033 MainModules []module.Version
1034 Mod module.Version
1035 Replacement module.Version
1036 Query string
1037 Pattern string
1038 }
1039
1040 func (e *PackageNotInModuleError) Error() string {
1041 if len(e.MainModules) > 0 {
1042 prefix := "workspace modules do"
1043 if len(e.MainModules) == 1 {
1044 prefix = fmt.Sprintf("main module (%s) does", e.MainModules[0])
1045 }
1046 if strings.Contains(e.Pattern, "...") {
1047 return fmt.Sprintf("%s not contain packages matching %s", prefix, e.Pattern)
1048 }
1049 return fmt.Sprintf("%s not contain package %s", prefix, e.Pattern)
1050 }
1051
1052 found := ""
1053 if r := e.Replacement; r.Path != "" {
1054 replacement := r.Path
1055 if r.Version != "" {
1056 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version)
1057 }
1058 if e.Query == e.Mod.Version {
1059 found = fmt.Sprintf(" (replaced by %s)", replacement)
1060 } else {
1061 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement)
1062 }
1063 } else if e.Query != e.Mod.Version {
1064 found = fmt.Sprintf(" (%s)", e.Mod.Version)
1065 }
1066
1067 if strings.Contains(e.Pattern, "...") {
1068 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern)
1069 }
1070 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern)
1071 }
1072
1073 func (e *PackageNotInModuleError) ImportPath() string {
1074 if !strings.Contains(e.Pattern, "...") {
1075 return e.Pattern
1076 }
1077 return ""
1078 }
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099 func versionHasGoMod(_ context.Context, m module.Version) (bool, error) {
1100 _, data, err := rawGoModData(m)
1101 if err != nil {
1102 return false, err
1103 }
1104 isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path))
1105 return !isFake, nil
1106 }
1107
1108
1109
1110 type versionRepo interface {
1111 ModulePath() string
1112 CheckReuse(context.Context, *codehost.Origin) error
1113 Versions(ctx context.Context, prefix string) (*modfetch.Versions, error)
1114 Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error)
1115 Latest(context.Context) (*modfetch.RevInfo, error)
1116 }
1117
1118 var _ versionRepo = modfetch.Repo(nil)
1119
1120 func lookupRepo(ctx context.Context, proxy, path string) (repo versionRepo, err error) {
1121 if path != "go" && path != "toolchain" {
1122 err = module.CheckPath(path)
1123 }
1124 if err == nil {
1125 repo = modfetch.Lookup(ctx, proxy, path)
1126 } else {
1127 repo = emptyRepo{path: path, err: err}
1128 }
1129
1130 if MainModules == nil {
1131 return repo, err
1132 } else if _, ok := MainModules.HighestReplaced()[path]; ok {
1133 return &replacementRepo{repo: repo}, nil
1134 }
1135
1136 return repo, err
1137 }
1138
1139
1140 type emptyRepo struct {
1141 path string
1142 err error
1143 }
1144
1145 var _ versionRepo = emptyRepo{}
1146
1147 func (er emptyRepo) ModulePath() string { return er.path }
1148 func (er emptyRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
1149 return fmt.Errorf("empty repo")
1150 }
1151 func (er emptyRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
1152 return &modfetch.Versions{}, nil
1153 }
1154 func (er emptyRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1155 return nil, er.err
1156 }
1157 func (er emptyRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) { return nil, er.err }
1158
1159
1160
1161
1162
1163
1164
1165 type replacementRepo struct {
1166 repo versionRepo
1167 }
1168
1169 var _ versionRepo = (*replacementRepo)(nil)
1170
1171 func (rr *replacementRepo) ModulePath() string { return rr.repo.ModulePath() }
1172
1173 func (rr *replacementRepo) CheckReuse(ctx context.Context, old *codehost.Origin) error {
1174 return fmt.Errorf("replacement repo")
1175 }
1176
1177
1178
1179 func (rr *replacementRepo) Versions(ctx context.Context, prefix string) (*modfetch.Versions, error) {
1180 repoVersions, err := rr.repo.Versions(ctx, prefix)
1181 if err != nil {
1182 if !errors.Is(err, os.ErrNotExist) {
1183 return nil, err
1184 }
1185 repoVersions = new(modfetch.Versions)
1186 }
1187
1188 versions := repoVersions.List
1189 for _, mm := range MainModules.Versions() {
1190 if index := MainModules.Index(mm); index != nil && len(index.replace) > 0 {
1191 path := rr.ModulePath()
1192 for m := range index.replace {
1193 if m.Path == path && strings.HasPrefix(m.Version, prefix) && m.Version != "" && !module.IsPseudoVersion(m.Version) {
1194 versions = append(versions, m.Version)
1195 }
1196 }
1197 }
1198 }
1199
1200 if len(versions) == len(repoVersions.List) {
1201 return repoVersions, nil
1202 }
1203
1204 path := rr.ModulePath()
1205 sort.Slice(versions, func(i, j int) bool {
1206 return gover.ModCompare(path, versions[i], versions[j]) < 0
1207 })
1208 str.Uniq(&versions)
1209 return &modfetch.Versions{List: versions}, nil
1210 }
1211
1212 func (rr *replacementRepo) Stat(ctx context.Context, rev string) (*modfetch.RevInfo, error) {
1213 info, err := rr.repo.Stat(ctx, rev)
1214 if err == nil {
1215 return info, err
1216 }
1217 var hasReplacements bool
1218 for _, v := range MainModules.Versions() {
1219 if index := MainModules.Index(v); index != nil && len(index.replace) > 0 {
1220 hasReplacements = true
1221 }
1222 }
1223 if !hasReplacements {
1224 return info, err
1225 }
1226
1227 v := module.CanonicalVersion(rev)
1228 if v != rev {
1229
1230
1231 return info, err
1232 }
1233
1234 path := rr.ModulePath()
1235 _, pathMajor, ok := module.SplitPathVersion(path)
1236 if ok && pathMajor == "" {
1237 if err := module.CheckPathMajor(v, pathMajor); err != nil && semver.Build(v) == "" {
1238 v += "+incompatible"
1239 }
1240 }
1241
1242 if r := Replacement(module.Version{Path: path, Version: v}); r.Path == "" {
1243 return info, err
1244 }
1245 return rr.replacementStat(v)
1246 }
1247
1248 func (rr *replacementRepo) Latest(ctx context.Context) (*modfetch.RevInfo, error) {
1249 info, err := rr.repo.Latest(ctx)
1250 path := rr.ModulePath()
1251
1252 if v, ok := MainModules.HighestReplaced()[path]; ok {
1253 if v == "" {
1254
1255
1256
1257
1258
1259 if _, pathMajor, ok := module.SplitPathVersion(path); ok && len(pathMajor) > 0 {
1260 v = module.PseudoVersion(pathMajor[1:], "", time.Time{}, "000000000000")
1261 } else {
1262 v = module.PseudoVersion("v0", "", time.Time{}, "000000000000")
1263 }
1264 }
1265
1266 if err != nil || gover.ModCompare(path, v, info.Version) > 0 {
1267 return rr.replacementStat(v)
1268 }
1269 }
1270
1271 return info, err
1272 }
1273
1274 func (rr *replacementRepo) replacementStat(v string) (*modfetch.RevInfo, error) {
1275 rev := &modfetch.RevInfo{Version: v}
1276 if module.IsPseudoVersion(v) {
1277 rev.Time, _ = module.PseudoVersionTime(v)
1278 rev.Short, _ = module.PseudoVersionRev(v)
1279 }
1280 return rev, nil
1281 }
1282
1283
1284
1285
1286 type QueryMatchesMainModulesError struct {
1287 MainModules []module.Version
1288 Pattern string
1289 Query string
1290 }
1291
1292 func (e *QueryMatchesMainModulesError) Error() string {
1293 if MainModules.Contains(e.Pattern) {
1294 return fmt.Sprintf("can't request version %q of the main module (%s)", e.Query, e.Pattern)
1295 }
1296
1297 plural := ""
1298 mainModulePaths := make([]string, len(e.MainModules))
1299 for i := range e.MainModules {
1300 mainModulePaths[i] = e.MainModules[i].Path
1301 }
1302 if len(e.MainModules) > 1 {
1303 plural = "s"
1304 }
1305 return fmt.Sprintf("can't request version %q of pattern %q that includes the main module%s (%s)", e.Query, e.Pattern, plural, strings.Join(mainModulePaths, ", "))
1306 }
1307
1308
1309
1310
1311 type QueryUpgradesAllError struct {
1312 MainModules []module.Version
1313 Query string
1314 }
1315
1316 func (e *QueryUpgradesAllError) Error() string {
1317 var plural string = ""
1318 if len(e.MainModules) != 1 {
1319 plural = "s"
1320 }
1321
1322 return fmt.Sprintf("can't request version %q of pattern \"all\" that includes the main module%s", e.Query, plural)
1323 }
1324
1325
1326
1327 type QueryMatchesPackagesInMainModuleError struct {
1328 Pattern string
1329 Query string
1330 Packages []string
1331 }
1332
1333 func (e *QueryMatchesPackagesInMainModuleError) Error() string {
1334 if len(e.Packages) > 1 {
1335 return fmt.Sprintf("pattern %s matches %d packages in the main module, so can't request version %s", e.Pattern, len(e.Packages), e.Query)
1336 }
1337
1338 if search.IsMetaPackage(e.Pattern) || strings.Contains(e.Pattern, "...") {
1339 return fmt.Sprintf("pattern %s matches package %s in the main module, so can't request version %s", e.Pattern, e.Packages[0], e.Query)
1340 }
1341
1342 return fmt.Sprintf("package %s is in the main module, so can't request version %s", e.Packages[0], e.Query)
1343 }
1344
View as plain text