github.com/lastbackend/toolkit@v0.0.0-20241020043710-cafa37b95aad/examples/service/magefile.go (about)

     1  //go:build mage
     2  // +build mage
     3  
     4  package main
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path"
    10  	"path/filepath"
    11  	"runtime"
    12  	"strings"
    13  	"time"
    14  
    15  	figure "github.com/common-nighthawk/go-figure"
    16  
    17  	"github.com/fatih/color"
    18  	"github.com/magefile/mage/mg"
    19  	"github.com/magefile/mage/sh"
    20  )
    21  
    22  var (
    23  	Default    = Build
    24  	goFiles    = getGoFiles()
    25  	goSrcFiles = getGoSrcFiles()
    26  )
    27  
    28  var curDir = func() string {
    29  	name, _ := os.Getwd()
    30  	return name
    31  }()
    32  
    33  var version = "0.0.1"
    34  
    35  // Calculate file paths
    36  var toolsBinDir = normalizePath(path.Join(curDir, "tools", "bin"))
    37  
    38  func init() {
    39  	time.Local = time.UTC
    40  
    41  	// Add local bin in PATH
    42  	err := os.Setenv("PATH", fmt.Sprintf("%s:%s", toolsBinDir, os.Getenv("PATH")))
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  }
    47  
    48  func Build() {
    49  	banner := figure.NewFigure("Example", "", true)
    50  	banner.Print()
    51  
    52  	fmt.Println("")
    53  	color.Red("# Build Info ---------------------------------------------------------------")
    54  	fmt.Printf("Go version : %s\n", runtime.Version())
    55  	fmt.Printf("Git revision : %s\n", hash())
    56  	fmt.Printf("Git branch : %s\n", branch())
    57  	fmt.Printf("Tag : %s\n", version)
    58  
    59  	fmt.Println("")
    60  
    61  	color.Red("# Core packages ------------------------------------------------------------")
    62  	mg.SerialDeps(Go.Generate, Go.Format, Go.Lint, Go.Test)
    63  
    64  	fmt.Println("")
    65  	color.Red("# Artifacts ----------------------------------------------------------------")
    66  	mg.Deps(Bin.HelloWorld)
    67  }
    68  
    69  // -----------------------------------------------------------------------------
    70  
    71  type Gen mg.Namespace
    72  
    73  // Generate mocks for tests
    74  func (Gen) Mocks() {
    75  	color.Blue("### Mocks")
    76  
    77  	mustGoGenerate("Mocks", "github.com/lastbackend/toolkit/example/service/gen/example/pkg/v1")
    78  }
    79  
    80  // Generate protobuf
    81  func (Gen) Protobuf() error {
    82  	color.Blue("### Protobuf")
    83  	mg.SerialDeps(Prototool.Lint)
    84  
    85  	return sh.RunV("buf", "generate")
    86  }
    87  
    88  // -----------------------------------------------------------------------------
    89  
    90  type Prototool mg.Namespace
    91  
    92  func (Prototool) Lint() error {
    93  	fmt.Println("#### Lint protobuf")
    94  	return sh.RunV("buf", "lint")
    95  }
    96  
    97  func (Prototool) Format() error {
    98  	fmt.Println("#### Format protobuf")
    99  	return sh.RunV("buf", "format")
   100  }
   101  
   102  // -----------------------------------------------------------------------------
   103  
   104  type Go mg.Namespace
   105  
   106  // Generate go code
   107  func (Go) Generate() error {
   108  	color.Cyan("## Generate code")
   109  	mg.SerialDeps(Gen.Protobuf, Gen.Mocks)
   110  	return nil
   111  }
   112  
   113  // Test run go test
   114  func (Go) Test() error {
   115  	color.Cyan("## Running unit tests")
   116  	sh.Run("mkdir", "-p", "test-results/junit")
   117  	return sh.RunV("gotestsum", "--junitfile", "test-results/junit/unit-tests.xml", "--", "-short", "-race", "-cover", "-coverprofile", "test-results/cover.out", "./...")
   118  }
   119  
   120  // AnalyzeCoverage calculates a single coverage percentage
   121  func (Go) AnalyzeCoverage() error {
   122  	color.Cyan("## Analyze tests coverage")
   123  	return sh.RunV("go-agg-cov", "-coverFile", "test-results/cover.out", "-minCoverageThreshold", "90")
   124  }
   125  
   126  // Test run go test
   127  func (Go) IntegrationTest() {
   128  	color.Cyan("## Running integration tests")
   129  	sh.Run("mkdir", "-p", "test-results/junit")
   130  }
   131  
   132  // Verifying dependencies
   133  func (Go) Verify() error {
   134  	fmt.Println("## Verifying dependencies")
   135  	return sh.RunV("go", "mod", "verify")
   136  }
   137  
   138  // Tidy add/remove depenedencies.
   139  func (Go) Tidy() error {
   140  	fmt.Println("## Cleaning go modules")
   141  	return sh.RunV("go", "mod", "tidy", "-v")
   142  }
   143  
   144  // Deps install dependency tools.
   145  func (Go) Deps() error {
   146  	color.Cyan("## Vendoring dependencies")
   147  	return sh.RunV("go", "mod", "vendor")
   148  }
   149  
   150  // Format runs gofmt on everything
   151  func (Go) Format() error {
   152  	color.Cyan("## Format everything")
   153  	args := []string{"-s", "-w"}
   154  	args = append(args, goFiles...)
   155  	return sh.RunV("gofumpt", args...)
   156  }
   157  
   158  // Import runs goimports on everything
   159  func (Go) Import() error {
   160  	color.Cyan("## Process imports")
   161  	args := []string{"-w"}
   162  	args = append(args, goSrcFiles...)
   163  	return sh.RunV("goreturns", args...)
   164  }
   165  
   166  // Lint run linter.
   167  func (Go) Lint() error {
   168  	mg.Deps(Go.Format)
   169  	color.Cyan("## Lint go code")
   170  	return sh.RunV("golangci-lint", "run")
   171  }
   172  
   173  type Bin mg.Namespace
   174  
   175  // Build licman microservice
   176  func (Bin) HelloWorld() error {
   177  	return goBuild("github.com/lastbackend/toolkit/examples/service/main", "example")
   178  }
   179  
   180  func goBuild(packageName, out string) error {
   181  	fmt.Printf(" > Building %s [%s]\n", out, packageName)
   182  
   183  	// TODO: version replace with tag()
   184  	varsSetByLinker := map[string]string{
   185  		"github.com/lastbackend/toolkit/examples/service/main.Version":   version,
   186  		"github.com/lastbackend/toolkit/examples/service/main.Revision":  hash(),
   187  		"github.com/lastbackend/toolkit/examples/service/main.Branch":    branch(),
   188  		"github.com/lastbackend/toolkit/examples/service/main.BuildUser": os.Getenv("USER"),
   189  		"github.com/lastbackend/toolkit/examples/service/main.BuildDate": time.Now().Format(time.RFC3339),
   190  		"github.com/lastbackend/toolkit/examples/service/main.GoVersion": runtime.Version(),
   191  	}
   192  	var linkerArgs string
   193  	for name, value := range varsSetByLinker {
   194  		linkerArgs += fmt.Sprintf(" -X %s=%s", name, value)
   195  	}
   196  	linkerArgs = fmt.Sprintf("-s -w %s", linkerArgs)
   197  
   198  	return sh.Run("go", "build", "-mod", "vendor", "-ldflags", linkerArgs, "-o", fmt.Sprintf("bin/%s", out), packageName)
   199  }
   200  
   201  // -----------------------------------------------------------------------------
   202  
   203  func getGoFiles() []string {
   204  	var goFiles []string
   205  
   206  	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
   207  		if strings.Contains(path, "vendor/") {
   208  			return filepath.SkipDir
   209  		}
   210  
   211  		if !strings.HasSuffix(path, ".go") {
   212  			return nil
   213  		}
   214  
   215  		goFiles = append(goFiles, path)
   216  		return nil
   217  	})
   218  
   219  	return goFiles
   220  }
   221  
   222  func getGoSrcFiles() []string {
   223  	var goSrcFiles []string
   224  
   225  	for _, path := range goFiles {
   226  		if !strings.HasSuffix(path, "_test.go") {
   227  			continue
   228  		}
   229  
   230  		goSrcFiles = append(goSrcFiles, path)
   231  	}
   232  
   233  	return goSrcFiles
   234  }
   235  
   236  // tag returns the git tag for the current branch or "" if none.
   237  func tag() string {
   238  	s, _ := sh.Output("git", "describe", "--tags")
   239  	fmt.Println(s)
   240  	return s
   241  }
   242  
   243  // hash returns the git hash for the current repo or "" if none.
   244  func hash() string {
   245  	hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
   246  	return hash
   247  }
   248  
   249  // branch returns the git branch for current repo
   250  func branch() string {
   251  	hash, _ := sh.Output("git", "rev-parse", "--abbrev-ref", "HEAD")
   252  	return hash
   253  }
   254  
   255  func mustStr(r string, err error) string {
   256  	if err != nil {
   257  		panic(err)
   258  	}
   259  	return r
   260  }
   261  
   262  func mustGoGenerate(txt, name string) {
   263  	fmt.Printf(" > %s [%s]\n", txt, name)
   264  	err := sh.RunV("go", "generate", name)
   265  	if err != nil {
   266  		panic(err)
   267  	}
   268  }
   269  
   270  func runIntegrationTest(txt, name string) {
   271  	fmt.Printf(" > %s [%s]\n", txt, name)
   272  	err := sh.RunV("gotestsum", "--junitfile", fmt.Sprintf("test-results/junit/integration-%s.xml", strings.ToLower(txt)), name, "--", "-tags=integration", "-race")
   273  	if err != nil {
   274  		panic(err)
   275  	}
   276  }
   277  
   278  // normalizePath turns a path into an absolute path and removes symlinks
   279  func normalizePath(name string) string {
   280  	absPath := mustStr(filepath.Abs(name))
   281  	return absPath
   282  }