github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/modcmd/verify.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package modcmd
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"errors"
    11  	"fmt"
    12  	"io/fs"
    13  	"os"
    14  	"runtime"
    15  
    16  	"github.com/go-asm/go/cmd/go/base"
    17  	"github.com/go-asm/go/cmd/go/gover"
    18  	"github.com/go-asm/go/cmd/go/modfetch"
    19  	"github.com/go-asm/go/cmd/go/modload"
    20  
    21  	"golang.org/x/mod/module"
    22  	"golang.org/x/mod/sumdb/dirhash"
    23  )
    24  
    25  var cmdVerify = &base.Command{
    26  	UsageLine: "go mod verify",
    27  	Short:     "verify dependencies have expected content",
    28  	Long: `
    29  Verify checks that the dependencies of the current module,
    30  which are stored in a local downloaded source cache, have not been
    31  modified since being downloaded. If all the modules are unmodified,
    32  verify prints "all modules verified." Otherwise it reports which
    33  modules have been changed and causes 'go mod' to exit with a
    34  non-zero status.
    35  
    36  See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
    37  	`,
    38  	Run: runVerify,
    39  }
    40  
    41  func init() {
    42  	base.AddChdirFlag(&cmdVerify.Flag)
    43  	base.AddModCommonFlags(&cmdVerify.Flag)
    44  }
    45  
    46  func runVerify(ctx context.Context, cmd *base.Command, args []string) {
    47  	modload.InitWorkfile()
    48  
    49  	if len(args) != 0 {
    50  		// NOTE(rsc): Could take a module pattern.
    51  		base.Fatalf("go: verify takes no arguments")
    52  	}
    53  	modload.ForceUseModules = true
    54  	modload.RootMode = modload.NeedRoot
    55  
    56  	// Only verify up to GOMAXPROCS zips at once.
    57  	type token struct{}
    58  	sem := make(chan token, runtime.GOMAXPROCS(0))
    59  
    60  	mg, err := modload.LoadModGraph(ctx, "")
    61  	if err != nil {
    62  		base.Fatal(err)
    63  	}
    64  	mods := mg.BuildList()[modload.MainModules.Len():]
    65  	// Use a slice of result channels, so that the output is deterministic.
    66  	errsChans := make([]<-chan []error, len(mods))
    67  
    68  	for i, mod := range mods {
    69  		sem <- token{}
    70  		errsc := make(chan []error, 1)
    71  		errsChans[i] = errsc
    72  		mod := mod // use a copy to avoid data races
    73  		go func() {
    74  			errsc <- verifyMod(ctx, mod)
    75  			<-sem
    76  		}()
    77  	}
    78  
    79  	ok := true
    80  	for _, errsc := range errsChans {
    81  		errs := <-errsc
    82  		for _, err := range errs {
    83  			base.Errorf("%s", err)
    84  			ok = false
    85  		}
    86  	}
    87  	if ok {
    88  		fmt.Printf("all modules verified\n")
    89  	}
    90  }
    91  
    92  func verifyMod(ctx context.Context, mod module.Version) []error {
    93  	if gover.IsToolchain(mod.Path) {
    94  		// "go" and "toolchain" have no disk footprint; nothing to verify.
    95  		return nil
    96  	}
    97  	var errs []error
    98  	zip, zipErr := modfetch.CachePath(ctx, mod, "zip")
    99  	if zipErr == nil {
   100  		_, zipErr = os.Stat(zip)
   101  	}
   102  	dir, dirErr := modfetch.DownloadDir(ctx, mod)
   103  	data, err := os.ReadFile(zip + "hash")
   104  	if err != nil {
   105  		if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) &&
   106  			dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
   107  			// Nothing downloaded yet. Nothing to verify.
   108  			return nil
   109  		}
   110  		errs = append(errs, fmt.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err))
   111  		return errs
   112  	}
   113  	h := string(bytes.TrimSpace(data))
   114  
   115  	if zipErr != nil && errors.Is(zipErr, fs.ErrNotExist) {
   116  		// ok
   117  	} else {
   118  		hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
   119  		if err != nil {
   120  			errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
   121  			return errs
   122  		} else if hZ != h {
   123  			errs = append(errs, fmt.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip))
   124  		}
   125  	}
   126  	if dirErr != nil && errors.Is(dirErr, fs.ErrNotExist) {
   127  		// ok
   128  	} else {
   129  		hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
   130  		if err != nil {
   131  
   132  			errs = append(errs, fmt.Errorf("%s %s: %v", mod.Path, mod.Version, err))
   133  			return errs
   134  		}
   135  		if hD != h {
   136  			errs = append(errs, fmt.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir))
   137  		}
   138  	}
   139  	return errs
   140  }