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 }