mage.go
1 //go:build mage 2 3 package main 4 5 import ( 6 "fmt" 7 "os" 8 "path/filepath" 9 "runtime" 10 "strings" 11 "time" 12 "unicode" 13 14 "github.com/magefile/mage/mg" 15 "github.com/magefile/mage/sh" 16 ) 17 18 const ( 19 projectName = "enbas" 20 defaultAppName = projectName 21 defaultInstallPrefix = "/usr/local" 22 23 envInstallPrefix = "ENBAS_INSTALL_PREFIX" 24 envTestVerbose = "ENBAS_TEST_VERBOSE" 25 envTestCover = "ENBAS_TEST_COVER" 26 envBuildRebuildAll = "ENBAS_BUILD_REBUILD_ALL" 27 envBuildVerbose = "ENBAS_BUILD_VERBOSE" 28 envFailOnFormatting = "ENBAS_FAIL_ON_FORMATTING" 29 envAppName = "ENBAS_APP_NAME" 30 envAppVersion = "ENBAS_APP_VERSION" 31 ) 32 33 var Default = Build 34 35 // Test run the go tests. 36 // To enable verbose mode set ENBAS_TEST_VERBOSE=1. 37 // To enable coverage mode set ENBAS_TEST_COVER=1. 38 func Test() error { 39 goTest := sh.RunCmd("go", "test") 40 41 args := []string{"./..."} 42 43 if os.Getenv(envTestVerbose) == "1" { 44 args = append(args, "-v") 45 } 46 47 if os.Getenv(envTestCover) == "1" { 48 args = append(args, "-cover") 49 } 50 51 return goTest(args...) 52 } 53 54 // Lint runs golangci-lint against the code. 55 func Lint() error { 56 return sh.RunV("golangci-lint", "run", "--color", "always") 57 } 58 59 // Gosec runs gosec against the code. 60 func Gosec() error { 61 return sh.RunV("gosec", "./...") 62 } 63 64 // Staticcheck runs staticcheck against the code. 65 func Staticcheck() error { 66 return sh.RunV("staticcheck", "./...") 67 } 68 69 // Gofmt checks the code for formatting. 70 // To fail on formatting set BEACON_FAIL_ON_FORMATTING=1 71 func Gofmt() error { 72 output, err := sh.Output("go", "fmt", "./...") 73 if err != nil { 74 return err 75 } 76 77 formattedFiles := "" 78 79 for _, file := range strings.Split(output, "\n") { 80 formattedFiles += "\n- " + file 81 } 82 83 if os.Getenv(envFailOnFormatting) != "1" { 84 fmt.Println(formattedFiles) 85 86 return nil 87 } 88 89 if len(output) != 0 { 90 return fmt.Errorf("The following files needed to be formatted: %s", formattedFiles) 91 } 92 93 return nil 94 } 95 96 // Govet runs go vet against the code. 97 func Govet() error { 98 return sh.RunV("go", "vet", "./...") 99 } 100 101 // Build build the executable. 102 // To rebuild packages that are already up-to-date set ENBAS_BUILD_REBUILD_ALL=1 103 // To enable verbose mode set ENBAS_BUILD_VERBOSE=1 104 func Build() error { 105 fmt.Println("Building the binary...") 106 107 main := "./cmd/" + projectName 108 flags := ldflags() 109 build := sh.RunCmd("go", "build") 110 args := []string{"-ldflags=" + flags, "-o", binary()} 111 112 if os.Getenv(envBuildRebuildAll) == "1" { 113 args = append(args, "-a") 114 } 115 116 if os.Getenv(envBuildVerbose) == "1" { 117 args = append(args, "-v") 118 } 119 120 args = append(args, main) 121 122 if err := build(args...); err != nil { 123 return fmt.Errorf("error building the binary: %w", err) 124 } 125 126 return nil 127 } 128 129 // Install install the executable. 130 func Install() error { 131 mg.Deps(Build) 132 133 installPrefix := os.Getenv(envInstallPrefix) 134 app := appName() 135 binary := binary() 136 137 if installPrefix == "" { 138 installPrefix = defaultInstallPrefix 139 } 140 141 dest := filepath.Join(installPrefix, "bin", app) 142 143 fmt.Println("Installing the binary to", dest) 144 145 if err := sh.Copy(dest, binary); err != nil { 146 return fmt.Errorf("unable to install %s; %w", dest, err) 147 } 148 149 fmt.Printf("Successfully installed %s to %s\n", projectName, dest) 150 151 return nil 152 } 153 154 // Clean clean the workspace. 155 func Clean() error { 156 fmt.Println("Cleaning the workspace...") 157 158 if err := sh.Rm(binary()); err != nil { 159 return err 160 } 161 162 if err := sh.Run("go", "clean", "./..."); err != nil { 163 return err 164 } 165 166 fmt.Println("Workspace cleaned.") 167 168 return nil 169 } 170 171 // ldflags returns the build flags. 172 func ldflags() string { 173 var ( 174 infoPackage = "codeflow.dananglin.me.uk/apollo/enbas/internal/info" 175 binaryVersionVar = infoPackage + "." + "BinaryVersion" 176 gitCommitVar = infoPackage + "." + "GitCommit" 177 goVersionVar = infoPackage + "." + "GoVersion" 178 buildTimeVar = infoPackage + "." + "BuildTime" 179 applicationNameVar = infoPackage + "." + "ApplicationName" 180 applicationTitledNameVar = infoPackage + "." + "ApplicationTitledName" 181 182 ldflagsfmt = "-s -w -X %s=%s -X %s=%s -X %s=%s -X %s=%s -X %s=%s -X %s=%s" 183 buildTime = time.Now().UTC().Format(time.RFC3339) 184 ) 185 186 return fmt.Sprintf( 187 ldflagsfmt, 188 binaryVersionVar, binaryVersion(), 189 gitCommitVar, gitCommit(), 190 goVersionVar, runtime.Version(), 191 buildTimeVar, buildTime, 192 applicationNameVar, appName(), 193 applicationTitledNameVar, appTitledName(), 194 ) 195 } 196 197 // binaryVersion returns the version of the binary. 198 // If ENBAS_APP_VERSION is set, the value of that is returned, otherwise 199 // the latest git tag (using git describe) is returned. 200 func binaryVersion() string { 201 ver := os.Getenv(envAppVersion) 202 if ver != "" { 203 return ver 204 } 205 206 ver, err := sh.Output("git", "describe", "--tags") 207 if err != nil { 208 fmt.Printf("WARNING: error getting the binary version: %v.\n", err) 209 return "N/A" 210 } 211 212 return ver 213 } 214 215 // gitCommit returns the current git commit 216 func gitCommit() string { 217 commit, err := sh.Output("git", "rev-parse", "--short", "HEAD") 218 if err != nil { 219 commit = "N/A" 220 } 221 222 return commit 223 } 224 225 // appName returns the application's name. 226 // The value of ENBAS_APP_NAME is return if the environment variable is set 227 // otherwise the default name is returned. 228 func appName() string { 229 appName := os.Getenv(envAppName) 230 231 if appName == "" { 232 return defaultAppName 233 } 234 235 return appName 236 } 237 238 func binary() string { 239 return filepath.Join("./__build", appName()) 240 } 241 242 func appTitledName() string { 243 runes := []rune(appName()) 244 runes[0] = unicode.ToUpper(runes[0]) 245 246 return string(runes) 247 }