github.com/neohugo/neohugo@v0.123.8/magefile.go (about) 1 //go:build mage 2 // +build mage 3 4 package main 5 6 import ( 7 "bytes" 8 "errors" 9 "fmt" 10 "os" 11 "path" 12 "path/filepath" 13 "runtime" 14 "strings" 15 "sync" 16 "time" 17 18 "github.com/neohugo/neohugo/codegen" 19 "github.com/neohugo/neohugo/resources/page/page_generate" 20 21 "github.com/magefile/mage/mg" 22 "github.com/magefile/mage/sh" 23 ) 24 25 const ( 26 packageName = "github.com/neohugo/neohugo" 27 noGitLdflags = "-X github.com/neohugo/neohugo/common/neohugo.vendorInfo=mage" 28 ) 29 30 var ldflags = noGitLdflags 31 32 // allow user to override go executable by running as GOEXE=xxx make ... on unix-like systems 33 var goexe = "go" 34 35 func init() { 36 if exe := os.Getenv("GOEXE"); exe != "" { 37 goexe = exe 38 } 39 40 // We want to use Go 1.11 modules even if the source lives inside GOPATH. 41 // The default is "auto". 42 os.Setenv("GO111MODULE", "on") 43 } 44 45 func runWith(env map[string]string, cmd string, inArgs ...any) error { 46 s := argsToStrings(inArgs...) 47 return sh.RunWith(env, cmd, s...) 48 } 49 50 // Build hugo binary 51 func NeoHugo() error { 52 return runWith(flagEnv(), goexe, "build", "-ldflags", ldflags, buildFlags(), "-tags", buildTags(), packageName) 53 } 54 55 // Build hugo binary with race detector enabled 56 func NeoHugoRace() error { 57 return runWith(flagEnv(), goexe, "build", "-race", "-ldflags", ldflags, buildFlags(), "-tags", buildTags(), packageName) 58 } 59 60 // Install hugo binary 61 func Install() error { 62 return runWith(flagEnv(), goexe, "install", "-ldflags", ldflags, buildFlags(), "-tags", buildTags(), packageName) 63 } 64 65 // Uninstall hugo binary 66 func Uninstall() error { 67 return sh.Run(goexe, "clean", "-i", packageName) 68 } 69 70 func flagEnv() map[string]string { 71 hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD") 72 return map[string]string{ 73 "PACKAGE": packageName, 74 "COMMIT_HASH": hash, 75 "BUILD_DATE": time.Now().Format("2006-01-02T15:04:05Z0700"), 76 } 77 } 78 79 // Generate autogen packages 80 func Generate() error { 81 generatorPackages := []string{ 82 //"tpl/tplimpl/embedded/generate", 83 //"resources/page/generate", 84 } 85 86 for _, pkg := range generatorPackages { 87 if err := runWith(flagEnv(), goexe, "generate", path.Join(packageName, pkg)); err != nil { 88 return err 89 } 90 } 91 92 dir, _ := os.Getwd() 93 c := codegen.NewInspector(dir) 94 95 if err := page_generate.Generate(c); err != nil { 96 return err 97 } 98 99 gofumptPatterns := []string{ 100 // TODO(bep) check: stat ./resources/page/*autogen*: no such file or directory 101 "./resources/page/page_marshaljson.autogen.go", 102 "./resources/page/page_wrappers.autogen.go", 103 "./resources/page/zero_file.autogen.go", 104 } 105 106 for _, pattern := range gofumptPatterns { 107 if err := sh.Run("gofumpt", "-w", filepath.FromSlash(pattern)); err != nil { 108 return err 109 } 110 } 111 112 return nil 113 } 114 115 // Generate docs helper 116 func GenDocsHelper() error { 117 return runCmd(flagEnv(), goexe, "run", "-tags", buildTags(), "main.go", "gen", "docshelper") 118 } 119 120 // Build hugo without git info 121 func HugoNoGitInfo() error { 122 ldflags = noGitLdflags 123 return NeoHugo() 124 } 125 126 var docker = sh.RunCmd("docker") 127 128 // Build hugo Docker container 129 func Docker() error { 130 if err := docker("build", "-t", "hugo", "."); err != nil { 131 return err 132 } 133 // yes ignore errors here 134 docker("rm", "-f", "hugo-build") 135 if err := docker("run", "--name", "hugo-build", "hugo ls /go/bin"); err != nil { 136 return err 137 } 138 if err := docker("cp", "hugo-build:/go/bin/hugo", "."); err != nil { 139 return err 140 } 141 return docker("rm", "hugo-build") 142 } 143 144 // Run tests and linters 145 func Check() { 146 if runtime.GOARCH == "amd64" && runtime.GOOS != "darwin" { 147 mg.Deps(Test386) 148 } else { 149 fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS) 150 } 151 152 mg.Deps(Fmt, Vet) 153 154 // don't run two tests in parallel, they saturate the CPUs anyway, and running two 155 // causes memory issues in CI. 156 mg.Deps(TestRace) 157 } 158 159 func testGoFlags() string { 160 if isCI() { 161 return "" 162 } 163 164 return "-timeout=1m" 165 } 166 167 // Run tests in 32-bit mode 168 // Note that we don't run with the extended tag. Currently not supported in 32 bit. 169 func Test386() error { 170 env := map[string]string{"GOARCH": "386", "GOFLAGS": testGoFlags()} 171 return runCmd(env, goexe, "test", "./...") 172 } 173 174 // Run tests 175 func Test() error { 176 env := map[string]string{"GOFLAGS": testGoFlags()} 177 return runCmd(env, goexe, "test", "./...", "-tags", buildTags()) 178 } 179 180 // Run tests with race detector 181 func TestRace() error { 182 env := map[string]string{"GOFLAGS": testGoFlags()} 183 return runCmd(env, goexe, "test", "-race", "./...", "-tags", buildTags()) 184 } 185 186 // Fmt, run gofumpt linter 187 func Fmt() error { 188 if !isGoLatest() && !isUnix() { 189 return nil 190 } 191 s, err := sh.Output("./check_gofmt.sh") 192 if err != nil { 193 fmt.Println(s) 194 return fmt.Errorf("gofmt needs to be run: %s", err) 195 } 196 197 return nil 198 } 199 200 var ( 201 pkgPrefixLen = len("github.com/neohugo/neohugo") 202 pkgs []string 203 pkgsInit sync.Once 204 ) 205 206 func hugoPackages() ([]string, error) { 207 var err error 208 pkgsInit.Do(func() { 209 var s string 210 s, err = sh.Output(goexe, "list", "./...") 211 if err != nil { 212 return 213 } 214 pkgs = strings.Split(s, "\n") 215 for i := range pkgs { 216 pkgs[i] = "." + pkgs[i][pkgPrefixLen:] 217 } 218 }) 219 return pkgs, err 220 } 221 222 // List, run golint linter 223 func Lint() error { 224 pkgs, err := hugoPackages() 225 if err != nil { 226 return err 227 } 228 failed := false 229 for _, pkg := range pkgs { 230 // We don't actually want to fail this target if we find golint errors, 231 // so we don't pass -set_exit_status, but we still print out any failures. 232 if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil { 233 fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err) 234 failed = true 235 } 236 } 237 if failed { 238 return errors.New("errors running golint") 239 } 240 return nil 241 } 242 243 // Run go vet linter 244 func Vet() error { 245 if err := sh.Run(goexe, "vet", "./..."); err != nil { 246 return fmt.Errorf("error running go vet: %v", err) 247 } 248 return nil 249 } 250 251 // Generate test coverage report 252 func TestCoverHTML() error { 253 const ( 254 coverAll = "coverage-all.out" 255 cover = "coverage.out" 256 ) 257 f, err := os.Create(coverAll) 258 if err != nil { 259 return err 260 } 261 defer f.Close() 262 if _, err := f.Write([]byte("mode: count")); err != nil { 263 return err 264 } 265 pkgs, err := hugoPackages() 266 if err != nil { 267 return err 268 } 269 for _, pkg := range pkgs { 270 if err := sh.Run(goexe, "test", "-coverprofile="+cover, "-covermode=count", pkg); err != nil { 271 return err 272 } 273 b, err := os.ReadFile(cover) 274 if err != nil { 275 if os.IsNotExist(err) { 276 continue 277 } 278 return err 279 } 280 idx := bytes.Index(b, []byte{'\n'}) 281 b = b[idx+1:] 282 if _, err := f.Write(b); err != nil { 283 return err 284 } 285 } 286 if err := f.Close(); err != nil { 287 return err 288 } 289 return sh.Run(goexe, "tool", "cover", "-html="+coverAll) 290 } 291 292 func runCmd(env map[string]string, cmd string, args ...any) error { 293 if mg.Verbose() { 294 return runWith(env, cmd, args...) 295 } 296 output, err := sh.OutputWith(env, cmd, argsToStrings(args...)...) 297 if err != nil { 298 fmt.Fprint(os.Stderr, output) 299 } 300 301 return err 302 } 303 304 func isGoLatest() bool { 305 return strings.Contains(runtime.Version(), "1.21") 306 } 307 308 func isUnix() bool { 309 return runtime.GOOS != "windows" 310 } 311 312 func isCI() bool { 313 return os.Getenv("CI") != "" 314 } 315 316 func buildFlags() []string { 317 if runtime.GOOS == "windows" { 318 return []string{"-buildmode", "exe"} 319 } 320 return nil 321 } 322 323 func buildTags() string { 324 // To build the extended Hugo SCSS/SASS enabled version, build with 325 // HUGO_BUILD_TAGS=extended mage install etc. 326 // To build without `hugo deploy` for smaller binary, use HUGO_BUILD_TAGS=nodeploy 327 if envtags := os.Getenv("HUGO_BUILD_TAGS"); envtags != "" { 328 return envtags 329 } 330 return "none" 331 } 332 333 func argsToStrings(v ...any) []string { 334 var args []string 335 for _, arg := range v { 336 switch v := arg.(type) { 337 case string: 338 if v != "" { 339 args = append(args, v) 340 } 341 case []string: 342 if v != nil { 343 args = append(args, v...) 344 } 345 default: 346 panic("invalid type") 347 } 348 } 349 350 return args 351 }