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  }