1// Copyright 2018 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package modload 6 7import ( 8 "errors" 9 "fmt" 10 "os" 11 pathpkg "path" 12 "path/filepath" 13 "strings" 14 "sync" 15 16 "cmd/go/internal/cfg" 17 "cmd/go/internal/imports" 18 "cmd/go/internal/modfetch" 19 "cmd/go/internal/search" 20 "cmd/go/internal/str" 21 22 "golang.org/x/mod/module" 23 "golang.org/x/mod/semver" 24) 25 26// Query looks up a revision of a given module given a version query string. 27// The module must be a complete module path. 28// The version must take one of the following forms: 29// 30// - the literal string "latest", denoting the latest available, allowed 31// tagged version, with non-prereleases preferred over prereleases. 32// If there are no tagged versions in the repo, latest returns the most 33// recent commit. 34// - the literal string "upgrade", equivalent to "latest" except that if 35// current is a newer version, current will be returned (see below). 36// - the literal string "patch", denoting the latest available tagged version 37// with the same major and minor number as current (see below). 38// - v1, denoting the latest available tagged version v1.x.x. 39// - v1.2, denoting the latest available tagged version v1.2.x. 40// - v1.2.3, a semantic version string denoting that tagged version. 41// - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3, 42// denoting the version closest to the target and satisfying the given operator, 43// with non-prereleases preferred over prereleases. 44// - a repository commit identifier or tag, denoting that commit. 45// 46// current denotes the current version of the module; it may be "" if the 47// current version is unknown or should not be considered. If query is 48// "upgrade" or "patch", current will be returned if it is a newer 49// semantic version or a chronologically later pseudo-version than the 50// version that would otherwise be chosen. This prevents accidental downgrades 51// from newer pre-release or development versions. 52// 53// If the allowed function is non-nil, Query excludes any versions for which 54// allowed returns false. 55// 56// If path is the path of the main module and the query is "latest", 57// Query returns Target.Version as the version. 58func Query(path, query, current string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) { 59 var info *modfetch.RevInfo 60 err := modfetch.TryProxies(func(proxy string) (err error) { 61 info, err = queryProxy(proxy, path, query, current, allowed) 62 return err 63 }) 64 return info, err 65} 66 67var errQueryDisabled error = queryDisabledError{} 68 69type queryDisabledError struct{} 70 71func (queryDisabledError) Error() string { 72 if cfg.BuildModReason == "" { 73 return fmt.Sprintf("cannot query module due to -mod=%s", cfg.BuildMod) 74 } 75 return fmt.Sprintf("cannot query module due to -mod=%s\n\t(%s)", cfg.BuildMod, cfg.BuildModReason) 76} 77 78func queryProxy(proxy, path, query, current string, allowed func(module.Version) bool) (*modfetch.RevInfo, error) { 79 if current != "" && !semver.IsValid(current) { 80 return nil, fmt.Errorf("invalid previous version %q", current) 81 } 82 if cfg.BuildMod == "vendor" { 83 return nil, errQueryDisabled 84 } 85 if allowed == nil { 86 allowed = func(module.Version) bool { return true } 87 } 88 89 // Parse query to detect parse errors (and possibly handle query) 90 // before any network I/O. 91 badVersion := func(v string) (*modfetch.RevInfo, error) { 92 return nil, fmt.Errorf("invalid semantic version %q in range %q", v, query) 93 } 94 matchesMajor := func(v string) bool { 95 _, pathMajor, ok := module.SplitPathVersion(path) 96 if !ok { 97 return false 98 } 99 return module.CheckPathMajor(v, pathMajor) == nil 100 } 101 var ( 102 ok func(module.Version) bool 103 prefix string 104 preferOlder bool 105 mayUseLatest bool 106 preferIncompatible bool = strings.HasSuffix(current, "+incompatible") 107 ) 108 switch { 109 case query == "latest": 110 ok = allowed 111 mayUseLatest = true 112 113 case query == "upgrade": 114 ok = allowed 115 mayUseLatest = true 116 117 case query == "patch": 118 if current == "" { 119 ok = allowed 120 mayUseLatest = true 121 } else { 122 prefix = semver.MajorMinor(current) 123 ok = func(m module.Version) bool { 124 return matchSemverPrefix(prefix, m.Version) && allowed(m) 125 } 126 } 127 128 case strings.HasPrefix(query, "<="): 129 v := query[len("<="):] 130 if !semver.IsValid(v) { 131 return badVersion(v) 132 } 133 if isSemverPrefix(v) { 134 // Refuse to say whether <=v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). 135 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) 136 } 137 ok = func(m module.Version) bool { 138 return semver.Compare(m.Version, v) <= 0 && allowed(m) 139 } 140 if !matchesMajor(v) { 141 preferIncompatible = true 142 } 143 144 case strings.HasPrefix(query, "<"): 145 v := query[len("<"):] 146 if !semver.IsValid(v) { 147 return badVersion(v) 148 } 149 ok = func(m module.Version) bool { 150 return semver.Compare(m.Version, v) < 0 && allowed(m) 151 } 152 if !matchesMajor(v) { 153 preferIncompatible = true 154 } 155 156 case strings.HasPrefix(query, ">="): 157 v := query[len(">="):] 158 if !semver.IsValid(v) { 159 return badVersion(v) 160 } 161 ok = func(m module.Version) bool { 162 return semver.Compare(m.Version, v) >= 0 && allowed(m) 163 } 164 preferOlder = true 165 if !matchesMajor(v) { 166 preferIncompatible = true 167 } 168 169 case strings.HasPrefix(query, ">"): 170 v := query[len(">"):] 171 if !semver.IsValid(v) { 172 return badVersion(v) 173 } 174 if isSemverPrefix(v) { 175 // Refuse to say whether >v1.2 allows v1.2.3 (remember, @v1.2 might mean v1.2.3). 176 return nil, fmt.Errorf("ambiguous semantic version %q in range %q", v, query) 177 } 178 ok = func(m module.Version) bool { 179 return semver.Compare(m.Version, v) > 0 && allowed(m) 180 } 181 preferOlder = true 182 if !matchesMajor(v) { 183 preferIncompatible = true 184 } 185 186 case semver.IsValid(query) && isSemverPrefix(query): 187 ok = func(m module.Version) bool { 188 return matchSemverPrefix(query, m.Version) && allowed(m) 189 } 190 prefix = query + "." 191 if !matchesMajor(query) { 192 preferIncompatible = true 193 } 194 195 default: 196 // Direct lookup of semantic version or commit identifier. 197 // 198 // If the identifier is not a canonical semver tag — including if it's a 199 // semver tag with a +metadata suffix — then modfetch.Stat will populate 200 // info.Version with a suitable pseudo-version. 201 info, err := modfetch.Stat(proxy, path, query) 202 if err != nil { 203 queryErr := err 204 // The full query doesn't correspond to a tag. If it is a semantic version 205 // with a +metadata suffix, see if there is a tag without that suffix: 206 // semantic versioning defines them to be equivalent. 207 if vers := module.CanonicalVersion(query); vers != "" && vers != query { 208 info, err = modfetch.Stat(proxy, path, vers) 209 if !errors.Is(err, os.ErrNotExist) { 210 return info, err 211 } 212 } 213 if err != nil { 214 return nil, queryErr 215 } 216 } 217 if !allowed(module.Version{Path: path, Version: info.Version}) { 218 return nil, fmt.Errorf("%s@%s excluded", path, info.Version) 219 } 220 return info, nil 221 } 222 223 if path == Target.Path { 224 if query != "latest" { 225 return nil, fmt.Errorf("can't query specific version (%q) for the main module (%s)", query, path) 226 } 227 if !allowed(Target) { 228 return nil, fmt.Errorf("internal error: main module version is not allowed") 229 } 230 return &modfetch.RevInfo{Version: Target.Version}, nil 231 } 232 233 if str.HasPathPrefix(path, "std") || str.HasPathPrefix(path, "cmd") { 234 return nil, fmt.Errorf("explicit requirement on standard-library module %s not allowed", path) 235 } 236 237 // Load versions and execute query. 238 repo, err := modfetch.Lookup(proxy, path) 239 if err != nil { 240 return nil, err 241 } 242 versions, err := repo.Versions(prefix) 243 if err != nil { 244 return nil, err 245 } 246 releases, prereleases, err := filterVersions(path, versions, ok, preferIncompatible) 247 if err != nil { 248 return nil, err 249 } 250 251 lookup := func(v string) (*modfetch.RevInfo, error) { 252 rev, err := repo.Stat(v) 253 if err != nil { 254 return nil, err 255 } 256 257 // For "upgrade" and "patch", make sure we don't accidentally downgrade 258 // from a newer prerelease or from a chronologically newer pseudoversion. 259 if current != "" && (query == "upgrade" || query == "patch") { 260 currentTime, err := modfetch.PseudoVersionTime(current) 261 if semver.Compare(rev.Version, current) < 0 || (err == nil && rev.Time.Before(currentTime)) { 262 return repo.Stat(current) 263 } 264 } 265 266 return rev, nil 267 } 268 269 if preferOlder { 270 if len(releases) > 0 { 271 return lookup(releases[0]) 272 } 273 if len(prereleases) > 0 { 274 return lookup(prereleases[0]) 275 } 276 } else { 277 if len(releases) > 0 { 278 return lookup(releases[len(releases)-1]) 279 } 280 if len(prereleases) > 0 { 281 return lookup(prereleases[len(prereleases)-1]) 282 } 283 } 284 285 if mayUseLatest { 286 // Special case for "latest": if no tags match, use latest commit in repo, 287 // provided it is not excluded. 288 latest, err := repo.Latest() 289 if err == nil { 290 if allowed(module.Version{Path: path, Version: latest.Version}) { 291 return lookup(latest.Version) 292 } 293 } else if !errors.Is(err, os.ErrNotExist) { 294 return nil, err 295 } 296 } 297 298 return nil, &NoMatchingVersionError{query: query, current: current} 299} 300 301// isSemverPrefix reports whether v is a semantic version prefix: v1 or v1.2 (not v1.2.3). 302// The caller is assumed to have checked that semver.IsValid(v) is true. 303func isSemverPrefix(v string) bool { 304 dots := 0 305 for i := 0; i < len(v); i++ { 306 switch v[i] { 307 case '-', '+': 308 return false 309 case '.': 310 dots++ 311 if dots >= 2 { 312 return false 313 } 314 } 315 } 316 return true 317} 318 319// matchSemverPrefix reports whether the shortened semantic version p 320// matches the full-width (non-shortened) semantic version v. 321func matchSemverPrefix(p, v string) bool { 322 return len(v) > len(p) && v[len(p)] == '.' && v[:len(p)] == p && semver.Prerelease(v) == "" 323} 324 325// filterVersions classifies versions into releases and pre-releases, filtering 326// out: 327// 1. versions that do not satisfy the 'ok' predicate, and 328// 2. "+incompatible" versions, if a compatible one satisfies the predicate 329// and the incompatible version is not preferred. 330func filterVersions(path string, versions []string, ok func(module.Version) bool, preferIncompatible bool) (releases, prereleases []string, err error) { 331 var lastCompatible string 332 for _, v := range versions { 333 if !ok(module.Version{Path: path, Version: v}) { 334 continue 335 } 336 337 if !preferIncompatible { 338 if !strings.HasSuffix(v, "+incompatible") { 339 lastCompatible = v 340 } else if lastCompatible != "" { 341 // If the latest compatible version is allowed and has a go.mod file, 342 // ignore any version with a higher (+incompatible) major version. (See 343 // https://golang.org/issue/34165.) Note that we even prefer a 344 // compatible pre-release over an incompatible release. 345 346 ok, err := versionHasGoMod(module.Version{Path: path, Version: lastCompatible}) 347 if err != nil { 348 return nil, nil, err 349 } 350 if ok { 351 break 352 } 353 354 // No acceptable compatible release has a go.mod file, so the versioning 355 // for the module might not be module-aware, and we should respect 356 // legacy major-version tags. 357 preferIncompatible = true 358 } 359 } 360 361 if semver.Prerelease(v) != "" { 362 prereleases = append(prereleases, v) 363 } else { 364 releases = append(releases, v) 365 } 366 } 367 368 return releases, prereleases, nil 369} 370 371type QueryResult struct { 372 Mod module.Version 373 Rev *modfetch.RevInfo 374 Packages []string 375} 376 377// QueryPackage looks up the module(s) containing path at a revision matching 378// query. The results are sorted by module path length in descending order. 379// 380// If the package is in the main module, QueryPackage considers only the main 381// module and only the version "latest", without checking for other possible 382// modules. 383func QueryPackage(path, query string, allowed func(module.Version) bool) ([]QueryResult, error) { 384 if search.IsMetaPackage(path) || strings.Contains(path, "...") { 385 return nil, fmt.Errorf("pattern %s is not an importable package", path) 386 } 387 return QueryPattern(path, query, allowed) 388} 389 390// QueryPattern looks up the module(s) containing at least one package matching 391// the given pattern at the given version. The results are sorted by module path 392// length in descending order. 393// 394// QueryPattern queries modules with package paths up to the first "..." 395// in the pattern. For the pattern "example.com/a/b.../c", QueryPattern would 396// consider prefixes of "example.com/a". If multiple modules have versions 397// that match the query and packages that match the pattern, QueryPattern 398// picks the one with the longest module path. 399// 400// If any matching package is in the main module, QueryPattern considers only 401// the main module and only the version "latest", without checking for other 402// possible modules. 403func QueryPattern(pattern, query string, allowed func(module.Version) bool) ([]QueryResult, error) { 404 base := pattern 405 var match func(m module.Version, root string, isLocal bool) (pkgs []string) 406 407 if i := strings.Index(pattern, "..."); i >= 0 { 408 base = pathpkg.Dir(pattern[:i+3]) 409 match = func(m module.Version, root string, isLocal bool) []string { 410 return matchPackages(pattern, imports.AnyTags(), false, []module.Version{m}) 411 } 412 } else { 413 match = func(m module.Version, root string, isLocal bool) []string { 414 prefix := m.Path 415 if m == Target { 416 prefix = targetPrefix 417 } 418 if _, ok := dirInModule(pattern, prefix, root, isLocal); ok { 419 return []string{pattern} 420 } else { 421 return nil 422 } 423 } 424 } 425 426 if HasModRoot() { 427 pkgs := match(Target, modRoot, true) 428 if len(pkgs) > 0 { 429 if query != "latest" { 430 return nil, fmt.Errorf("can't query specific version for package %s in the main module (%s)", pattern, Target.Path) 431 } 432 if !allowed(Target) { 433 return nil, fmt.Errorf("internal error: package %s is in the main module (%s), but version is not allowed", pattern, Target.Path) 434 } 435 return []QueryResult{{ 436 Mod: Target, 437 Rev: &modfetch.RevInfo{Version: Target.Version}, 438 Packages: pkgs, 439 }}, nil 440 } 441 } 442 443 var ( 444 results []QueryResult 445 candidateModules = modulePrefixesExcludingTarget(base) 446 ) 447 if len(candidateModules) == 0 { 448 return nil, fmt.Errorf("package %s is not in the main module (%s)", pattern, Target.Path) 449 } 450 451 err := modfetch.TryProxies(func(proxy string) error { 452 queryModule := func(path string) (r QueryResult, err error) { 453 r.Mod.Path = path 454 r.Rev, err = queryProxy(proxy, path, query, "", allowed) 455 if err != nil { 456 return r, err 457 } 458 r.Mod.Version = r.Rev.Version 459 root, isLocal, err := fetch(r.Mod) 460 if err != nil { 461 return r, err 462 } 463 r.Packages = match(r.Mod, root, isLocal) 464 if len(r.Packages) == 0 { 465 return r, &PackageNotInModuleError{ 466 Mod: r.Mod, 467 Replacement: Replacement(r.Mod), 468 Query: query, 469 Pattern: pattern, 470 } 471 } 472 return r, nil 473 } 474 475 var err error 476 results, err = queryPrefixModules(candidateModules, queryModule) 477 return err 478 }) 479 480 return results, err 481} 482 483// modulePrefixesExcludingTarget returns all prefixes of path that may plausibly 484// exist as a module, excluding targetPrefix but otherwise including path 485// itself, sorted by descending length. 486func modulePrefixesExcludingTarget(path string) []string { 487 prefixes := make([]string, 0, strings.Count(path, "/")+1) 488 489 for { 490 if path != targetPrefix { 491 if _, _, ok := module.SplitPathVersion(path); ok { 492 prefixes = append(prefixes, path) 493 } 494 } 495 496 j := strings.LastIndexByte(path, '/') 497 if j < 0 { 498 break 499 } 500 path = path[:j] 501 } 502 503 return prefixes 504} 505 506type prefixResult struct { 507 QueryResult 508 err error 509} 510 511func queryPrefixModules(candidateModules []string, queryModule func(path string) (QueryResult, error)) (found []QueryResult, err error) { 512 // If the path we're attempting is not in the module cache and we don't have a 513 // fetch result cached either, we'll end up making a (potentially slow) 514 // request to the proxy or (often even slower) the origin server. 515 // To minimize latency, execute all of those requests in parallel. 516 type result struct { 517 QueryResult 518 err error 519 } 520 results := make([]result, len(candidateModules)) 521 var wg sync.WaitGroup 522 wg.Add(len(candidateModules)) 523 for i, p := range candidateModules { 524 go func(p string, r *result) { 525 r.QueryResult, r.err = queryModule(p) 526 wg.Done() 527 }(p, &results[i]) 528 } 529 wg.Wait() 530 531 // Classify the results. In case of failure, identify the error that the user 532 // is most likely to find helpful: the most useful class of error at the 533 // longest matching path. 534 var ( 535 noPackage *PackageNotInModuleError 536 noVersion *NoMatchingVersionError 537 notExistErr error 538 ) 539 for _, r := range results { 540 switch rErr := r.err.(type) { 541 case nil: 542 found = append(found, r.QueryResult) 543 case *PackageNotInModuleError: 544 if noPackage == nil { 545 noPackage = rErr 546 } 547 case *NoMatchingVersionError: 548 if noVersion == nil { 549 noVersion = rErr 550 } 551 default: 552 if errors.Is(rErr, os.ErrNotExist) { 553 if notExistErr == nil { 554 notExistErr = rErr 555 } 556 } else if err == nil { 557 if len(found) > 0 || noPackage != nil { 558 // golang.org/issue/34094: If we have already found a module that 559 // could potentially contain the target package, ignore unclassified 560 // errors for modules with shorter paths. 561 562 // golang.org/issue/34383 is a special case of this: if we have 563 // already found example.com/foo/v2@v2.0.0 with a matching go.mod 564 // file, ignore the error from example.com/foo@v2.0.0. 565 } else { 566 err = r.err 567 } 568 } 569 } 570 } 571 572 // TODO(#26232): If len(found) == 0 and some of the errors are 4xx HTTP 573 // codes, have the auth package recheck the failed paths. 574 // If we obtain new credentials for any of them, re-run the above loop. 575 576 if len(found) == 0 && err == nil { 577 switch { 578 case noPackage != nil: 579 err = noPackage 580 case noVersion != nil: 581 err = noVersion 582 case notExistErr != nil: 583 err = notExistErr 584 default: 585 panic("queryPrefixModules: no modules found, but no error detected") 586 } 587 } 588 589 return found, err 590} 591 592// A NoMatchingVersionError indicates that Query found a module at the requested 593// path, but not at any versions satisfying the query string and allow-function. 594// 595// NOTE: NoMatchingVersionError MUST NOT implement Is(os.ErrNotExist). 596// 597// If the module came from a proxy, that proxy had to return a successful status 598// code for the versions it knows about, and thus did not have the opportunity 599// to return a non-400 status code to suppress fallback. 600type NoMatchingVersionError struct { 601 query, current string 602} 603 604func (e *NoMatchingVersionError) Error() string { 605 currentSuffix := "" 606 if (e.query == "upgrade" || e.query == "patch") && e.current != "" { 607 currentSuffix = fmt.Sprintf(" (current version is %s)", e.current) 608 } 609 return fmt.Sprintf("no matching versions for query %q", e.query) + currentSuffix 610} 611 612// A PackageNotInModuleError indicates that QueryPattern found a candidate 613// module at the requested version, but that module did not contain any packages 614// matching the requested pattern. 615// 616// NOTE: PackageNotInModuleError MUST NOT implement Is(os.ErrNotExist). 617// 618// If the module came from a proxy, that proxy had to return a successful status 619// code for the versions it knows about, and thus did not have the opportunity 620// to return a non-400 status code to suppress fallback. 621type PackageNotInModuleError struct { 622 Mod module.Version 623 Replacement module.Version 624 Query string 625 Pattern string 626} 627 628func (e *PackageNotInModuleError) Error() string { 629 found := "" 630 if r := e.Replacement; r.Path != "" { 631 replacement := r.Path 632 if r.Version != "" { 633 replacement = fmt.Sprintf("%s@%s", r.Path, r.Version) 634 } 635 if e.Query == e.Mod.Version { 636 found = fmt.Sprintf(" (replaced by %s)", replacement) 637 } else { 638 found = fmt.Sprintf(" (%s, replaced by %s)", e.Mod.Version, replacement) 639 } 640 } else if e.Query != e.Mod.Version { 641 found = fmt.Sprintf(" (%s)", e.Mod.Version) 642 } 643 644 if strings.Contains(e.Pattern, "...") { 645 return fmt.Sprintf("module %s@%s found%s, but does not contain packages matching %s", e.Mod.Path, e.Query, found, e.Pattern) 646 } 647 return fmt.Sprintf("module %s@%s found%s, but does not contain package %s", e.Mod.Path, e.Query, found, e.Pattern) 648} 649 650// ModuleHasRootPackage returns whether module m contains a package m.Path. 651func ModuleHasRootPackage(m module.Version) (bool, error) { 652 root, isLocal, err := fetch(m) 653 if err != nil { 654 return false, err 655 } 656 _, ok := dirInModule(m.Path, m.Path, root, isLocal) 657 return ok, nil 658} 659 660func versionHasGoMod(m module.Version) (bool, error) { 661 root, _, err := fetch(m) 662 if err != nil { 663 return false, err 664 } 665 fi, err := os.Stat(filepath.Join(root, "go.mod")) 666 return err == nil && !fi.IsDir(), nil 667} 668