1 package modload
2
3 import (
4 "context"
5 "fmt"
6 "io/fs"
7 "runtime"
8 "strings"
9 "sync/atomic"
10
11 "cuelang.org/go/internal/mod/modrequirements"
12 "cuelang.org/go/internal/mod/semver"
13 "cuelang.org/go/internal/par"
14 "cuelang.org/go/mod/modfile"
15 "cuelang.org/go/mod/module"
16 )
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 func UpdateVersions(ctx context.Context, fsys fs.FS, modRoot string, reg Registry, versions []string) (*modfile.File, error) {
32 mainModuleVersion, mf, err := readModuleFile(ctx, fsys, modRoot)
33 if err != nil {
34 return nil, err
35 }
36 rs := modrequirements.NewRequirements(mf.Module, reg, mf.DepVersions(), mf.DefaultMajorVersions())
37 mversions, err := resolveUpdateVersions(ctx, reg, rs, mainModuleVersion, versions)
38 if err != nil {
39 return nil, err
40 }
41
42
43
44 mversionsMap := make(map[string]module.Version)
45 for _, v := range mversions {
46
47
48 if v1, ok := mversionsMap[v.Path()]; ok && v1.Version() != v.Version() {
49
50
51
52
53 return nil, fmt.Errorf("conflicting version update requirements %v vs %v", v1, v)
54 }
55 mversionsMap[v.Path()] = v
56 }
57 g, err := rs.Graph(ctx)
58 if err != nil {
59 return nil, fmt.Errorf("cannot determine module graph: %v", err)
60 }
61 var newVersions []module.Version
62 for _, v := range g.BuildList() {
63 if v.Path() == mainModuleVersion.Path() {
64 continue
65 }
66 if newv, ok := mversionsMap[v.Path()]; ok {
67 newVersions = append(newVersions, newv)
68 delete(mversionsMap, v.Path())
69 } else {
70 newVersions = append(newVersions, v)
71 }
72 }
73 for _, v := range mversionsMap {
74 newVersions = append(newVersions, v)
75 }
76 rs = modrequirements.NewRequirements(mf.Module, reg, newVersions, mf.DefaultMajorVersions())
77 g, err = rs.Graph(ctx)
78 if err != nil {
79 return nil, fmt.Errorf("cannot determine new module graph: %v", err)
80 }
81
82 for _, v := range mversions {
83 actualVers := g.Selected(v.Path())
84 if actualVers != v.Version() {
85 return nil, fmt.Errorf("other requirements prevent changing module %v to version %v (actual selected version: %v)", v.Path(), v.Version(), actualVers)
86 }
87 }
88
89 var finalVersions []module.Version
90 for _, v := range g.BuildList() {
91 if v.Path() != mainModuleVersion.Path() {
92 finalVersions = append(finalVersions, v)
93 }
94 }
95 rs = modrequirements.NewRequirements(mf.Module, reg, finalVersions, mf.DefaultMajorVersions())
96 return modfileFromRequirements(mf, rs, ""), nil
97 }
98
99
100
101 func resolveUpdateVersions(ctx context.Context, reg Registry, rs *modrequirements.Requirements, mainModuleVersion module.Version, versions []string) ([]module.Version, error) {
102 work := par.NewQueue(runtime.GOMAXPROCS(0))
103 mversions := make([]module.Version, len(versions))
104 var queryErr atomic.Pointer[error]
105 setError := func(err error) {
106 queryErr.CompareAndSwap(nil, &err)
107 }
108 for i, v := range versions {
109 i, v := i, v
110 if mv, err := module.ParseVersion(v); err == nil {
111
112 mversions[i] = mv
113 continue
114 }
115 mpath, vers, ok := strings.Cut(v, "@")
116 if !ok {
117 if major, status := rs.DefaultMajorVersion(mpath); status == modrequirements.ExplicitDefault {
118
119 vers = major
120 } else {
121 vers = "latest"
122 }
123 }
124 if err := module.CheckPathWithoutVersion(mpath); err != nil {
125 return nil, fmt.Errorf("invalid module path in %q", v)
126 }
127 versionPrefix := ""
128 if vers != "latest" {
129 if !semver.IsValid(vers) {
130 return nil, fmt.Errorf("%q does not specify a valid semantic version", v)
131 }
132 if semver.Build(vers) != "" {
133 return nil, fmt.Errorf("build version suffixes not supported (%v)", v)
134 }
135
136
137
138
139 versionPrefix = vers + "."
140 }
141 work.Add(func() {
142 allVersions, err := reg.ModuleVersions(ctx, mpath)
143 if err != nil {
144 setError(err)
145 return
146 }
147 possibleVersions := make([]string, 0, len(allVersions))
148 for _, v := range allVersions {
149 if strings.HasPrefix(v, versionPrefix) {
150 possibleVersions = append(possibleVersions, v)
151 }
152 }
153 chosen := latestVersion(possibleVersions)
154 mv, err := module.NewVersion(mpath, chosen)
155 if err != nil {
156
157
158
159 setError(err)
160 return
161 }
162 mversions[i] = mv
163 })
164 }
165 <-work.Idle()
166 if errPtr := queryErr.Load(); errPtr != nil {
167 return nil, *errPtr
168 }
169 for _, v := range mversions {
170 if v.Path() == mainModuleVersion.Path() {
171 return nil, fmt.Errorf("cannot update version of main module")
172 }
173 }
174 return mversions, nil
175 }
176
View as plain text