@@ -92,73 +92,53 @@ func (e *excludedError) Is(err error) bool { return err == ErrDisallowed }
92
92
93
93
// CheckRetractions returns an error if module m has been retracted by
94
94
// its author.
95
- func CheckRetractions (ctx context.Context , m module.Version ) error {
95
+ func CheckRetractions (ctx context.Context , m module.Version ) (err error ) {
96
+ defer func () {
97
+ if retractErr := (* ModuleRetractedError )(nil ); err == nil || errors .As (err , & retractErr ) {
98
+ return
99
+ }
100
+ // Attribute the error to the version being checked, not the version from
101
+ // which the retractions were to be loaded.
102
+ if mErr := (* module .ModuleError )(nil ); errors .As (err , & mErr ) {
103
+ err = mErr .Err
104
+ }
105
+ err = & retractionLoadingError {m : m , err : err }
106
+ }()
107
+
96
108
if m .Version == "" {
97
109
// Main module, standard library, or file replacement module.
98
110
// Cannot be retracted.
99
111
return nil
100
112
}
101
-
102
- // Look up retraction information from the latest available version of
103
- // the module. Cache retraction information so we don't parse the go.mod
104
- // file repeatedly.
105
- type entry struct {
106
- retract []retraction
107
- err error
113
+ if repl := Replacement (module.Version {Path : m .Path }); repl .Path != "" {
114
+ // All versions of the module were replaced.
115
+ // Don't load retractions, since we'd just load the replacement.
116
+ return nil
108
117
}
109
- path := m .Path
110
- e := retractCache .Do (path , func () (v interface {}) {
111
- ctx , span := trace .StartSpan (ctx , "checkRetractions " + path )
112
- defer span .Done ()
113
-
114
- if repl := Replacement (module.Version {Path : m .Path }); repl .Path != "" {
115
- // All versions of the module were replaced with a local directory.
116
- // Don't load retractions.
117
- return & entry {nil , nil }
118
- }
119
118
120
- // Find the latest version of the module.
121
- // Ignore exclusions from the main module's go.mod.
122
- const ignoreSelected = ""
123
- var allowAll AllowedFunc
124
- rev , err := Query (ctx , path , "latest" , ignoreSelected , allowAll )
125
- if err != nil {
126
- return & entry {nil , err }
127
- }
128
-
129
- // Load go.mod for that version.
130
- // If the version is replaced, we'll load retractions from the replacement.
131
- //
132
- // If there's an error loading the go.mod, we'll return it here.
133
- // These errors should generally be ignored by callers of checkRetractions,
134
- // since they happen frequently when we're offline. These errors are not
135
- // equivalent to ErrDisallowed, so they may be distinguished from
136
- // retraction errors.
137
- //
138
- // We load the raw file here: the go.mod file may have a different module
139
- // path that we expect if the module or its repository was renamed.
140
- // We still want to apply retractions to other aliases of the module.
141
- rm := resolveReplacement (module.Version {Path : path , Version : rev .Version })
142
- summary , err := rawGoModSummary (rm )
143
- if err != nil {
144
- return & entry {nil , err }
145
- }
146
- return & entry {summary .retract , nil }
147
- }).(* entry )
148
-
149
- if err := e .err ; err != nil {
150
- // Attribute the error to the version being checked, not the version from
151
- // which the retractions were to be loaded.
152
- var mErr * module.ModuleError
153
- if errors .As (err , & mErr ) {
154
- err = mErr .Err
155
- }
156
- return & retractionLoadingError {m : m , err : err }
119
+ // Find the latest available version of the module, and load its go.mod. If
120
+ // the latest version is replaced, we'll load the replacement.
121
+ //
122
+ // If there's an error loading the go.mod, we'll return it here. These errors
123
+ // should generally be ignored by callers since they happen frequently when
124
+ // we're offline. These errors are not equivalent to ErrDisallowed, so they
125
+ // may be distinguished from retraction errors.
126
+ //
127
+ // We load the raw file here: the go.mod file may have a different module
128
+ // path that we expect if the module or its repository was renamed.
129
+ // We still want to apply retractions to other aliases of the module.
130
+ rm , err := queryLatestVersionIgnoringRetractions (ctx , m .Path )
131
+ if err != nil {
132
+ return err
133
+ }
134
+ summary , err := rawGoModSummary (rm )
135
+ if err != nil {
136
+ return err
157
137
}
158
138
159
139
var rationale []string
160
140
isRetracted := false
161
- for _ , r := range e .retract {
141
+ for _ , r := range summary .retract {
162
142
if semver .Compare (r .Low , m .Version ) <= 0 && semver .Compare (m .Version , r .High ) <= 0 {
163
143
isRetracted = true
164
144
if r .Rationale != "" {
@@ -172,8 +152,6 @@ func CheckRetractions(ctx context.Context, m module.Version) error {
172
152
return nil
173
153
}
174
154
175
- var retractCache par.Cache
176
-
177
155
type ModuleRetractedError struct {
178
156
Rationale []string
179
157
}
@@ -183,7 +161,7 @@ func (e *ModuleRetractedError) Error() string {
183
161
if len (e .Rationale ) > 0 {
184
162
// This is meant to be a short error printed on a terminal, so just
185
163
// print the first rationale.
186
- msg += ": " + ShortRetractionRationale (e .Rationale [0 ])
164
+ msg += ": " + ShortMessage (e .Rationale [0 ], "retracted by module author" )
187
165
}
188
166
return msg
189
167
}
@@ -205,28 +183,31 @@ func (e *retractionLoadingError) Unwrap() error {
205
183
return e .err
206
184
}
207
185
208
- // ShortRetractionRationale returns a retraction rationale string that is safe
209
- // to print in a terminal. It returns hard-coded strings if the rationale
210
- // is empty, too long, or contains non-printable characters.
211
- func ShortRetractionRationale (rationale string ) string {
212
- const maxRationaleBytes = 500
213
- if i := strings .Index (rationale , "\n " ); i >= 0 {
214
- rationale = rationale [:i ]
215
- }
216
- rationale = strings .TrimSpace (rationale )
217
- if rationale == "" {
218
- return "retracted by module author"
219
- }
220
- if len (rationale ) > maxRationaleBytes {
221
- return "(rationale omitted: too long)"
222
- }
223
- for _ , r := range rationale {
186
+ // ShortMessage returns a string from go.mod (for example, a retraction
187
+ // rationale or deprecation message) that is safe to print in a terminal.
188
+ //
189
+ // If the given string is empty, ShortMessage returns the given default. If the
190
+ // given string is too long or contains non-printable characters, ShortMessage
191
+ // returns a hard-coded string.
192
+ func ShortMessage (message , emptyDefault string ) string {
193
+ const maxLen = 500
194
+ if i := strings .Index (message , "\n " ); i >= 0 {
195
+ message = message [:i ]
196
+ }
197
+ message = strings .TrimSpace (message )
198
+ if message == "" {
199
+ return emptyDefault
200
+ }
201
+ if len (message ) > maxLen {
202
+ return "(message omitted: too long)"
203
+ }
204
+ for _ , r := range message {
224
205
if ! unicode .IsGraphic (r ) && ! unicode .IsSpace (r ) {
225
- return "(rationale omitted: contains non-printable characters)"
206
+ return "(message omitted: contains non-printable characters)"
226
207
}
227
208
}
228
209
// NOTE: the go.mod parser rejects invalid UTF-8, so we don't check that here.
229
- return rationale
210
+ return message
230
211
}
231
212
232
213
// Replacement returns the replacement for mod, if any, from go.mod.
@@ -596,3 +577,47 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
596
577
}
597
578
598
579
var rawGoModSummaryCache par.Cache // module.Version → rawGoModSummary result
580
+
581
+ // queryLatestVersionIgnoringRetractions looks up the latest version of the
582
+ // module with the given path without considering retracted or excluded
583
+ // versions.
584
+ //
585
+ // If all versions of the module are replaced,
586
+ // queryLatestVersionIgnoringRetractions returns the replacement without making
587
+ // a query.
588
+ //
589
+ // If the queried latest version is replaced,
590
+ // queryLatestVersionIgnoringRetractions returns the replacement.
591
+ func queryLatestVersionIgnoringRetractions (ctx context.Context , path string ) (latest module.Version , err error ) {
592
+ type entry struct {
593
+ latest module.Version
594
+ err error
595
+ }
596
+ e := latestVersionIgnoringRetractionsCache .Do (path , func () interface {} {
597
+ ctx , span := trace .StartSpan (ctx , "queryLatestVersionIgnoringRetractions " + path )
598
+ defer span .Done ()
599
+
600
+ if repl := Replacement (module.Version {Path : path }); repl .Path != "" {
601
+ // All versions of the module were replaced.
602
+ // No need to query.
603
+ return & entry {latest : repl }
604
+ }
605
+
606
+ // Find the latest version of the module.
607
+ // Ignore exclusions from the main module's go.mod.
608
+ const ignoreSelected = ""
609
+ var allowAll AllowedFunc
610
+ rev , err := Query (ctx , path , "latest" , ignoreSelected , allowAll )
611
+ if err != nil {
612
+ return & entry {err : err }
613
+ }
614
+ latest := module.Version {Path : path , Version : rev .Version }
615
+ if repl := resolveReplacement (latest ); repl .Path != "" {
616
+ latest = repl
617
+ }
618
+ return & entry {latest : latest }
619
+ }).(* entry )
620
+ return e .latest , e .err
621
+ }
622
+
623
+ var latestVersionIgnoringRetractionsCache par.Cache // path → queryLatestVersionIgnoringRetractions result
0 commit comments