1
2
3
4
5 package modfetch
6
7 import (
8 "archive/zip"
9 "bytes"
10 "context"
11 "crypto/sha256"
12 "encoding/base64"
13 "errors"
14 "fmt"
15 "io"
16 "io/fs"
17 "os"
18 "path/filepath"
19 "sort"
20 "strings"
21 "sync"
22
23 "cmd/go/internal/base"
24 "cmd/go/internal/cfg"
25 "cmd/go/internal/fsys"
26 "cmd/go/internal/gover"
27 "cmd/go/internal/lockedfile"
28 "cmd/go/internal/par"
29 "cmd/go/internal/robustio"
30 "cmd/go/internal/str"
31 "cmd/go/internal/trace"
32
33 "golang.org/x/mod/module"
34 "golang.org/x/mod/sumdb/dirhash"
35 modzip "golang.org/x/mod/zip"
36 )
37
38 var downloadCache par.ErrCache[module.Version, string]
39
40 var ErrToolchain = errors.New("internal error: invalid operation on toolchain module")
41
42
43
44
45 func Download(ctx context.Context, mod module.Version) (dir string, err error) {
46 if gover.IsToolchain(mod.Path) {
47 return "", ErrToolchain
48 }
49 if err := checkCacheDir(ctx); err != nil {
50 base.Fatal(err)
51 }
52
53
54 return downloadCache.Do(mod, func() (string, error) {
55 dir, err := download(ctx, mod)
56 if err != nil {
57 return "", err
58 }
59 checkMod(ctx, mod)
60
61
62 if data, err := os.ReadFile(filepath.Join(dir, "go.mod")); err == nil {
63 goVersion := gover.GoModLookup(data, "go")
64 if gover.Compare(goVersion, gover.Local()) > 0 {
65 return "", &gover.TooNewError{What: mod.String(), GoVersion: goVersion}
66 }
67 } else if !errors.Is(err, fs.ErrNotExist) {
68 return "", err
69 }
70
71 return dir, nil
72 })
73 }
74
75 func download(ctx context.Context, mod module.Version) (dir string, err error) {
76 ctx, span := trace.StartSpan(ctx, "modfetch.download "+mod.String())
77 defer span.Done()
78
79 dir, err = DownloadDir(ctx, mod)
80 if err == nil {
81
82 return dir, nil
83 } else if dir == "" || !errors.Is(err, fs.ErrNotExist) {
84 return "", err
85 }
86
87
88
89
90 zipfile, err := DownloadZip(ctx, mod)
91 if err != nil {
92 return "", err
93 }
94
95 unlock, err := lockVersion(ctx, mod)
96 if err != nil {
97 return "", err
98 }
99 defer unlock()
100
101 ctx, span = trace.StartSpan(ctx, "unzip "+zipfile)
102 defer span.Done()
103
104
105 _, dirErr := DownloadDir(ctx, mod)
106 if dirErr == nil {
107 return dir, nil
108 }
109 _, dirExists := dirErr.(*DownloadDirPartialError)
110
111
112
113
114
115
116 parentDir := filepath.Dir(dir)
117 tmpPrefix := filepath.Base(dir) + ".tmp-"
118 if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(parentDir), str.QuoteGlob(tmpPrefix)+"*")); err == nil {
119 for _, path := range old {
120 RemoveAll(path)
121 }
122 }
123 if dirExists {
124 if err := RemoveAll(dir); err != nil {
125 return "", err
126 }
127 }
128
129 partialPath, err := CachePath(ctx, mod, "partial")
130 if err != nil {
131 return "", err
132 }
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148 if err := os.MkdirAll(parentDir, 0777); err != nil {
149 return "", err
150 }
151 if err := os.WriteFile(partialPath, nil, 0666); err != nil {
152 return "", err
153 }
154 if err := modzip.Unzip(dir, mod, zipfile); err != nil {
155 fmt.Fprintf(os.Stderr, "-> %s\n", err)
156 if rmErr := RemoveAll(dir); rmErr == nil {
157 os.Remove(partialPath)
158 }
159 return "", err
160 }
161 if err := os.Remove(partialPath); err != nil {
162 return "", err
163 }
164
165 if !cfg.ModCacheRW {
166 makeDirsReadOnly(dir)
167 }
168 return dir, nil
169 }
170
171 var downloadZipCache par.ErrCache[module.Version, string]
172
173
174
175 func DownloadZip(ctx context.Context, mod module.Version) (zipfile string, err error) {
176
177 return downloadZipCache.Do(mod, func() (string, error) {
178 zipfile, err := CachePath(ctx, mod, "zip")
179 if err != nil {
180 return "", err
181 }
182 ziphashfile := zipfile + "hash"
183
184
185 if _, err := os.Stat(zipfile); err == nil {
186 if _, err := os.Stat(ziphashfile); err == nil {
187 return zipfile, nil
188 }
189 }
190
191
192 if cfg.CmdName != "mod download" {
193 vers := mod.Version
194 if mod.Path == "golang.org/toolchain" {
195
196 _, vers, _ = strings.Cut(vers, "-")
197 if i := strings.LastIndex(vers, "."); i >= 0 {
198 goos, goarch, _ := strings.Cut(vers[i+1:], "-")
199 vers = vers[:i] + " (" + goos + "/" + goarch + ")"
200 }
201 fmt.Fprintf(os.Stderr, "go: downloading %s\n", vers)
202 } else {
203 fmt.Fprintf(os.Stderr, "go: downloading %s %s\n", mod.Path, vers)
204 }
205 }
206 unlock, err := lockVersion(ctx, mod)
207 if err != nil {
208 return "", err
209 }
210 defer unlock()
211
212 if err := downloadZip(ctx, mod, zipfile); err != nil {
213 return "", err
214 }
215 return zipfile, nil
216 })
217 }
218
219 func downloadZip(ctx context.Context, mod module.Version, zipfile string) (err error) {
220 ctx, span := trace.StartSpan(ctx, "modfetch.downloadZip "+zipfile)
221 defer span.Done()
222
223
224
225 ziphashfile := zipfile + "hash"
226 var zipExists, ziphashExists bool
227 if _, err := os.Stat(zipfile); err == nil {
228 zipExists = true
229 }
230 if _, err := os.Stat(ziphashfile); err == nil {
231 ziphashExists = true
232 }
233 if zipExists && ziphashExists {
234 return nil
235 }
236
237
238 if err := os.MkdirAll(filepath.Dir(zipfile), 0777); err != nil {
239 return err
240 }
241
242
243
244
245 tmpPattern := filepath.Base(zipfile) + "*.tmp"
246 if old, err := filepath.Glob(filepath.Join(str.QuoteGlob(filepath.Dir(zipfile)), tmpPattern)); err == nil {
247 for _, path := range old {
248 os.Remove(path)
249 }
250 }
251
252
253
254 if zipExists {
255 return hashZip(mod, zipfile, ziphashfile)
256 }
257
258
259
260
261
262
263 f, err := tempFile(ctx, filepath.Dir(zipfile), filepath.Base(zipfile), 0666)
264 if err != nil {
265 return err
266 }
267 defer func() {
268 if err != nil {
269 f.Close()
270 os.Remove(f.Name())
271 }
272 }()
273
274 var unrecoverableErr error
275 err = TryProxies(func(proxy string) error {
276 if unrecoverableErr != nil {
277 return unrecoverableErr
278 }
279 repo := Lookup(ctx, proxy, mod.Path)
280 err := repo.Zip(ctx, f, mod.Version)
281 if err != nil {
282
283
284
285
286 if _, err := f.Seek(0, io.SeekStart); err != nil {
287 unrecoverableErr = err
288 return err
289 }
290 if err := f.Truncate(0); err != nil {
291 unrecoverableErr = err
292 return err
293 }
294 }
295 return err
296 })
297 if err != nil {
298 return err
299 }
300
301
302
303
304 fi, err := f.Stat()
305 if err != nil {
306 return err
307 }
308 z, err := zip.NewReader(f, fi.Size())
309 if err != nil {
310 return err
311 }
312 prefix := mod.Path + "@" + mod.Version + "/"
313 for _, f := range z.File {
314 if !strings.HasPrefix(f.Name, prefix) {
315 return fmt.Errorf("zip for %s has unexpected file %s", prefix[:len(prefix)-1], f.Name)
316 }
317 }
318
319 if err := f.Close(); err != nil {
320 return err
321 }
322
323
324 if err := hashZip(mod, f.Name(), ziphashfile); err != nil {
325 return err
326 }
327 if err := os.Rename(f.Name(), zipfile); err != nil {
328 return err
329 }
330
331
332
333 return nil
334 }
335
336
337
338
339
340
341 func hashZip(mod module.Version, zipfile, ziphashfile string) (err error) {
342 hash, err := dirhash.HashZip(zipfile, dirhash.DefaultHash)
343 if err != nil {
344 return err
345 }
346 if err := checkModSum(mod, hash); err != nil {
347 return err
348 }
349 hf, err := lockedfile.Create(ziphashfile)
350 if err != nil {
351 return err
352 }
353 defer func() {
354 if closeErr := hf.Close(); err == nil && closeErr != nil {
355 err = closeErr
356 }
357 }()
358 if err := hf.Truncate(int64(len(hash))); err != nil {
359 return err
360 }
361 if _, err := hf.WriteAt([]byte(hash), 0); err != nil {
362 return err
363 }
364 return nil
365 }
366
367
368
369 func makeDirsReadOnly(dir string) {
370 type pathMode struct {
371 path string
372 mode fs.FileMode
373 }
374 var dirs []pathMode
375 filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
376 if err == nil && d.IsDir() {
377 info, err := d.Info()
378 if err == nil && info.Mode()&0222 != 0 {
379 dirs = append(dirs, pathMode{path, info.Mode()})
380 }
381 }
382 return nil
383 })
384
385
386 for i := len(dirs) - 1; i >= 0; i-- {
387 os.Chmod(dirs[i].path, dirs[i].mode&^0222)
388 }
389 }
390
391
392
393 func RemoveAll(dir string) error {
394
395 filepath.WalkDir(dir, func(path string, info fs.DirEntry, err error) error {
396 if err != nil {
397 return nil
398 }
399 if info.IsDir() {
400 os.Chmod(path, 0777)
401 }
402 return nil
403 })
404 return robustio.RemoveAll(dir)
405 }
406
407 var GoSumFile string
408 var WorkspaceGoSumFiles []string
409
410 type modSum struct {
411 mod module.Version
412 sum string
413 }
414
415 var goSum struct {
416 mu sync.Mutex
417 m map[module.Version][]string
418 w map[string]map[module.Version][]string
419 status map[modSum]modSumStatus
420 overwrite bool
421 enabled bool
422 }
423
424 type modSumStatus struct {
425 used, dirty bool
426 }
427
428
429
430 func Reset() {
431 GoSumFile = ""
432 WorkspaceGoSumFiles = nil
433
434
435
436
437 lookupCache = par.Cache[lookupCacheKey, Repo]{}
438 downloadCache = par.ErrCache[module.Version, string]{}
439
440
441 goSum.mu.Lock()
442 goSum.m = nil
443 goSum.w = nil
444 goSum.status = nil
445 goSum.overwrite = false
446 goSum.enabled = false
447 goSum.mu.Unlock()
448 }
449
450
451
452
453
454 func initGoSum() (bool, error) {
455 if GoSumFile == "" {
456 return false, nil
457 }
458 if goSum.m != nil {
459 return true, nil
460 }
461
462 goSum.m = make(map[module.Version][]string)
463 goSum.status = make(map[modSum]modSumStatus)
464 goSum.w = make(map[string]map[module.Version][]string)
465
466 for _, f := range WorkspaceGoSumFiles {
467 goSum.w[f] = make(map[module.Version][]string)
468 _, err := readGoSumFile(goSum.w[f], f)
469 if err != nil {
470 return false, err
471 }
472 }
473
474 enabled, err := readGoSumFile(goSum.m, GoSumFile)
475 goSum.enabled = enabled
476 return enabled, err
477 }
478
479 func readGoSumFile(dst map[module.Version][]string, file string) (bool, error) {
480 var (
481 data []byte
482 err error
483 )
484 if actualSumFile, ok := fsys.OverlayPath(file); ok {
485
486
487
488 data, err = os.ReadFile(actualSumFile)
489 } else {
490 data, err = lockedfile.Read(file)
491 }
492 if err != nil && !os.IsNotExist(err) {
493 return false, err
494 }
495 readGoSum(dst, file, data)
496
497 return true, nil
498 }
499
500
501
502
503 const emptyGoModHash = "h1:G7mAYYxgmS0lVkHyy2hEOLQCFB0DlQFTMLWggykrydY="
504
505
506
507 func readGoSum(dst map[module.Version][]string, file string, data []byte) {
508 lineno := 0
509 for len(data) > 0 {
510 var line []byte
511 lineno++
512 i := bytes.IndexByte(data, '\n')
513 if i < 0 {
514 line, data = data, nil
515 } else {
516 line, data = data[:i], data[i+1:]
517 }
518 f := strings.Fields(string(line))
519 if len(f) == 0 {
520
521 continue
522 }
523 if len(f) != 3 {
524 if cfg.CmdName == "mod tidy" {
525
526 continue
527 } else {
528 base.Fatalf("malformed go.sum:\n%s:%d: wrong number of fields %v\n", file, lineno, len(f))
529 }
530 }
531 if f[2] == emptyGoModHash {
532
533 continue
534 }
535 mod := module.Version{Path: f[0], Version: f[1]}
536 dst[mod] = append(dst[mod], f[2])
537 }
538 }
539
540
541
542
543
544 func HaveSum(mod module.Version) bool {
545 goSum.mu.Lock()
546 defer goSum.mu.Unlock()
547 inited, err := initGoSum()
548 if err != nil || !inited {
549 return false
550 }
551 for _, goSums := range goSum.w {
552 for _, h := range goSums[mod] {
553 if !strings.HasPrefix(h, "h1:") {
554 continue
555 }
556 if !goSum.status[modSum{mod, h}].dirty {
557 return true
558 }
559 }
560 }
561 for _, h := range goSum.m[mod] {
562 if !strings.HasPrefix(h, "h1:") {
563 continue
564 }
565 if !goSum.status[modSum{mod, h}].dirty {
566 return true
567 }
568 }
569 return false
570 }
571
572
573 func checkMod(ctx context.Context, mod module.Version) {
574
575 ziphash, err := CachePath(ctx, mod, "ziphash")
576 if err != nil {
577 base.Fatalf("verifying %v", module.VersionError(mod, err))
578 }
579 data, err := lockedfile.Read(ziphash)
580 if err != nil {
581 base.Fatalf("verifying %v", module.VersionError(mod, err))
582 }
583 data = bytes.TrimSpace(data)
584 if !isValidSum(data) {
585
586 zip, err := CachePath(ctx, mod, "zip")
587 if err != nil {
588 base.Fatalf("verifying %v", module.VersionError(mod, err))
589 }
590 err = hashZip(mod, zip, ziphash)
591 if err != nil {
592 base.Fatalf("verifying %v", module.VersionError(mod, err))
593 }
594 return
595 }
596 h := string(data)
597 if !strings.HasPrefix(h, "h1:") {
598 base.Fatalf("verifying %v", module.VersionError(mod, fmt.Errorf("unexpected ziphash: %q", h)))
599 }
600
601 if err := checkModSum(mod, h); err != nil {
602 base.Fatalf("%s", err)
603 }
604 }
605
606
607 func goModSum(data []byte) (string, error) {
608 return dirhash.Hash1([]string{"go.mod"}, func(string) (io.ReadCloser, error) {
609 return io.NopCloser(bytes.NewReader(data)), nil
610 })
611 }
612
613
614
615 func checkGoMod(path, version string, data []byte) error {
616 h, err := goModSum(data)
617 if err != nil {
618 return &module.ModuleError{Path: path, Version: version, Err: fmt.Errorf("verifying go.mod: %v", err)}
619 }
620
621 return checkModSum(module.Version{Path: path, Version: version + "/go.mod"}, h)
622 }
623
624
625
626
627
628 func checkModSum(mod module.Version, h string) error {
629
630
631
632
633
634
635 goSum.mu.Lock()
636 inited, err := initGoSum()
637 if err != nil {
638 goSum.mu.Unlock()
639 return err
640 }
641 done := inited && haveModSumLocked(mod, h)
642 if inited {
643 st := goSum.status[modSum{mod, h}]
644 st.used = true
645 goSum.status[modSum{mod, h}] = st
646 }
647 goSum.mu.Unlock()
648
649 if done {
650 return nil
651 }
652
653
654
655 if useSumDB(mod) {
656
657 if err := checkSumDB(mod, h); err != nil {
658 return err
659 }
660 }
661
662
663 if inited {
664 goSum.mu.Lock()
665 addModSumLocked(mod, h)
666 st := goSum.status[modSum{mod, h}]
667 st.dirty = true
668 goSum.status[modSum{mod, h}] = st
669 goSum.mu.Unlock()
670 }
671 return nil
672 }
673
674
675
676
677 func haveModSumLocked(mod module.Version, h string) bool {
678 sumFileName := "go.sum"
679 if strings.HasSuffix(GoSumFile, "go.work.sum") {
680 sumFileName = "go.work.sum"
681 }
682 for _, vh := range goSum.m[mod] {
683 if h == vh {
684 return true
685 }
686 if strings.HasPrefix(vh, "h1:") {
687 base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, sumFileName, vh)
688 }
689 }
690
691 foundMatch := false
692
693
694 for goSumFile, goSums := range goSum.w {
695 for _, vh := range goSums[mod] {
696 if h == vh {
697 foundMatch = true
698 } else if strings.HasPrefix(vh, "h1:") {
699 base.Fatalf("verifying %s@%s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+goSumMismatch, mod.Path, mod.Version, h, goSumFile, vh)
700 }
701 }
702 }
703 return foundMatch
704 }
705
706
707
708 func addModSumLocked(mod module.Version, h string) {
709 if haveModSumLocked(mod, h) {
710 return
711 }
712 if len(goSum.m[mod]) > 0 {
713 fmt.Fprintf(os.Stderr, "warning: verifying %s@%s: unknown hashes in go.sum: %v; adding %v"+hashVersionMismatch, mod.Path, mod.Version, strings.Join(goSum.m[mod], ", "), h)
714 }
715 goSum.m[mod] = append(goSum.m[mod], h)
716 }
717
718
719
720 func checkSumDB(mod module.Version, h string) error {
721 modWithoutSuffix := mod
722 noun := "module"
723 if before, found := strings.CutSuffix(mod.Version, "/go.mod"); found {
724 noun = "go.mod"
725 modWithoutSuffix.Version = before
726 }
727
728 db, lines, err := lookupSumDB(mod)
729 if err != nil {
730 return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: %v", noun, err))
731 }
732
733 have := mod.Path + " " + mod.Version + " " + h
734 prefix := mod.Path + " " + mod.Version + " h1:"
735 for _, line := range lines {
736 if line == have {
737 return nil
738 }
739 if strings.HasPrefix(line, prefix) {
740 return module.VersionError(modWithoutSuffix, fmt.Errorf("verifying %s: checksum mismatch\n\tdownloaded: %v\n\t%s: %v"+sumdbMismatch, noun, h, db, line[len(prefix)-len("h1:"):]))
741 }
742 }
743 return nil
744 }
745
746
747
748 func Sum(ctx context.Context, mod module.Version) string {
749 if cfg.GOMODCACHE == "" {
750
751 return ""
752 }
753
754 ziphash, err := CachePath(ctx, mod, "ziphash")
755 if err != nil {
756 return ""
757 }
758 data, err := lockedfile.Read(ziphash)
759 if err != nil {
760 return ""
761 }
762 data = bytes.TrimSpace(data)
763 if !isValidSum(data) {
764 return ""
765 }
766 return string(data)
767 }
768
769
770
771
772
773
774 func isValidSum(data []byte) bool {
775 if bytes.IndexByte(data, '\000') >= 0 {
776 return false
777 }
778
779 if len(data) != len("h1:")+base64.StdEncoding.EncodedLen(sha256.Size) {
780 return false
781 }
782
783 return true
784 }
785
786 var ErrGoSumDirty = errors.New("updates to go.sum needed, disabled by -mod=readonly")
787
788
789
790
791
792
793
794 func WriteGoSum(ctx context.Context, keep map[module.Version]bool, readonly bool) error {
795 goSum.mu.Lock()
796 defer goSum.mu.Unlock()
797
798
799 if !goSum.enabled {
800 return nil
801 }
802
803
804
805
806 dirty := false
807 Outer:
808 for m, hs := range goSum.m {
809 for _, h := range hs {
810 st := goSum.status[modSum{m, h}]
811 if st.dirty && (!st.used || keep[m]) {
812 dirty = true
813 break Outer
814 }
815 }
816 }
817 if !dirty {
818 return nil
819 }
820 if readonly {
821 return ErrGoSumDirty
822 }
823 if _, ok := fsys.OverlayPath(GoSumFile); ok {
824 base.Fatalf("go: updates to go.sum needed, but go.sum is part of the overlay specified with -overlay")
825 }
826
827
828
829 if unlock, err := SideLock(ctx); err == nil {
830 defer unlock()
831 }
832
833 err := lockedfile.Transform(GoSumFile, func(data []byte) ([]byte, error) {
834 if !goSum.overwrite {
835
836
837
838
839 goSum.m = make(map[module.Version][]string, len(goSum.m))
840 readGoSum(goSum.m, GoSumFile, data)
841 for ms, st := range goSum.status {
842 if st.used && !sumInWorkspaceModulesLocked(ms.mod) {
843 addModSumLocked(ms.mod, ms.sum)
844 }
845 }
846 }
847
848 var mods []module.Version
849 for m := range goSum.m {
850 mods = append(mods, m)
851 }
852 module.Sort(mods)
853
854 var buf bytes.Buffer
855 for _, m := range mods {
856 list := goSum.m[m]
857 sort.Strings(list)
858 str.Uniq(&list)
859 for _, h := range list {
860 st := goSum.status[modSum{m, h}]
861 if (!st.dirty || (st.used && keep[m])) && !sumInWorkspaceModulesLocked(m) {
862 fmt.Fprintf(&buf, "%s %s %s\n", m.Path, m.Version, h)
863 }
864 }
865 }
866 return buf.Bytes(), nil
867 })
868
869 if err != nil {
870 return fmt.Errorf("updating go.sum: %w", err)
871 }
872
873 goSum.status = make(map[modSum]modSumStatus)
874 goSum.overwrite = false
875 return nil
876 }
877
878 func sumInWorkspaceModulesLocked(m module.Version) bool {
879 for _, goSums := range goSum.w {
880 if _, ok := goSums[m]; ok {
881 return true
882 }
883 }
884 return false
885 }
886
887
888
889
890
891
892
893 func TrimGoSum(keep map[module.Version]bool) {
894 goSum.mu.Lock()
895 defer goSum.mu.Unlock()
896 inited, err := initGoSum()
897 if err != nil {
898 base.Fatalf("%s", err)
899 }
900 if !inited {
901 return
902 }
903
904 for m, hs := range goSum.m {
905 if !keep[m] {
906 for _, h := range hs {
907 goSum.status[modSum{m, h}] = modSumStatus{used: false, dirty: true}
908 }
909 goSum.overwrite = true
910 }
911 }
912 }
913
914 const goSumMismatch = `
915
916 SECURITY ERROR
917 This download does NOT match an earlier download recorded in go.sum.
918 The bits may have been replaced on the origin server, or an attacker may
919 have intercepted the download attempt.
920
921 For more information, see 'go help module-auth'.
922 `
923
924 const sumdbMismatch = `
925
926 SECURITY ERROR
927 This download does NOT match the one reported by the checksum server.
928 The bits may have been replaced on the origin server, or an attacker may
929 have intercepted the download attempt.
930
931 For more information, see 'go help module-auth'.
932 `
933
934 const hashVersionMismatch = `
935
936 SECURITY WARNING
937 This download is listed in go.sum, but using an unknown hash algorithm.
938 The download cannot be verified.
939
940 For more information, see 'go help module-auth'.
941
942 `
943
944 var HelpModuleAuth = &base.Command{
945 UsageLine: "module-auth",
946 Short: "module authentication using go.sum",
947 Long: `
948 When the go command downloads a module zip file or go.mod file into the
949 module cache, it computes a cryptographic hash and compares it with a known
950 value to verify the file hasn't changed since it was first downloaded. Known
951 hashes are stored in a file in the module root directory named go.sum. Hashes
952 may also be downloaded from the checksum database depending on the values of
953 GOSUMDB, GOPRIVATE, and GONOSUMDB.
954
955 For details, see https://golang.org/ref/mod#authenticating.
956 `,
957 }
958
959 var HelpPrivate = &base.Command{
960 UsageLine: "private",
961 Short: "configuration for downloading non-public code",
962 Long: `
963 The go command defaults to downloading modules from the public Go module
964 mirror at proxy.golang.org. It also defaults to validating downloaded modules,
965 regardless of source, against the public Go checksum database at sum.golang.org.
966 These defaults work well for publicly available source code.
967
968 The GOPRIVATE environment variable controls which modules the go command
969 considers to be private (not available publicly) and should therefore not use
970 the proxy or checksum database. The variable is a comma-separated list of
971 glob patterns (in the syntax of Go's path.Match) of module path prefixes.
972 For example,
973
974 GOPRIVATE=*.corp.example.com,rsc.io/private
975
976 causes the go command to treat as private any module with a path prefix
977 matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
978 and rsc.io/private/quux.
979
980 For fine-grained control over module download and validation, the GONOPROXY
981 and GONOSUMDB environment variables accept the same kind of glob list
982 and override GOPRIVATE for the specific decision of whether to use the proxy
983 and checksum database, respectively.
984
985 For example, if a company ran a module proxy serving private modules,
986 users would configure go using:
987
988 GOPRIVATE=*.corp.example.com
989 GOPROXY=proxy.example.com
990 GONOPROXY=none
991
992 The GOPRIVATE variable is also used to define the "public" and "private"
993 patterns for the GOVCS variable; see 'go help vcs'. For that usage,
994 GOPRIVATE applies even in GOPATH mode. In that case, it matches import paths
995 instead of module paths.
996
997 The 'go env -w' command (see 'go help env') can be used to set these variables
998 for future go command invocations.
999
1000 For more details, see https://golang.org/ref/mod#private-modules.
1001 `,
1002 }
1003
View as plain text