mod.go (8124B)
1 // Copyright 2020 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package commands 15 16 import ( 17 "errors" 18 "fmt" 19 "os" 20 "path/filepath" 21 "regexp" 22 23 "github.com/gohugoio/hugo/hugolib" 24 25 "github.com/gohugoio/hugo/modules" 26 "github.com/spf13/cobra" 27 ) 28 29 var _ cmder = (*modCmd)(nil) 30 31 type modCmd struct { 32 *baseBuilderCmd 33 } 34 35 func (c *modCmd) newVerifyCmd() *cobra.Command { 36 var clean bool 37 38 verifyCmd := &cobra.Command{ 39 Use: "verify", 40 Short: "Verify dependencies.", 41 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. 42 `, 43 RunE: func(cmd *cobra.Command, args []string) error { 44 return c.withModsClient(true, func(c *modules.Client) error { 45 return c.Verify(clean) 46 }) 47 }, 48 } 49 50 verifyCmd.Flags().BoolVarP(&clean, "clean", "", false, "delete module cache for dependencies that fail verification") 51 52 return verifyCmd 53 } 54 55 var moduleNotFoundRe = regexp.MustCompile("module.*not found") 56 57 func (c *modCmd) newCleanCmd() *cobra.Command { 58 var pattern string 59 var all bool 60 cmd := &cobra.Command{ 61 Use: "clean", 62 Short: "Delete the Hugo Module cache for the current project.", 63 Long: `Delete the Hugo Module cache for the current project. 64 65 Note that after you run this command, all of your dependencies will be re-downloaded next time you run "hugo". 66 67 Also note that if you configure a positive maxAge for the "modules" file cache, it will also be cleaned as part of "hugo --gc". 68 69 `, 70 RunE: func(cmd *cobra.Command, args []string) error { 71 if all { 72 com, err := c.initConfig(false) 73 74 if err != nil && com == nil { 75 return err 76 } 77 78 count, err := com.hugo().FileCaches.ModulesCache().Prune(true) 79 com.logger.Printf("Deleted %d files from module cache.", count) 80 return err 81 } 82 return c.withModsClient(true, func(c *modules.Client) error { 83 return c.Clean(pattern) 84 }) 85 }, 86 } 87 88 cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`) 89 cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache") 90 91 return cmd 92 } 93 94 func (b *commandsBuilder) newModCmd() *modCmd { 95 c := &modCmd{} 96 97 const commonUsage = ` 98 Note that Hugo will always start out by resolving the components defined in the site 99 configuration, provided by a _vendor directory (if no --ignoreVendorPaths flag provided), 100 Go Modules, or a folder inside the themes directory, in that order. 101 102 See https://gohugo.io/hugo-modules/ for more information. 103 104 ` 105 106 cmd := &cobra.Command{ 107 Use: "mod", 108 Short: "Various Hugo Modules helpers.", 109 Long: `Various helpers to help manage the modules in your project's dependency graph. 110 111 Most operations here requires a Go version installed on your system (>= Go 1.12) and the relevant VCS client (typically Git). 112 This is not needed if you only operate on modules inside /themes or if you have vendored them via "hugo mod vendor". 113 114 ` + commonUsage, 115 116 RunE: nil, 117 } 118 119 cmd.AddCommand(newModNPMCmd(c)) 120 121 cmd.AddCommand( 122 &cobra.Command{ 123 Use: "get", 124 DisableFlagParsing: true, 125 Short: "Resolves dependencies in your current Hugo Project.", 126 Long: ` 127 Resolves dependencies in your current Hugo Project. 128 129 Some examples: 130 131 Install the latest version possible for a given module: 132 133 hugo mod get github.com/gohugoio/testshortcodes 134 135 Install a specific version: 136 137 hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 138 139 Install the latest versions of all module dependencies: 140 141 hugo mod get -u 142 hugo mod get -u ./... (recursive) 143 144 Run "go help get" for more information. All flags available for "go get" is also relevant here. 145 ` + commonUsage, 146 RunE: func(cmd *cobra.Command, args []string) error { 147 // We currently just pass on the flags we get to Go and 148 // need to do the flag handling manually. 149 if len(args) == 1 && args[0] == "-h" { 150 return cmd.Help() 151 } 152 153 var lastArg string 154 if len(args) != 0 { 155 lastArg = args[len(args)-1] 156 } 157 158 if lastArg == "./..." { 159 args = args[:len(args)-1] 160 // Do a recursive update. 161 dirname, err := os.Getwd() 162 if err != nil { 163 return err 164 } 165 166 // Sanity check. We do recursive walking and want to avoid 167 // accidents. 168 if len(dirname) < 5 { 169 return errors.New("must not be run from the file system root") 170 } 171 172 filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { 173 if info.IsDir() { 174 return nil 175 } 176 177 if info.Name() == "go.mod" { 178 // Found a module. 179 dir := filepath.Dir(path) 180 fmt.Println("Update module in", dir) 181 c.source = dir 182 err := c.withModsClient(false, func(c *modules.Client) error { 183 if len(args) == 1 && args[0] == "-h" { 184 return cmd.Help() 185 } 186 return c.Get(args...) 187 }) 188 if err != nil { 189 return err 190 } 191 192 } 193 194 return nil 195 }) 196 197 return nil 198 } 199 200 return c.withModsClient(false, func(c *modules.Client) error { 201 return c.Get(args...) 202 }) 203 }, 204 }, 205 &cobra.Command{ 206 Use: "graph", 207 Short: "Print a module dependency graph.", 208 Long: `Print a module dependency graph with information about module status (disabled, vendored). 209 Note that for vendored modules, that is the version listed and not the one from go.mod. 210 `, 211 RunE: func(cmd *cobra.Command, args []string) error { 212 return c.withModsClient(true, func(c *modules.Client) error { 213 return c.Graph(os.Stdout) 214 }) 215 }, 216 }, 217 &cobra.Command{ 218 Use: "init", 219 Short: "Initialize this project as a Hugo Module.", 220 Long: `Initialize this project as a Hugo Module. 221 It will try to guess the module path, but you may help by passing it as an argument, e.g: 222 223 hugo mod init github.com/gohugoio/testshortcodes 224 225 Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module 226 inside a subfolder on GitHub, as one example. 227 `, 228 RunE: func(cmd *cobra.Command, args []string) error { 229 var path string 230 if len(args) >= 1 { 231 path = args[0] 232 } 233 return c.withModsClient(false, func(c *modules.Client) error { 234 return c.Init(path) 235 }) 236 }, 237 }, 238 &cobra.Command{ 239 Use: "vendor", 240 Short: "Vendor all module dependencies into the _vendor directory.", 241 Long: `Vendor all module dependencies into the _vendor directory. 242 243 If a module is vendored, that is where Hugo will look for it's dependencies. 244 `, 245 RunE: func(cmd *cobra.Command, args []string) error { 246 return c.withModsClient(true, func(c *modules.Client) error { 247 return c.Vendor() 248 }) 249 }, 250 }, 251 c.newVerifyCmd(), 252 &cobra.Command{ 253 Use: "tidy", 254 Short: "Remove unused entries in go.mod and go.sum.", 255 RunE: func(cmd *cobra.Command, args []string) error { 256 return c.withModsClient(true, func(c *modules.Client) error { 257 return c.Tidy() 258 }) 259 }, 260 }, 261 c.newCleanCmd(), 262 ) 263 264 c.baseBuilderCmd = b.newBuilderCmd(cmd) 265 266 return c 267 } 268 269 func (c *modCmd) withModsClient(failOnMissingConfig bool, f func(*modules.Client) error) error { 270 com, err := c.initConfig(failOnMissingConfig) 271 if err != nil { 272 return err 273 } 274 275 return f(com.hugo().ModulesClient) 276 } 277 278 func (c *modCmd) withHugo(f func(*hugolib.HugoSites) error) error { 279 com, err := c.initConfig(true) 280 if err != nil { 281 return err 282 } 283 284 return f(com.hugo()) 285 } 286 287 func (c *modCmd) initConfig(failOnNoConfig bool) (*commandeer, error) { 288 com, err := initializeConfig(failOnNoConfig, false, false, &c.hugoBuilderCommon, c, nil) 289 if err != nil { 290 return nil, err 291 } 292 return com, nil 293 }