refactor: fix contextcheck lint errors on magefile by passing mage context

This commit is contained in:
John Starich
2026-03-06 01:12:23 -06:00
committed by kolaente
parent cea8c7807d
commit 0a1104b75c

View File

@@ -87,8 +87,8 @@ func goDetectVerboseFlag() string {
return fmt.Sprintf("-v=%t", mg.Verbose())
}
func runGitCommandWithOutput(arg ...string) (output []byte, err error) {
cmd := exec.Command("git", arg...)
func runGitCommandWithOutput(ctx context.Context, arg ...string) (output []byte, err error) {
cmd := exec.CommandContext(ctx, "git", arg...)
output, err = cmd.Output()
if err != nil {
var ee *exec.ExitError
@@ -101,8 +101,8 @@ func runGitCommandWithOutput(arg ...string) (output []byte, err error) {
return output, nil
}
func getRawVersionString() (version string, err error) {
version, err = getRawVersionNumber()
func getRawVersionString(ctx context.Context) (version string, err error) {
version, err = getRawVersionNumber(ctx)
if err != nil {
return
}
@@ -118,7 +118,7 @@ func getRawVersionString() (version string, err error) {
return
}
func getRawVersionNumber() (version string, err error) {
func getRawVersionNumber(ctx context.Context) (version string, err error) {
versionEnv := os.Getenv("RELEASE_VERSION")
if versionEnv != "" {
return versionEnv, nil
@@ -132,19 +132,19 @@ func getRawVersionNumber() (version string, err error) {
return strings.Replace(os.Getenv("DRONE_BRANCH"), "release/v", "", 1), nil
}
versionBytes, err := runGitCommandWithOutput("describe", "--tags", "--always", "--abbrev=10")
versionBytes, err := runGitCommandWithOutput(ctx, "describe", "--tags", "--always", "--abbrev=10")
return string(versionBytes), err
}
func setVersion() error {
versionNumber, err := getRawVersionNumber()
func setVersion(ctx context.Context) error {
versionNumber, err := getRawVersionNumber(ctx)
if err != nil {
return err
}
VersionNumber = strings.Trim(versionNumber, "\n")
VersionNumber = strings.Replace(VersionNumber, "-g", "-", 1)
version, err := getRawVersionString()
version, err := getRawVersionString(ctx)
if err != nil {
return fmt.Errorf("error getting version: %w", err)
}
@@ -178,13 +178,13 @@ func init() {
}
// Some variables have external dependencies (like git) which may not always be available.
func initVars() error {
func initVars(ctx context.Context) error {
// Always include osusergo to use pure Go os/user implementation instead of CGO.
// This prevents SIGFPE crashes when running under systemd without HOME set,
// caused by glibc's getpwuid_r() failing in certain environments.
// See: https://github.com/go-vikunja/vikunja/issues/2170
Tags = "osusergo " + strings.ReplaceAll(os.Getenv("TAGS"), ",", " ")
if err := setVersion(); err != nil {
if err := setVersion(ctx); err != nil {
return err
}
setBinLocation()
@@ -193,8 +193,8 @@ func initVars() error {
return nil
}
func runAndStreamOutput(cmd string, args ...string) error {
c := exec.Command(cmd, args...)
func runAndStreamOutput(ctx context.Context, cmd string, args ...string) error {
c := exec.CommandContext(ctx, cmd, args...)
c.Env = os.Environ()
c.Stdout = os.Stdout
@@ -206,10 +206,10 @@ func runAndStreamOutput(cmd string, args ...string) error {
// Will check if the tool exists and if not install it from the provided import path
// If any errors occur, it will exit with a status code of 1.
func checkAndInstallGoTool(tool, importPath string) error {
if err := exec.Command(tool).Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
func checkAndInstallGoTool(ctx context.Context, tool, importPath string) error {
if err := exec.CommandContext(ctx, tool).Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
fmt.Printf("%s not installed, installing %s...\n", tool, importPath)
if err := exec.Command("go", "install", goDetectVerboseFlag(), importPath).Run(); err != nil { //nolint:gosec // Every caller to checkAndInstallGoTool is hard-coded at time of writing, so no injection possible.
if err := exec.CommandContext(ctx, "go", "install", goDetectVerboseFlag(), importPath).Run(); err != nil { //nolint:gosec // Every caller to checkAndInstallGoTool is hard-coded at time of writing, so no injection possible.
return fmt.Errorf("error installing %s: %w", tool, err)
}
fmt.Println("Installed.")
@@ -324,16 +324,16 @@ func printSuccess(text string, args ...any) {
}
// getE2EPort returns the port from the given env var, or a random available port.
func getE2EPort(envVar string) (int, error) {
func getE2EPort(ctx context.Context, envVar string) (int, error) {
if v := os.Getenv(envVar); v != "" {
return strconv.Atoi(v)
}
return getRandomPort()
return getRandomPort(ctx)
}
// getRandomPort finds a random available TCP port.
func getRandomPort() (int, error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
func getRandomPort(ctx context.Context) (int, error) {
l, err := (&net.ListenConfig{}).Listen(ctx, "tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
@@ -362,11 +362,15 @@ func killProcessGroup(cmd *exec.Cmd) error {
}
// waitForHTTP polls a URL until it returns a 200 status or the timeout expires.
func waitForHTTP(url string, timeout time.Duration) error {
func waitForHTTP(ctx context.Context, url string, timeout time.Duration) error {
deadline := time.Now().Add(timeout)
client := &http.Client{Timeout: 2 * time.Second}
for time.Now().Before(deadline) {
resp, err := client.Get(url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err == nil {
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
@@ -379,7 +383,7 @@ func waitForHTTP(url string, timeout time.Duration) error {
}
// Fmt formats the code using go fmt
func Fmt() error {
func Fmt(ctx context.Context) error {
mg.Deps(initVars)
var goFiles []string
err := filepath.Walk(".", func(path string, info fs.FileInfo, err error) error {
@@ -395,37 +399,37 @@ func Fmt() error {
return err
}
args := append([]string{"-s", "-w"}, goFiles...)
return runAndStreamOutput("gofmt", args...)
return runAndStreamOutput(ctx, "gofmt", args...)
}
type Test mg.Namespace
// Feature runs the feature tests
func (Test) Feature() error {
func (Test) Feature(ctx context.Context) error {
mg.Deps(initVars)
// We run everything sequentially and not in parallel to prevent issues with real test databases
return runAndStreamOutput("go", "test", goDetectVerboseFlag(), "-p", "1", "-coverprofile", "cover.out", "-timeout", "45m", "-short", "./...")
return runAndStreamOutput(ctx, "go", "test", goDetectVerboseFlag(), "-p", "1", "-coverprofile", "cover.out", "-timeout", "45m", "-short", "./...")
}
// Coverage runs the tests and builds the coverage html file from coverage output
func (Test) Coverage() error {
func (Test) Coverage(ctx context.Context) error {
mg.Deps(initVars)
mg.Deps(Test.Feature)
return runAndStreamOutput("go", "tool", "cover", "-html=cover.out", "-o", "cover.html")
return runAndStreamOutput(ctx, "go", "tool", "cover", "-html=cover.out", "-o", "cover.html")
}
// Web runs the web tests
func (Test) Web() error {
func (Test) Web(ctx context.Context) error {
mg.Deps(initVars)
// We run everything sequentially and not in parallel to prevent issues with real test databases
args := []string{"test", goDetectVerboseFlag(), "-p", "1", "-timeout", "45m", "./pkg/webtests"}
return runAndStreamOutput("go", args...)
return runAndStreamOutput(ctx, "go", args...)
}
func (Test) Filter(filter string) error {
func (Test) Filter(ctx context.Context, filter string) error {
mg.Deps(initVars)
// We run everything sequentially and not in parallel to prevent issues with real test databases
return runAndStreamOutput("go", "test", goDetectVerboseFlag(), "-p", "1", "-timeout", "45m", "-run", filter, "-short", "./...")
return runAndStreamOutput(ctx, "go", "test", goDetectVerboseFlag(), "-p", "1", "-timeout", "45m", "-run", filter, "-short", "./...")
}
func (Test) All() {
@@ -436,9 +440,9 @@ func (Test) All() {
// E2EApi runs the end-to-end API tests in pkg/e2etests.
// These tests use the real event system (not events.Fake()) to verify
// the full async pipeline: web handler → DB → event dispatch → watermill → listener.
func (Test) E2EApi() error {
func (Test) E2EApi(ctx context.Context) error {
mg.Deps(initVars)
return runAndStreamOutput("go", "test", goDetectVerboseFlag(), "-p", "1", "-timeout", "45m", "./pkg/e2etests")
return runAndStreamOutput(ctx, "go", "test", goDetectVerboseFlag(), "-p", "1", "-timeout", "45m", "./pkg/e2etests")
}
// E2E builds the API, starts it with an in-memory database and the frontend dev server,
@@ -458,15 +462,15 @@ func (Test) E2EApi() error {
// - VIKUNJA_E2E_FRONTEND_PORT: Frontend port (default: random)
// - VIKUNJA_E2E_TESTING_TOKEN: Testing token for seed endpoints (default: random)
// - VIKUNJA_E2E_SKIP_BUILD: Set to "true" to skip rebuilding the API binary (default: false)
func (Test) E2E(args string) error {
func (Test) E2E(ctx context.Context, args string) error {
mg.Deps(initVars)
// Determine ports
apiPort, err := getE2EPort("VIKUNJA_E2E_API_PORT")
apiPort, err := getE2EPort(ctx, "VIKUNJA_E2E_API_PORT")
if err != nil {
return fmt.Errorf("could not get API port: %w", err)
}
frontendPort, err := getE2EPort("VIKUNJA_E2E_FRONTEND_PORT")
frontendPort, err := getE2EPort(ctx, "VIKUNJA_E2E_FRONTEND_PORT")
if err != nil {
return fmt.Errorf("could not get frontend port: %w", err)
}
@@ -485,7 +489,7 @@ func (Test) E2E(args string) error {
// Build the API binary (unless skipped)
if os.Getenv("VIKUNJA_E2E_SKIP_BUILD") != "true" {
fmt.Println("\n--- Building API binary ---")
if err := (Build{}).Build(); err != nil {
if err := (Build{}).Build(ctx); err != nil {
return fmt.Errorf("failed to build API: %w", err)
}
}
@@ -507,7 +511,7 @@ func (Test) E2E(args string) error {
// Start the API server — all config via env vars, no config file
// Uses in-memory SQLite (no DB file on disk)
fmt.Println("\n--- Starting API server ---")
apiCmd := exec.Command("./vikunja", "web")
apiCmd := exec.CommandContext(ctx, "./vikunja", "web")
apiCmd.Env = append(os.Environ(),
fmt.Sprintf("VIKUNJA_SERVICE_INTERFACE=:%d", apiPort),
fmt.Sprintf("VIKUNJA_SERVICE_PUBLICURL=http://127.0.0.1:%d/", apiPort),
@@ -538,14 +542,14 @@ func (Test) E2E(args string) error {
// Wait for API to be ready
apiBase := fmt.Sprintf("http://127.0.0.1:%d/api/v1", apiPort)
fmt.Printf("Waiting for API at %s ...\n", apiBase)
if err := waitForHTTP(apiBase+"/info", 30*time.Second); err != nil {
if err := waitForHTTP(ctx, apiBase+"/info", 30*time.Second); err != nil {
return fmt.Errorf("API failed to start: %w", err)
}
printSuccess("API is ready!")
// Build the frontend
fmt.Println("\n--- Building frontend ---")
buildFrontendCmd := exec.Command("pnpm", "build:dev")
buildFrontendCmd := exec.CommandContext(ctx, "pnpm", "build:dev")
buildFrontendCmd.Dir = "frontend"
buildFrontendCmd.Stdout = os.Stdout
buildFrontendCmd.Stderr = os.Stderr
@@ -556,7 +560,7 @@ func (Test) E2E(args string) error {
// Serve the built frontend with vite preview (static, no file watchers)
fmt.Println("\n--- Starting frontend preview server ---")
frontendCmd := exec.Command("pnpm", "preview:dev", "--port", strconv.Itoa(frontendPort)) //nolint:gosec // This mage task runs end to end tests with environment-based configuration, it must use the port environment variable to suit its current environment.
frontendCmd := exec.CommandContext(ctx, "pnpm", "preview:dev", "--port", strconv.Itoa(frontendPort)) //nolint:gosec // This mage task runs end to end tests with environment-based configuration, it must use the port environment variable to suit its current environment.
frontendCmd.Dir = "frontend"
frontendCmd.Stdout = os.Stdout
frontendCmd.Stderr = os.Stderr
@@ -574,7 +578,7 @@ func (Test) E2E(args string) error {
// Wait for frontend to be ready
frontendBase := fmt.Sprintf("http://127.0.0.1:%d", frontendPort)
fmt.Printf("Waiting for frontend at %s ...\n", frontendBase)
if err := waitForHTTP(frontendBase, 60*time.Second); err != nil {
if err := waitForHTTP(ctx, frontendBase, 60*time.Second); err != nil {
return fmt.Errorf("frontend failed to start: %w", err)
}
printSuccess("Frontend is ready!")
@@ -585,7 +589,7 @@ func (Test) E2E(args string) error {
if strings.TrimSpace(args) != "" {
playwrightArgs = append(playwrightArgs, strings.Fields(args)...)
}
playwrightCmd := exec.Command("pnpm", playwrightArgs...)
playwrightCmd := exec.CommandContext(ctx, "pnpm", playwrightArgs...)
playwrightCmd.Dir = "frontend"
playwrightCmd.Env = append(os.Environ(),
fmt.Sprintf("API_URL=%s/", apiBase),
@@ -609,7 +613,7 @@ func (Test) E2E(args string) error {
type Check mg.Namespace
// GotSwag checks if the swagger docs need to be re-generated from the code annotations
func (Check) GotSwag() error {
func (Check) GotSwag(ctx context.Context) error {
mg.Deps(initVars)
// The check is pretty cheaply done: We take the hash of the swagger.json file, generate the docs,
// hash the file again and compare the two hashes to see if anything changed. If that's the case,
@@ -622,7 +626,7 @@ func (Check) GotSwag() error {
return fmt.Errorf("error getting old hash of the swagger docs: %w", err)
}
if generateErr := (Generate{}).SwaggerDocs(); generateErr != nil {
if generateErr := (Generate{}).SwaggerDocs(ctx); generateErr != nil {
return generateErr
}
@@ -793,26 +797,26 @@ func extractTranslationKeysFromFile(filePath string) ([]TranslationKey, error) {
return keys, nil
}
func checkGolangCiLintInstalled() error {
func checkGolangCiLintInstalled(ctx context.Context) error {
mg.Deps(initVars)
if err := exec.Command("golangci-lint").Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
if err := exec.CommandContext(ctx, "golangci-lint").Run(); err != nil && strings.Contains(err.Error(), "executable file not found") {
return fmt.Errorf("golangci-lint executable failed to run, please manually install golangci-lint by running the command: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v2.4.0")
}
return nil
}
func (Check) Golangci() error {
if err := checkGolangCiLintInstalled(); err != nil {
func (Check) Golangci(ctx context.Context) error {
if err := checkGolangCiLintInstalled(ctx); err != nil {
return err
}
return runAndStreamOutput("golangci-lint", "run")
return runAndStreamOutput(ctx, "golangci-lint", "run")
}
func (Check) GolangciFix() error {
if err := checkGolangCiLintInstalled(); err != nil {
func (Check) GolangciFix(ctx context.Context) error {
if err := checkGolangCiLintInstalled(ctx); err != nil {
return err
}
return runAndStreamOutput("golangci-lint", "run", "--fix")
return runAndStreamOutput(ctx, "golangci-lint", "run", "--fix")
}
// All runs golangci and the swagger test in parallel
@@ -828,9 +832,9 @@ func (Check) All() {
type Build mg.Namespace
// Clean cleans all build, executable and bindata files
func (Build) Clean() error {
func (Build) Clean(ctx context.Context) error {
mg.Deps(initVars)
if err := exec.Command("go", "clean", "./...").Run(); err != nil {
if err := exec.CommandContext(ctx, "go", "clean", "./...").Run(); err != nil {
return err
}
if err := os.Remove(Executable); err != nil && !os.IsNotExist(err) {
@@ -846,7 +850,7 @@ func (Build) Clean() error {
}
// Build builds a vikunja binary, ready to run
func (Build) Build() error {
func (Build) Build(ctx context.Context) error {
mg.Deps(initVars)
// Check if the frontend dist folder exists
distPath := filepath.Join("frontend", "dist")
@@ -866,7 +870,7 @@ func (Build) Build() error {
fmt.Printf("Warning: %s not found, created empty file\n", indexFile)
}
return runAndStreamOutput("go", "build", goDetectVerboseFlag(), "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
return runAndStreamOutput(ctx, "go", "build", goDetectVerboseFlag(), "-tags", Tags, "-ldflags", "-s -w "+Ldflags, "-o", Executable)
}
func (Build) SaveVersionToFile() error {
@@ -898,9 +902,9 @@ func (Release) Release(ctx context.Context) error {
// Run compiling in parallel to speed it up
errs, _ := errgroup.WithContext(ctx)
errs.Go((Release{}).Windows)
errs.Go((Release{}).Linux)
errs.Go((Release{}).Darwin)
errgroupGoWithContext(ctx, errs, (Release{}).Windows)
errgroupGoWithContext(ctx, errs, (Release{}).Linux)
errgroupGoWithContext(ctx, errs, (Release{}).Darwin)
if err := errs.Wait(); err != nil {
return err
}
@@ -917,13 +921,19 @@ func (Release) Release(ctx context.Context) error {
if err := (Release{}).OsPackage(); err != nil {
return err
}
if err := (Release{}).Zip(); err != nil {
if err := (Release{}).Zip(ctx); err != nil {
return err
}
return nil
}
func errgroupGoWithContext(ctx context.Context, errs *errgroup.Group, do func(context.Context) error) {
errs.Go(func() error {
return do(ctx)
})
}
// Dirs creates all directories needed to release vikunja
func (Release) Dirs() error {
for _, d := range []string{"binaries", "release", "zip"} {
@@ -934,19 +944,19 @@ func (Release) Dirs() error {
return nil
}
func prepareXgo() error {
func prepareXgo(ctx context.Context) error {
mg.Deps(initVars)
if err := checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo"); err != nil {
if err := checkAndInstallGoTool(ctx, "xgo", "src.techknowlogick.com/xgo"); err != nil {
return err
}
fmt.Println("Pulling latest xgo docker image...")
return runAndStreamOutput("docker", "pull", "ghcr.io/techknowlogick/xgo:latest")
return runAndStreamOutput(ctx, "docker", "pull", "ghcr.io/techknowlogick/xgo:latest")
}
func runXgo(targets string) error {
func runXgo(ctx context.Context, targets string) error {
mg.Deps(initVars)
if err := checkAndInstallGoTool("xgo", "src.techknowlogick.com/xgo"); err != nil {
if err := checkAndInstallGoTool(ctx, "xgo", "src.techknowlogick.com/xgo"); err != nil {
return err
}
@@ -961,7 +971,7 @@ func runXgo(targets string) error {
outName = Executable + "-" + Version
}
if err := runAndStreamOutput("xgo",
if err := runAndStreamOutput(ctx, "xgo",
"-dest", "./"+DIST+"/binaries",
"-tags", "netgo "+Tags,
"-ldflags", extraLdflags+Ldflags,
@@ -987,12 +997,12 @@ func runXgo(targets string) error {
}
// Windows builds binaries for windows
func (Release) Windows() error {
return runXgo("windows/*")
func (Release) Windows(ctx context.Context) error {
return runXgo(ctx, "windows/*")
}
// Linux builds binaries for linux
func (Release) Linux() error {
func (Release) Linux(ctx context.Context) error {
targets := []string{
"linux/amd64",
"linux/arm-5",
@@ -1005,15 +1015,15 @@ func (Release) Linux() error {
"linux/mips64le",
"linux/riscv64",
}
return runXgo(strings.Join(targets, ","))
return runXgo(ctx, strings.Join(targets, ","))
}
// Darwin builds binaries for darwin
func (Release) Darwin() error {
return runXgo("darwin-10.15/*")
func (Release) Darwin(ctx context.Context) error {
return runXgo(ctx, "darwin-10.15/*")
}
func (Release) Xgo(target string) error {
func (Release) Xgo(ctx context.Context, target string) error {
parts := strings.Split(target, "/")
if len(parts) < 2 {
return fmt.Errorf("invalid target")
@@ -1024,7 +1034,7 @@ func (Release) Xgo(target string) error {
variant = "-" + strings.ReplaceAll(parts[2], "v", "")
}
return runXgo(parts[0] + "/" + parts[1] + variant)
return runXgo(ctx, parts[0]+"/"+parts[1]+variant)
}
// Compress compresses the built binaries in dist/binaries/ to reduce their filesize
@@ -1052,10 +1062,10 @@ func (Release) Compress(ctx context.Context) error {
// Runs compressing in parallel since upx is single-threaded
errs.Go(func() error {
if err := runAndStreamOutput("chmod", "+x", path); err != nil { // Make sure all binaries are executable. Sometimes the CI does weird things and they're not.
if err := runAndStreamOutput(ctx, "chmod", "+x", path); err != nil { // Make sure all binaries are executable. Sometimes the CI does weird things and they're not.
return err
}
return runAndStreamOutput("upx", "-9", path)
return runAndStreamOutput(ctx, "upx", "-9", path)
})
return nil
@@ -1155,7 +1165,7 @@ func (Release) OsPackage() error {
}
// Zip creates a zip file from all os-package folders in dist/release
func (Release) Zip() error {
func (Release) Zip(ctx context.Context) error {
rootDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("could not get working directory: %w", err)
@@ -1173,7 +1183,7 @@ func (Release) Zip() error {
fmt.Printf("Zipping %s...\n", info.Name())
zipFile := filepath.Join(rootDir, DIST, "zip", info.Name()+".zip")
c := exec.Command("zip", "-r", zipFile, ".", "-i", "*") //nolint:gosec // This mage task creates zips of every directory recursively, it must use the directory name in the resulting file path to distinguish output files.
c := exec.CommandContext(ctx, "zip", "-r", zipFile, ".", "-i", "*") //nolint:gosec // This mage task creates zips of every directory recursively, it must use the directory name in the resulting file path to distinguish output files.
c.Dir = path
out, err := c.Output()
fmt.Print(string(out))
@@ -1186,9 +1196,9 @@ func (Release) Zip() error {
}
// Reprepro creates a debian repo structure
func (Release) Reprepro() error {
func (Release) Reprepro(ctx context.Context) error {
mg.Deps(setVersion, setBinLocation)
return runAndStreamOutput("reprepro_expect", "debian", "includedeb", "buster", "./"+DIST+"/os-packages/"+Executable+"_"+strings.ReplaceAll(VersionNumber, "v0", "0")+"_amd64.deb")
return runAndStreamOutput(ctx, "reprepro_expect", "debian", "includedeb", "buster", "./"+DIST+"/os-packages/"+Executable+"_"+strings.ReplaceAll(VersionNumber, "v0", "0")+"_amd64.deb")
}
// PrepareNFPMConfig prepares the nfpm config
@@ -1215,7 +1225,7 @@ func (Release) PrepareNFPMConfig() error {
}
// Packages creates deb, rpm and apk packages
func (Release) Packages() error {
func (Release) Packages(ctx context.Context) error {
mg.Deps(initVars)
var err error
@@ -1223,10 +1233,10 @@ func (Release) Packages() error {
if binpath == "" {
binpath = "nfpm"
}
err = exec.Command(binpath).Run()
err = exec.CommandContext(ctx, binpath).Run()
if err != nil && strings.Contains(err.Error(), "executable file not found") {
binpath = "/usr/bin/nfpm"
err = exec.Command(binpath).Run()
err = exec.CommandContext(ctx, binpath).Run()
}
if err != nil && strings.Contains(err.Error(), "executable file not found") {
return fmt.Errorf("executable %s not found: please manually install nfpm by running the command: curl -sfL https://install.goreleaser.com/github.com/goreleaser/nfpm.sh | sh -s -- -b $(go env GOPATH)/bin", binpath)
@@ -1242,13 +1252,13 @@ func (Release) Packages() error {
return err
}
if err := runAndStreamOutput(binpath, "pkg", "--packager", "deb", "--target", releasePath); err != nil {
if err := runAndStreamOutput(ctx, binpath, "pkg", "--packager", "deb", "--target", releasePath); err != nil {
return err
}
if err := runAndStreamOutput(binpath, "pkg", "--packager", "rpm", "--target", releasePath); err != nil {
if err := runAndStreamOutput(ctx, binpath, "pkg", "--packager", "rpm", "--target", releasePath); err != nil {
return err
}
if err := runAndStreamOutput(binpath, "pkg", "--packager", "apk", "--target", releasePath); err != nil {
if err := runAndStreamOutput(ctx, binpath, "pkg", "--packager", "apk", "--target", releasePath); err != nil {
return err
}
@@ -1493,13 +1503,13 @@ type Generate mg.Namespace
const DefaultConfigYAMLSamplePath = "config.yml.sample"
// SwaggerDocs generates the swagger docs from the code annotations
func (Generate) SwaggerDocs() error {
func (Generate) SwaggerDocs(ctx context.Context) error {
mg.Deps(initVars)
if err := checkAndInstallGoTool("swag", "github.com/swaggo/swag/cmd/swag"); err != nil {
if err := checkAndInstallGoTool(ctx, "swag", "github.com/swaggo/swag/cmd/swag"); err != nil {
return err
}
return runAndStreamOutput("swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", ".", "-o", "./pkg/swagger")
return runAndStreamOutput(ctx, "swag", "init", "-g", "./pkg/routes/routes.go", "--parseDependency", "-d", ".", "-o", "./pkg/swagger")
}
type ConfigNode struct {
@@ -1636,7 +1646,7 @@ func (Generate) ConfigYAML(commented bool) {
// The second argument is a path to a plan file that will be copied to the new worktree (pass "" to skip).
// The worktree is created in the parent directory (../).
// It also copies the current config.yml with an updated rootpath, and initializes the frontend.
func (Dev) PrepareWorktree(name string, planPath string) error {
func (Dev) PrepareWorktree(ctx context.Context, name string, planPath string) error {
if name == "" {
return fmt.Errorf("name is required: mage dev:prepare-worktree <name> <plan-path>")
}
@@ -1647,7 +1657,7 @@ func (Dev) PrepareWorktree(name string, planPath string) error {
fmt.Printf("Creating worktree at %s with branch %s...\n", worktreePath, name)
// Create the git worktree
cmd := exec.Command("git", "worktree", "add", worktreePath, "-b", name)
cmd := exec.CommandContext(ctx, "git", "worktree", "add", worktreePath, "-b", name)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
@@ -1728,7 +1738,7 @@ func (Dev) PrepareWorktree(name string, planPath string) error {
frontendDir := filepath.Join(worktreePath, "frontend")
// Run pnpm install
pnpmCmd := exec.Command("pnpm", "i")
pnpmCmd := exec.CommandContext(ctx, "pnpm", "i")
pnpmCmd.Dir = frontendDir
pnpmCmd.Stdout = os.Stdout
pnpmCmd.Stderr = os.Stderr
@@ -1737,7 +1747,7 @@ func (Dev) PrepareWorktree(name string, planPath string) error {
}
// Run patch-sass-embedded (shell alias from devenv)
patchCmd := exec.Command("bash", "-ic", "patch-sass-embedded")
patchCmd := exec.CommandContext(ctx, "bash", "-ic", "patch-sass-embedded")
patchCmd.Dir = frontendDir
patchCmd.Stdout = os.Stdout
patchCmd.Stderr = os.Stderr
@@ -1756,8 +1766,8 @@ func (Dev) PrepareWorktree(name string, planPath string) error {
}
// printReleaseStats prints commit statistics for the range between two refs.
func printReleaseStats(fromRef, toRef string) error {
output, err := runGitCommandWithOutput("log", fromRef+".."+toRef, "--oneline")
func printReleaseStats(ctx context.Context, fromRef, toRef string) error {
output, err := runGitCommandWithOutput(ctx, "log", fromRef+".."+toRef, "--oneline")
if err != nil {
return fmt.Errorf("failed to get commit log: %w", err)
}
@@ -1811,7 +1821,7 @@ func printReleaseStats(fromRef, toRef string) error {
// TagRelease creates a new release tag with changelog.
// It updates the version badge in README.md, generates changelog using git-cliff,
// commits the changes, and creates an annotated tag.
func (Dev) TagRelease(version string) error {
func (Dev) TagRelease(ctx context.Context, version string) error {
if version == "" {
return fmt.Errorf("version is required: mage dev:tag-release <version>")
}
@@ -1824,7 +1834,7 @@ func (Dev) TagRelease(version string) error {
fmt.Printf("Creating release %s...\n", version)
// Get the last tag
lastTagBytes, err := runGitCommandWithOutput("describe", "--tags", "--abbrev=0")
lastTagBytes, err := runGitCommandWithOutput(ctx, "describe", "--tags", "--abbrev=0")
if err != nil {
return fmt.Errorf("failed to get last tag: %w", err)
}
@@ -1832,13 +1842,13 @@ func (Dev) TagRelease(version string) error {
fmt.Printf("Last tag: %s\n", lastTag)
// Print commit statistics
if err := printReleaseStats(lastTag, "HEAD"); err != nil {
if err := printReleaseStats(ctx, lastTag, "HEAD"); err != nil {
fmt.Printf("Warning: could not print release stats: %v\n", err)
}
// Generate changelog using git cliff
fmt.Println("Generating changelog...")
changelogBytes, err := runGitCommandWithOutput("cliff", lastTag+"..HEAD", "--tag", version)
changelogBytes, err := runGitCommandWithOutput(ctx, "cliff", lastTag+"..HEAD", "--tag", version)
if err != nil {
return fmt.Errorf("failed to generate changelog: %w", err)
}
@@ -1868,12 +1878,12 @@ func (Dev) TagRelease(version string) error {
// Commit the changes
fmt.Println("Committing changes...")
commitMsg := fmt.Sprintf("chore: %s release preparations", version)
cmd := exec.Command("git", "add", "README.md", "CHANGELOG.md", "frontend/package.json")
cmd := exec.CommandContext(ctx, "git", "add", "README.md", "CHANGELOG.md", "frontend/package.json")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to stage files: %w", err)
}
cmd = exec.Command("git", "commit", "-m", commitMsg)
cmd = exec.CommandContext(ctx, "git", "commit", "-m", commitMsg)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
@@ -1885,7 +1895,7 @@ func (Dev) TagRelease(version string) error {
// Create the annotated tag
fmt.Printf("Creating tag %s...\n", version)
cmd = exec.Command("git", "tag", "-a", version, "-m", tagMessage)
cmd = exec.CommandContext(ctx, "git", "tag", "-a", version, "-m", tagMessage)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
@@ -2070,7 +2080,7 @@ func prepareTagMessage(changelog string) string {
type Plugins mg.Namespace
// Build compiles a Go plugin at the provided path.
func (Plugins) Build(pathToSourceFiles string) error {
func (Plugins) Build(ctx context.Context, pathToSourceFiles string) error {
mg.Deps(initVars)
if pathToSourceFiles == "" {
return fmt.Errorf("please provide a plugin path")
@@ -2086,5 +2096,5 @@ func (Plugins) Build(pathToSourceFiles string) error {
}
out := filepath.Join("plugins", filepath.Base(pathToSourceFiles)+".so")
return runAndStreamOutput("go", "build", "-buildmode=plugin", "-tags", Tags, "-o", out, pathToSourceFiles)
return runAndStreamOutput(ctx, "go", "build", "-buildmode=plugin", "-tags", Tags, "-o", out, pathToSourceFiles)
}