github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/go/not-internal/modconv/dep.go (about) 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 5 package modconv 6 7 import ( 8 "fmt" 9 "github.com/gagliardetto/golang-go/not-internal/lazyregexp" 10 "net/url" 11 "path" 12 "strconv" 13 "strings" 14 15 "golang.org/x/mod/modfile" 16 "golang.org/x/mod/module" 17 "golang.org/x/mod/semver" 18 ) 19 20 func ParseGopkgLock(file string, data []byte) (*modfile.File, error) { 21 type pkg struct { 22 Path string 23 Version string 24 Source string 25 } 26 mf := new(modfile.File) 27 var list []pkg 28 var r *pkg 29 for lineno, line := range strings.Split(string(data), "\n") { 30 lineno++ 31 if i := strings.Index(line, "#"); i >= 0 { 32 line = line[:i] 33 } 34 line = strings.TrimSpace(line) 35 if line == "[[projects]]" { 36 list = append(list, pkg{}) 37 r = &list[len(list)-1] 38 continue 39 } 40 if strings.HasPrefix(line, "[") { 41 r = nil 42 continue 43 } 44 if r == nil { 45 continue 46 } 47 i := strings.Index(line, "=") 48 if i < 0 { 49 continue 50 } 51 key := strings.TrimSpace(line[:i]) 52 val := strings.TrimSpace(line[i+1:]) 53 if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' { 54 q, err := strconv.Unquote(val) // Go unquoting, but close enough for now 55 if err != nil { 56 return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err) 57 } 58 val = q 59 } 60 switch key { 61 case "name": 62 r.Path = val 63 case "source": 64 r.Source = val 65 case "revision", "version": 66 // Note: key "version" should take priority over "revision", 67 // and it does, because dep writes toml keys in alphabetical order, 68 // so we see version (if present) second. 69 if key == "version" { 70 if !semver.IsValid(val) || semver.Canonical(val) != val { 71 break 72 } 73 } 74 r.Version = val 75 } 76 } 77 for _, r := range list { 78 if r.Path == "" || r.Version == "" { 79 return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path) 80 } 81 mf.Require = append(mf.Require, &modfile.Require{Mod: module.Version{Path: r.Path, Version: r.Version}}) 82 83 if r.Source != "" { 84 // Convert "source" to import path, such as 85 // git@test.com:x/y.git and https://test.com/x/y.git. 86 // We get "test.com/x/y" at last. 87 source, err := decodeSource(r.Source) 88 if err != nil { 89 return nil, err 90 } 91 old := module.Version{Path: r.Path, Version: r.Version} 92 new := module.Version{Path: source, Version: r.Version} 93 mf.Replace = append(mf.Replace, &modfile.Replace{Old: old, New: new}) 94 } 95 } 96 return mf, nil 97 } 98 99 var scpSyntaxReg = lazyregexp.New(`^([a-zA-Z0-9_]+)@([a-zA-Z0-9._-]+):(.*)$`) 100 101 func decodeSource(source string) (string, error) { 102 var u *url.URL 103 var p string 104 if m := scpSyntaxReg.FindStringSubmatch(source); m != nil { 105 // Match SCP-like syntax and convert it to a URL. 106 // Eg, "git@github.com:user/repo" becomes 107 // "ssh://git@github.com/user/repo". 108 u = &url.URL{ 109 Scheme: "ssh", 110 User: url.User(m[1]), 111 Host: m[2], 112 Path: "/" + m[3], 113 } 114 } else { 115 var err error 116 u, err = url.Parse(source) 117 if err != nil { 118 return "", fmt.Errorf("%q is not a valid URI", source) 119 } 120 } 121 122 // If no scheme was passed, then the entire path will have been put into 123 // u.Path. Either way, construct the normalized path correctly. 124 if u.Host == "" { 125 p = source 126 } else { 127 p = path.Join(u.Host, u.Path) 128 } 129 p = strings.TrimSuffix(p, ".git") 130 p = strings.TrimSuffix(p, ".hg") 131 return p, nil 132 }