diff --git a/cache/filecache/filecache_pruner.go b/cache/filecache/filecache_pruner.go index 7f68c8b822f..b77f5331bee 100644 --- a/cache/filecache/filecache_pruner.go +++ b/cache/filecache/filecache_pruner.go @@ -17,6 +17,8 @@ import ( "io" "os" + "github.com/gohugoio/hugo/hugofs" + "github.com/pkg/errors" "github.com/spf13/afero" ) @@ -121,18 +123,6 @@ func (c *Cache) pruneRootDir(force bool) (int, error) { return 0, nil } - counter := 0 - // Module cache has 0555 directories; make them writable in order to remove content. - afero.Walk(c.Fs, c.pruneAllRootDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil - } - if info.IsDir() { - counter++ - c.Fs.Chmod(path, 0777) - } - return nil - }) - return 1, c.Fs.RemoveAll(c.pruneAllRootDir) + return hugofs.MakeReadableAndRemoveAllModulePkgDir(c.Fs, c.pruneAllRootDir) } diff --git a/commands/mod.go b/commands/mod.go index 582ebfda797..61e3c74e655 100644 --- a/commands/mod.go +++ b/commands/mod.go @@ -29,7 +29,28 @@ type modCmd struct { *baseBuilderCmd } +func (c *modCmd) newVerifyCmd() *cobra.Command { + var clean bool + + verifyCmd := &cobra.Command{ + Use: "verify", + Short: "Verify dependencies.", + Long: `Verify checks that the dependencies of the current module, which are stored in a local downloaded source cache, have not been modified since being downloaded. +`, + RunE: func(cmd *cobra.Command, args []string) error { + return c.withModsClient(true, func(c *modules.Client) error { + return c.Verify(clean) + }) + }, + } + + verifyCmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") + + return verifyCmd +} + func (b *commandsBuilder) newModCmd() *modCmd { + c := &modCmd{} const commonUsage = ` @@ -184,6 +205,7 @@ If a module is vendored, that is where Hugo will look for it's dependencies. }) }, }, + c.newVerifyCmd(), &cobra.Command{ Use: "tidy", Short: "Remove unused entries in go.mod and go.sum.", diff --git a/hugofs/fs.go b/hugofs/fs.go index 163807704a8..c8c4c8afd51 100644 --- a/hugofs/fs.go +++ b/hugofs/fs.go @@ -16,6 +16,7 @@ package hugofs import ( "os" + "strings" "github.com/gohugoio/hugo/config" "github.com/spf13/afero" @@ -88,3 +89,27 @@ func getWorkingDirFs(base afero.Fs, cfg config.Provider) *afero.BasePathFs { func isWrite(flag int) bool { return flag&os.O_RDWR != 0 || flag&os.O_WRONLY != 0 } + +// MakeReadableAndRemoveAllModulePkgDir makes any subdir in dir readable and then +// removes the root. +// TODO(bep) move this to a more suitable place. +// +func MakeReadableAndRemoveAllModulePkgDir(fs afero.Fs, dir string) (int, error) { + // Safe guard + if !strings.Contains(dir, "pkg") { + panic("invalid dir") + } + + counter := 0 + afero.Walk(fs, dir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return nil + } + if info.IsDir() { + counter++ + fs.Chmod(path, 0777) + } + return nil + }) + return counter, fs.RemoveAll(dir) +} diff --git a/modules/client.go b/modules/client.go index 8ea8b179409..601b5e109d9 100644 --- a/modules/client.go +++ b/modules/client.go @@ -24,6 +24,9 @@ import ( "os" "os/exec" "path/filepath" + "regexp" + + "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugofs/files" @@ -308,6 +311,38 @@ func (c *Client) Init(path string) error { return nil } +var verifyErrorDirRe = regexp.MustCompile(`dir has been modified \((.*?)\)`) + +// Verify checks that the dependencies of the current module, +// which are stored in a local downloaded source cache, have not been +// modified since being downloaded. +func (c *Client) Verify(clean bool) error { + // TODO1 add path to mod clean + err := c.runVerify() + + if err != nil { + if clean { + m := verifyErrorDirRe.FindAllStringSubmatch(err.Error(), -1) + if m != nil { + for i := 0; i < len(m); i++ { + c, err := hugofs.MakeReadableAndRemoveAllModulePkgDir(c.fs, m[i][1]) + if err != nil { + return err + } + fmt.Println("Cleaned", c) + } + } + // Try to verify it again. + err = c.runVerify() + } + } + return err +} + +func (c *Client) runVerify() error { + return c.runGo(context.Background(), ioutil.Discard, "mod", "verify") +} + func isProbablyModule(path string) bool { return module.CheckPath(path) == nil }