@@ -50,6 +50,7 @@ import (
50
50
"bytes"
51
51
"errors"
52
52
"fmt"
53
+ "go/version"
53
54
"io"
54
55
"os"
55
56
"os/exec"
@@ -60,6 +61,7 @@ import (
60
61
"unicode"
61
62
"unicode/utf8"
62
63
64
+ "golang.org/x/mod/modfile"
63
65
"golang.org/x/mod/module"
64
66
)
65
67
@@ -193,6 +195,20 @@ func CheckFiles(files []File) (CheckedFiles, error) {
193
195
return cf , cf .Err ()
194
196
}
195
197
198
+ // parseGoVers extracts the Go version specified in the given go.mod file.
199
+ // It returns an empty string if the version is not found or if an error
200
+ // occurs during file parsing.
201
+ //
202
+ // The version string is in Go toolchain name syntax, prefixed with "go".
203
+ // Examples: "go1.21", "go1.22rc2", "go1.23.0"
204
+ func parseGoVers (file string , data []byte ) string {
205
+ mfile , err := modfile .ParseLax (file , data , nil )
206
+ if err != nil || mfile .Go == nil {
207
+ return ""
208
+ }
209
+ return "go" + mfile .Go .Version
210
+ }
211
+
196
212
// checkFiles implements CheckFiles and also returns lists of valid files and
197
213
// their sizes, corresponding to cf.Valid. It omits files in submodules, files
198
214
// in vendored packages, symlinked files, and various other unwanted files.
@@ -217,6 +233,7 @@ func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []
217
233
// Files in these directories will be omitted.
218
234
// These directories will not be included in the output zip.
219
235
haveGoMod := make (map [string ]bool )
236
+ var vers string
220
237
for _ , f := range files {
221
238
p := f .Path ()
222
239
dir , base := path .Split (p )
@@ -226,8 +243,21 @@ func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []
226
243
addError (p , false , err )
227
244
continue
228
245
}
229
- if info .Mode ().IsRegular () {
230
- haveGoMod [dir ] = true
246
+ if ! info .Mode ().IsRegular () {
247
+ continue
248
+ }
249
+ haveGoMod [dir ] = true
250
+ // Extract the Go language version from the root "go.mod" file.
251
+ // This ensures we correctly interpret Go version-specific file omissions.
252
+ // We use f.Open() to handle potential custom Open() implementations
253
+ // that the underlying File type might have.
254
+ if base == "go.mod" && dir == "" {
255
+ if file , err := f .Open (); err == nil {
256
+ if data , err := io .ReadAll (file ); err == nil {
257
+ vers = version .Lang (parseGoVers ("go.mod" , data ))
258
+ }
259
+ file .Close ()
260
+ }
231
261
}
232
262
}
233
263
}
@@ -257,7 +287,7 @@ func checkFiles(files []File) (cf CheckedFiles, validFiles []File, validSizes []
257
287
addError (p , false , errPathNotRelative )
258
288
continue
259
289
}
260
- if isVendoredPackage (p ) {
290
+ if isVendoredPackage (p , vers ) {
261
291
// Skip files in vendored packages.
262
292
addError (p , true , errVendored )
263
293
continue
@@ -758,7 +788,17 @@ func (fi dataFileInfo) Sys() interface{} { return nil }
758
788
//
759
789
// Unfortunately, isVendoredPackage reports false positives for files in any
760
790
// non-top-level package whose import path ends in "vendor".
761
- func isVendoredPackage (name string ) bool {
791
+ // The 'vers' parameter specifies the Go version declared in the module's
792
+ // go.mod file and must be a valid Go version according to the
793
+ // go/version.IsValid function.
794
+ // Vendoring behavior has evolved across Go versions, so this function adapts
795
+ // its logic accordingly.
796
+ func isVendoredPackage (name string , vers string ) bool {
797
+ // vendor/modules.txt is a vendored package but was included in 1.23 and earlier.
798
+ // Remove vendor/modules.txt only for 1.24 and beyond to preserve older checksums.
799
+ if version .Compare (vers , "go1.24" ) >= 0 && name == "vendor/modules.txt" {
800
+ return true
801
+ }
762
802
var i int
763
803
if strings .HasPrefix (name , "vendor/" ) {
764
804
i += len ("vendor/" )
@@ -894,6 +934,12 @@ func (cc collisionChecker) check(p string, isDir bool) error {
894
934
// files, as well as a list of directories and files that were skipped (for
895
935
// example, nested modules and symbolic links).
896
936
func listFilesInDir (dir string ) (files []File , omitted []FileError , err error ) {
937
+ // Extract the Go language version from the root "go.mod" file.
938
+ // This ensures we correctly interpret Go version-specific file omissions.
939
+ var vers string
940
+ if data , err := os .ReadFile (filepath .Join (dir , "go.mod" )); err == nil {
941
+ vers = version .Lang (parseGoVers ("go.mod" , data ))
942
+ }
897
943
err = filepath .Walk (dir , func (filePath string , info os.FileInfo , err error ) error {
898
944
if err != nil {
899
945
return err
@@ -908,7 +954,7 @@ func listFilesInDir(dir string) (files []File, omitted []FileError, err error) {
908
954
// golang.org/issue/31562, described in isVendoredPackage.
909
955
// We would like Create and CreateFromDir to produce the same result
910
956
// for a set of files, whether expressed as a directory tree or zip.
911
- if isVendoredPackage (slashPath ) {
957
+ if isVendoredPackage (slashPath , vers ) {
912
958
omitted = append (omitted , FileError {Path : slashPath , Err : errVendored })
913
959
return nil
914
960
}
0 commit comments