hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit 0b96aba022d51cf9939605c029bb8dba806653a1
parent fa520a2d983b982394ad10088393fb303e48980a
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date:   Wed, 19 Feb 2020 10:39:36 +0100

commands: Add "hugo mod verify"

See #6907

Diffstat:
Mcache/filecache/filecache_pruner.go | 16+++-------------
Mcommands/mod.go | 22++++++++++++++++++++++
Mhugofs/fs.go | 25+++++++++++++++++++++++++
Mmodules/client.go | 35+++++++++++++++++++++++++++++++++++
4 files changed, 85 insertions(+), 13 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
 }