-
Notifications
You must be signed in to change notification settings - Fork 354
/
parse-go-lock.go
106 lines (83 loc) · 2.57 KB
/
parse-go-lock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package lockfile
import (
"fmt"
"io"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
)
const GoEcosystem Ecosystem = "Go"
func deduplicatePackages(packages map[string]PackageDetails) map[string]PackageDetails {
details := map[string]PackageDetails{}
for _, detail := range packages {
details[detail.Name+"@"+detail.Version] = detail
}
return details
}
type GoLockExtractor struct{}
func (e GoLockExtractor) ShouldExtract(path string) bool {
return filepath.Base(path) == "go.mod"
}
func (e GoLockExtractor) Extract(f DepFile) ([]PackageDetails, error) {
var parsedLockfile *modfile.File
b, err := io.ReadAll(f)
if err == nil {
parsedLockfile, err = modfile.Parse(f.Path(), b, nil)
}
if err != nil {
return []PackageDetails{}, fmt.Errorf("could not extract from %s: %w", f.Path(), err)
}
packages := map[string]PackageDetails{}
for _, require := range parsedLockfile.Require {
packages[require.Mod.Path+"@"+require.Mod.Version] = PackageDetails{
Name: require.Mod.Path,
Version: strings.TrimPrefix(require.Mod.Version, "v"),
Ecosystem: GoEcosystem,
CompareAs: GoEcosystem,
}
}
for _, replace := range parsedLockfile.Replace {
var replacements []string
if replace.Old.Version == "" {
// If the left version is omitted, all versions of the module are replaced.
for k, pkg := range packages {
if pkg.Name == replace.Old.Path {
replacements = append(replacements, k)
}
}
} else {
// If a version is present on the left side of the arrow (=>),
// only that specific version of the module is replaced
s := replace.Old.Path + "@" + replace.Old.Version
// A `replace` directive has no effect if the module version on the left side is not required.
if _, ok := packages[s]; ok {
replacements = []string{s}
}
}
for _, replacement := range replacements {
packages[replacement] = PackageDetails{
Name: replace.New.Path,
Version: strings.TrimPrefix(replace.New.Version, "v"),
Ecosystem: GoEcosystem,
CompareAs: GoEcosystem,
}
}
}
if parsedLockfile.Go != nil && parsedLockfile.Go.Version != "" {
packages["stdlib"] = PackageDetails{
Name: "stdlib",
Version: parsedLockfile.Go.Version,
Ecosystem: GoEcosystem,
CompareAs: GoEcosystem,
}
}
return pkgDetailsMapToSlice(deduplicatePackages(packages)), nil
}
var _ Extractor = GoLockExtractor{}
//nolint:gochecknoinits
func init() {
registerExtractor("go.mod", GoLockExtractor{})
}
func ParseGoLock(pathToLockfile string) ([]PackageDetails, error) {
return extractFromFile(pathToLockfile, GoLockExtractor{})
}