github.com/gohugoio/hugo@v0.88.1/commands/mod.go (about) 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 && !moduleNotFoundRe.MatchString(err.Error()) { 75 return err 76 } 77 78 _, err = com.hugo().FileCaches.ModulesCache().Prune(true) 79 return err 80 } 81 return c.withModsClient(true, func(c *modules.Client) error { 82 return c.Clean(pattern) 83 }) 84 }, 85 } 86 87 cmd.Flags().StringVarP(&pattern, "pattern", "", "", `pattern matching module paths to clean (all if not set), e.g. "**hugo*"`) 88 cmd.Flags().BoolVarP(&all, "all", "", false, "clean entire module cache") 89 90 return cmd 91 } 92 93 func (b *commandsBuilder) newModCmd() *modCmd { 94 c := &modCmd{} 95 96 const commonUsage = ` 97 Note that Hugo will always start out by resolving the components defined in the site 98 configuration, provided by a _vendor directory (if no --ignoreVendor flag provided), 99 Go Modules, or a folder inside the themes directory, in that order. 100 101 See https://gohugo.io/hugo-modules/ for more information. 102 103 ` 104 105 cmd := &cobra.Command{ 106 Use: "mod", 107 Short: "Various Hugo Modules helpers.", 108 Long: `Various helpers to help manage the modules in your project's dependency graph. 109 110 Most operations here requires a Go version installed on your system (>= Go 1.12) and the relevant VCS client (typically Git). 111 This is not needed if you only operate on modules inside /themes or if you have vendored them via "hugo mod vendor". 112 113 ` + commonUsage, 114 115 RunE: nil, 116 } 117 118 cmd.AddCommand(newModNPMCmd(c)) 119 120 cmd.AddCommand( 121 &cobra.Command{ 122 Use: "get", 123 DisableFlagParsing: true, 124 Short: "Resolves dependencies in your current Hugo Project.", 125 Long: ` 126 Resolves dependencies in your current Hugo Project. 127 128 Some examples: 129 130 Install the latest version possible for a given module: 131 132 hugo mod get github.com/gohugoio/testshortcodes 133 134 Install a specific version: 135 136 hugo mod get github.com/gohugoio/testshortcodes@v0.3.0 137 138 Install the latest versions of all module dependencies: 139 140 hugo mod get -u 141 hugo mod get -u ./... (recursive) 142 143 Run "go help get" for more information. All flags available for "go get" is also relevant here. 144 ` + commonUsage, 145 RunE: func(cmd *cobra.Command, args []string) error { 146 // We currently just pass on the flags we get to Go and 147 // need to do the flag handling manually. 148 if len(args) == 1 && args[0] == "-h" { 149 return cmd.Help() 150 } 151 152 var lastArg string 153 if len(args) != 0 { 154 lastArg = args[len(args)-1] 155 } 156 157 if lastArg == "./..." { 158 args = args[:len(args)-1] 159 // Do a recursive update. 160 dirname, err := os.Getwd() 161 if err != nil { 162 return err 163 } 164 165 // Sanity check. We do recursive walking and want to avoid 166 // accidents. 167 if len(dirname) < 5 { 168 return errors.New("must not be run from the file system root") 169 } 170 171 filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { 172 if info.IsDir() { 173 return nil 174 } 175 176 if info.Name() == "go.mod" { 177 // Found a module. 178 dir := filepath.Dir(path) 179 fmt.Println("Update module in", dir) 180 c.source = dir 181 err := c.withModsClient(false, func(c *modules.Client) error { 182 if len(args) == 1 && args[0] == "-h" { 183 return cmd.Help() 184 } 185 return c.Get(args...) 186 }) 187 if err != nil { 188 return err 189 } 190 191 } 192 193 return nil 194 }) 195 196 return nil 197 } 198 199 return c.withModsClient(false, func(c *modules.Client) error { 200 return c.Get(args...) 201 }) 202 }, 203 }, 204 &cobra.Command{ 205 Use: "graph", 206 Short: "Print a module dependency graph.", 207 Long: `Print a module dependency graph with information about module status (disabled, vendored). 208 Note that for vendored modules, that is the version listed and not the one from go.mod. 209 `, 210 RunE: func(cmd *cobra.Command, args []string) error { 211 return c.withModsClient(true, func(c *modules.Client) error { 212 return c.Graph(os.Stdout) 213 }) 214 }, 215 }, 216 &cobra.Command{ 217 Use: "init", 218 Short: "Initialize this project as a Hugo Module.", 219 Long: `Initialize this project as a Hugo Module. 220 It will try to guess the module path, but you may help by passing it as an argument, e.g: 221 222 hugo mod init github.com/gohugoio/testshortcodes 223 224 Note that Hugo Modules supports multi-module projects, so you can initialize a Hugo Module 225 inside a subfolder on GitHub, as one example. 226 `, 227 RunE: func(cmd *cobra.Command, args []string) error { 228 var path string 229 if len(args) >= 1 { 230 path = args[0] 231 } 232 return c.withModsClient(false, func(c *modules.Client) error { 233 return c.Init(path) 234 }) 235 }, 236 }, 237 &cobra.Command{ 238 Use: "vendor", 239 Short: "Vendor all module dependencies into the _vendor directory.", 240 Long: `Vendor all module dependencies into the _vendor directory. 241 242 If a module is vendored, that is where Hugo will look for it's dependencies. 243 `, 244 RunE: func(cmd *cobra.Command, args []string) error { 245 return c.withModsClient(true, func(c *modules.Client) error { 246 return c.Vendor() 247 }) 248 }, 249 }, 250 c.newVerifyCmd(), 251 &cobra.Command{ 252 Use: "tidy", 253 Short: "Remove unused entries in go.mod and go.sum.", 254 RunE: func(cmd *cobra.Command, args []string) error { 255 return c.withModsClient(true, func(c *modules.Client) error { 256 return c.Tidy() 257 }) 258 }, 259 }, 260 c.newCleanCmd(), 261 ) 262 263 c.baseBuilderCmd = b.newBuilderCmd(cmd) 264 265 return c 266 } 267 268 func (c *modCmd) withModsClient(failOnMissingConfig bool, f func(*modules.Client) error) error { 269 com, err := c.initConfig(failOnMissingConfig) 270 if err != nil { 271 return err 272 } 273 274 return f(com.hugo().ModulesClient) 275 } 276 277 func (c *modCmd) withHugo(f func(*hugolib.HugoSites) error) error { 278 com, err := c.initConfig(true) 279 if err != nil { 280 return err 281 } 282 283 return f(com.hugo()) 284 } 285 286 func (c *modCmd) initConfig(failOnNoConfig bool) (*commandeer, error) { 287 com, err := initializeConfig(failOnNoConfig, false, false, &c.hugoBuilderCommon, c, nil) 288 if err != nil { 289 return nil, err 290 } 291 return com, nil 292 }